VirtualBox

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

Last change on this file since 47470 was 47401, checked in by vboxsync, 11 years ago

Main,Frontends: Second step of USB controller rework. There is one controller instance for every USB controller now. Adapt frontends and testsuite to work with the changed API

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 505.1 KB
Line 
1/* $Id: MachineImpl.cpp 47401 2013-07-25 19:12:24Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.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 "USBDeviceFiltersImpl.h"
44#include "HostImpl.h"
45#include "SharedFolderImpl.h"
46#include "GuestOSTypeImpl.h"
47#include "VirtualBoxErrorInfoImpl.h"
48#include "GuestImpl.h"
49#include "StorageControllerImpl.h"
50#include "DisplayImpl.h"
51#include "DisplayUtils.h"
52#include "MachineImplCloneVM.h"
53#include "AutostartDb.h"
54#include "SystemPropertiesImpl.h"
55
56// generated header
57#include "VBoxEvents.h"
58
59#ifdef VBOX_WITH_USB
60# include "USBProxyService.h"
61#endif
62
63#include "AutoCaller.h"
64#include "HashedPw.h"
65#include "Performance.h"
66
67#include <iprt/asm.h>
68#include <iprt/path.h>
69#include <iprt/dir.h>
70#include <iprt/env.h>
71#include <iprt/lockvalidator.h>
72#include <iprt/process.h>
73#include <iprt/cpp/utils.h>
74#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
75#include <iprt/sha.h>
76#include <iprt/string.h>
77#include <iprt/base64.h>
78
79#include <VBox/com/array.h>
80#include <VBox/com/list.h>
81
82#include <VBox/err.h>
83#include <VBox/param.h>
84#include <VBox/settings.h>
85#include <VBox/vmm/ssm.h>
86
87#ifdef VBOX_WITH_GUEST_PROPS
88# include <VBox/HostServices/GuestPropertySvc.h>
89# include <VBox/com/array.h>
90#endif
91
92#include "VBox/com/MultiResult.h"
93
94#include <algorithm>
95
96#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
97# define HOSTSUFF_EXE ".exe"
98#else /* !RT_OS_WINDOWS */
99# define HOSTSUFF_EXE ""
100#endif /* !RT_OS_WINDOWS */
101
102// defines / prototypes
103/////////////////////////////////////////////////////////////////////////////
104
105/////////////////////////////////////////////////////////////////////////////
106// Machine::Data structure
107/////////////////////////////////////////////////////////////////////////////
108
109Machine::Data::Data()
110{
111 mRegistered = FALSE;
112 pMachineConfigFile = NULL;
113 /* Contains hints on what has changed when the user is using the VM (config
114 * changes, running the VM, ...). This is used to decide if a config needs
115 * to be written to disk. */
116 flModifications = 0;
117 /* VM modification usually also trigger setting the current state to
118 * "Modified". Although this is not always the case. An e.g. is the VM
119 * initialization phase or when snapshot related data is changed. The
120 * actually behavior is controlled by the following flag. */
121 m_fAllowStateModification = false;
122 mAccessible = FALSE;
123 /* mUuid is initialized in Machine::init() */
124
125 mMachineState = MachineState_PoweredOff;
126 RTTimeNow(&mLastStateChange);
127
128 mMachineStateDeps = 0;
129 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
130 mMachineStateChangePending = 0;
131
132 mCurrentStateModified = TRUE;
133 mGuestPropertiesModified = FALSE;
134
135 mSession.mPID = NIL_RTPROCESS;
136 mSession.mState = SessionState_Unlocked;
137}
138
139Machine::Data::~Data()
140{
141 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
142 {
143 RTSemEventMultiDestroy(mMachineStateDepsSem);
144 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
145 }
146 if (pMachineConfigFile)
147 {
148 delete pMachineConfigFile;
149 pMachineConfigFile = NULL;
150 }
151}
152
153/////////////////////////////////////////////////////////////////////////////
154// Machine::HWData structure
155/////////////////////////////////////////////////////////////////////////////
156
157Machine::HWData::HWData()
158{
159 /* default values for a newly created machine */
160 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
161 mMemorySize = 128;
162 mCPUCount = 1;
163 mCPUHotPlugEnabled = false;
164 mMemoryBalloonSize = 0;
165 mPageFusionEnabled = false;
166 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
167 mVRAMSize = 8;
168 mAccelerate3DEnabled = false;
169 mAccelerate2DVideoEnabled = false;
170 mMonitorCount = 1;
171 mVideoCaptureWidth = 1024;
172 mVideoCaptureHeight = 768;
173 mVideoCaptureRate = 512;
174 mVideoCaptureFPS = 25;
175 mVideoCaptureEnabled = false;
176 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); i++)
177 maVideoCaptureScreens[i] = true;
178
179 mHWVirtExEnabled = true;
180 mHWVirtExNestedPagingEnabled = true;
181#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
182 mHWVirtExLargePagesEnabled = true;
183#else
184 /* Not supported on 32 bits hosts. */
185 mHWVirtExLargePagesEnabled = false;
186#endif
187 mHWVirtExVPIDEnabled = true;
188 mHWVirtExUXEnabled = true;
189 mHWVirtExForceEnabled = false;
190#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
191 mHWVirtExExclusive = false;
192#else
193 mHWVirtExExclusive = true;
194#endif
195#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
196 mPAEEnabled = true;
197#else
198 mPAEEnabled = false;
199#endif
200 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
201 mSyntheticCpu = false;
202 mHPETEnabled = false;
203
204 /* default boot order: floppy - DVD - HDD */
205 mBootOrder[0] = DeviceType_Floppy;
206 mBootOrder[1] = DeviceType_DVD;
207 mBootOrder[2] = DeviceType_HardDisk;
208 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
209 mBootOrder[i] = DeviceType_Null;
210
211 mClipboardMode = ClipboardMode_Disabled;
212 mDragAndDropMode = DragAndDropMode_Disabled;
213 mGuestPropertyNotificationPatterns = "";
214
215 mFirmwareType = FirmwareType_BIOS;
216 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
217 mPointingHIDType = PointingHIDType_PS2Mouse;
218 mChipsetType = ChipsetType_PIIX3;
219 mEmulatedUSBWebcamEnabled = FALSE;
220 mEmulatedUSBCardReaderEnabled = FALSE;
221
222 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
223 mCPUAttached[i] = false;
224
225 mIOCacheEnabled = true;
226 mIOCacheSize = 5; /* 5MB */
227
228 /* Maximum CPU execution cap by default. */
229 mCpuExecutionCap = 100;
230}
231
232Machine::HWData::~HWData()
233{
234}
235
236/////////////////////////////////////////////////////////////////////////////
237// Machine::HDData structure
238/////////////////////////////////////////////////////////////////////////////
239
240Machine::MediaData::MediaData()
241{
242}
243
244Machine::MediaData::~MediaData()
245{
246}
247
248/////////////////////////////////////////////////////////////////////////////
249// Machine class
250/////////////////////////////////////////////////////////////////////////////
251
252// constructor / destructor
253/////////////////////////////////////////////////////////////////////////////
254
255Machine::Machine() :
256#ifdef VBOX_WITH_RESOURCE_USAGE_API
257 mCollectorGuest(NULL),
258#endif
259 mPeer(NULL),
260 mParent(NULL),
261 mSerialPorts(),
262 mParallelPorts(),
263 uRegistryNeedsSaving(0)
264{}
265
266Machine::~Machine()
267{}
268
269HRESULT Machine::FinalConstruct()
270{
271 LogFlowThisFunc(("\n"));
272 return BaseFinalConstruct();
273}
274
275void Machine::FinalRelease()
276{
277 LogFlowThisFunc(("\n"));
278 uninit();
279 BaseFinalRelease();
280}
281
282/**
283 * Initializes a new machine instance; this init() variant creates a new, empty machine.
284 * This gets called from VirtualBox::CreateMachine().
285 *
286 * @param aParent Associated parent object
287 * @param strConfigFile Local file system path to the VM settings file (can
288 * be relative to the VirtualBox config directory).
289 * @param strName name for the machine
290 * @param llGroups list of groups for the machine
291 * @param aOsType OS Type of this machine or NULL.
292 * @param aId UUID for the new machine.
293 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
294 *
295 * @return Success indicator. if not S_OK, the machine object is invalid
296 */
297HRESULT Machine::init(VirtualBox *aParent,
298 const Utf8Str &strConfigFile,
299 const Utf8Str &strName,
300 const StringsList &llGroups,
301 GuestOSType *aOsType,
302 const Guid &aId,
303 bool fForceOverwrite,
304 bool fDirectoryIncludesUUID)
305{
306 LogFlowThisFuncEnter();
307 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
308
309 /* Enclose the state transition NotReady->InInit->Ready */
310 AutoInitSpan autoInitSpan(this);
311 AssertReturn(autoInitSpan.isOk(), E_FAIL);
312
313 HRESULT rc = initImpl(aParent, strConfigFile);
314 if (FAILED(rc)) return rc;
315
316 rc = tryCreateMachineConfigFile(fForceOverwrite);
317 if (FAILED(rc)) return rc;
318
319 if (SUCCEEDED(rc))
320 {
321 // create an empty machine config
322 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
323
324 rc = initDataAndChildObjects();
325 }
326
327 if (SUCCEEDED(rc))
328 {
329 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
330 mData->mAccessible = TRUE;
331
332 unconst(mData->mUuid) = aId;
333
334 mUserData->s.strName = strName;
335
336 mUserData->s.llGroups = llGroups;
337
338 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
339 // the "name sync" flag determines whether the machine directory gets renamed along
340 // with the machine file; say so if the settings file name is the same as the
341 // settings file parent directory (machine directory)
342 mUserData->s.fNameSync = isInOwnDir();
343
344 // initialize the default snapshots folder
345 rc = COMSETTER(SnapshotFolder)(NULL);
346 AssertComRC(rc);
347
348 if (aOsType)
349 {
350 /* Store OS type */
351 mUserData->s.strOsType = aOsType->id();
352
353 /* Apply BIOS defaults */
354 mBIOSSettings->applyDefaults(aOsType);
355
356 /* Apply network adapters defaults */
357 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
358 mNetworkAdapters[slot]->applyDefaults(aOsType);
359
360 /* Apply serial port defaults */
361 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
362 mSerialPorts[slot]->applyDefaults(aOsType);
363
364 /* Let the OS type select 64-bit ness. */
365 mHWData->mLongMode = aOsType->is64Bit()
366 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
367 }
368
369 /* At this point the changing of the current state modification
370 * flag is allowed. */
371 allowStateModification();
372
373 /* commit all changes made during the initialization */
374 commit();
375 }
376
377 /* Confirm a successful initialization when it's the case */
378 if (SUCCEEDED(rc))
379 {
380 if (mData->mAccessible)
381 autoInitSpan.setSucceeded();
382 else
383 autoInitSpan.setLimited();
384 }
385
386 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
387 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
388 mData->mRegistered,
389 mData->mAccessible,
390 rc));
391
392 LogFlowThisFuncLeave();
393
394 return rc;
395}
396
397/**
398 * Initializes a new instance with data from machine XML (formerly Init_Registered).
399 * Gets called in two modes:
400 *
401 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
402 * UUID is specified and we mark the machine as "registered";
403 *
404 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
405 * and the machine remains unregistered until RegisterMachine() is called.
406 *
407 * @param aParent Associated parent object
408 * @param aConfigFile Local file system path to the VM settings file (can
409 * be relative to the VirtualBox config directory).
410 * @param aId UUID of the machine or NULL (see above).
411 *
412 * @return Success indicator. if not S_OK, the machine object is invalid
413 */
414HRESULT Machine::initFromSettings(VirtualBox *aParent,
415 const Utf8Str &strConfigFile,
416 const Guid *aId)
417{
418 LogFlowThisFuncEnter();
419 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
420
421 /* Enclose the state transition NotReady->InInit->Ready */
422 AutoInitSpan autoInitSpan(this);
423 AssertReturn(autoInitSpan.isOk(), E_FAIL);
424
425 HRESULT rc = initImpl(aParent, strConfigFile);
426 if (FAILED(rc)) return rc;
427
428 if (aId)
429 {
430 // loading a registered VM:
431 unconst(mData->mUuid) = *aId;
432 mData->mRegistered = TRUE;
433 // now load the settings from XML:
434 rc = registeredInit();
435 // this calls initDataAndChildObjects() and loadSettings()
436 }
437 else
438 {
439 // opening an unregistered VM (VirtualBox::OpenMachine()):
440 rc = initDataAndChildObjects();
441
442 if (SUCCEEDED(rc))
443 {
444 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
445 mData->mAccessible = TRUE;
446
447 try
448 {
449 // load and parse machine XML; this will throw on XML or logic errors
450 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
451
452 // reject VM UUID duplicates, they can happen if someone
453 // tries to register an already known VM config again
454 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
455 true /* fPermitInaccessible */,
456 false /* aDoSetError */,
457 NULL) != VBOX_E_OBJECT_NOT_FOUND)
458 {
459 throw setError(E_FAIL,
460 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
461 mData->m_strConfigFile.c_str());
462 }
463
464 // use UUID from machine config
465 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
466
467 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
468 NULL /* puuidRegistry */);
469 if (FAILED(rc)) throw rc;
470
471 /* At this point the changing of the current state modification
472 * flag is allowed. */
473 allowStateModification();
474
475 commit();
476 }
477 catch (HRESULT err)
478 {
479 /* we assume that error info is set by the thrower */
480 rc = err;
481 }
482 catch (...)
483 {
484 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
485 }
486 }
487 }
488
489 /* Confirm a successful initialization when it's the case */
490 if (SUCCEEDED(rc))
491 {
492 if (mData->mAccessible)
493 autoInitSpan.setSucceeded();
494 else
495 {
496 autoInitSpan.setLimited();
497
498 // uninit media from this machine's media registry, or else
499 // reloading the settings will fail
500 mParent->unregisterMachineMedia(getId());
501 }
502 }
503
504 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
505 "rc=%08X\n",
506 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
507 mData->mRegistered, mData->mAccessible, rc));
508
509 LogFlowThisFuncLeave();
510
511 return rc;
512}
513
514/**
515 * Initializes a new instance from a machine config that is already in memory
516 * (import OVF case). Since we are importing, the UUID in the machine
517 * config is ignored and we always generate a fresh one.
518 *
519 * @param strName Name for the new machine; this overrides what is specified in config and is used
520 * for the settings file as well.
521 * @param config Machine configuration loaded and parsed from XML.
522 *
523 * @return Success indicator. if not S_OK, the machine object is invalid
524 */
525HRESULT Machine::init(VirtualBox *aParent,
526 const Utf8Str &strName,
527 const settings::MachineConfigFile &config)
528{
529 LogFlowThisFuncEnter();
530
531 /* Enclose the state transition NotReady->InInit->Ready */
532 AutoInitSpan autoInitSpan(this);
533 AssertReturn(autoInitSpan.isOk(), E_FAIL);
534
535 Utf8Str strConfigFile;
536 aParent->getDefaultMachineFolder(strConfigFile);
537 strConfigFile.append(RTPATH_DELIMITER);
538 strConfigFile.append(strName);
539 strConfigFile.append(RTPATH_DELIMITER);
540 strConfigFile.append(strName);
541 strConfigFile.append(".vbox");
542
543 HRESULT rc = initImpl(aParent, strConfigFile);
544 if (FAILED(rc)) return rc;
545
546 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
547 if (FAILED(rc)) return rc;
548
549 rc = initDataAndChildObjects();
550
551 if (SUCCEEDED(rc))
552 {
553 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
554 mData->mAccessible = TRUE;
555
556 // create empty machine config for instance data
557 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
558
559 // generate fresh UUID, ignore machine config
560 unconst(mData->mUuid).create();
561
562 rc = loadMachineDataFromSettings(config,
563 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
564
565 // override VM name as well, it may be different
566 mUserData->s.strName = strName;
567
568 if (SUCCEEDED(rc))
569 {
570 /* At this point the changing of the current state modification
571 * flag is allowed. */
572 allowStateModification();
573
574 /* commit all changes made during the initialization */
575 commit();
576 }
577 }
578
579 /* Confirm a successful initialization when it's the case */
580 if (SUCCEEDED(rc))
581 {
582 if (mData->mAccessible)
583 autoInitSpan.setSucceeded();
584 else
585 {
586 autoInitSpan.setLimited();
587
588 // uninit media from this machine's media registry, or else
589 // reloading the settings will fail
590 mParent->unregisterMachineMedia(getId());
591 }
592 }
593
594 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
595 "rc=%08X\n",
596 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
597 mData->mRegistered, mData->mAccessible, rc));
598
599 LogFlowThisFuncLeave();
600
601 return rc;
602}
603
604/**
605 * Shared code between the various init() implementations.
606 * @param aParent
607 * @return
608 */
609HRESULT Machine::initImpl(VirtualBox *aParent,
610 const Utf8Str &strConfigFile)
611{
612 LogFlowThisFuncEnter();
613
614 AssertReturn(aParent, E_INVALIDARG);
615 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
616
617 HRESULT rc = S_OK;
618
619 /* share the parent weakly */
620 unconst(mParent) = aParent;
621
622 /* allocate the essential machine data structure (the rest will be
623 * allocated later by initDataAndChildObjects() */
624 mData.allocate();
625
626 /* memorize the config file name (as provided) */
627 mData->m_strConfigFile = strConfigFile;
628
629 /* get the full file name */
630 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
631 if (RT_FAILURE(vrc1))
632 return setError(VBOX_E_FILE_ERROR,
633 tr("Invalid machine settings file name '%s' (%Rrc)"),
634 strConfigFile.c_str(),
635 vrc1);
636
637 LogFlowThisFuncLeave();
638
639 return rc;
640}
641
642/**
643 * Tries to create a machine settings file in the path stored in the machine
644 * instance data. Used when a new machine is created to fail gracefully if
645 * the settings file could not be written (e.g. because machine dir is read-only).
646 * @return
647 */
648HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
649{
650 HRESULT rc = S_OK;
651
652 // when we create a new machine, we must be able to create the settings file
653 RTFILE f = NIL_RTFILE;
654 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
655 if ( RT_SUCCESS(vrc)
656 || vrc == VERR_SHARING_VIOLATION
657 )
658 {
659 if (RT_SUCCESS(vrc))
660 RTFileClose(f);
661 if (!fForceOverwrite)
662 rc = setError(VBOX_E_FILE_ERROR,
663 tr("Machine settings file '%s' already exists"),
664 mData->m_strConfigFileFull.c_str());
665 else
666 {
667 /* try to delete the config file, as otherwise the creation
668 * of a new settings file will fail. */
669 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
670 if (RT_FAILURE(vrc2))
671 rc = setError(VBOX_E_FILE_ERROR,
672 tr("Could not delete the existing settings file '%s' (%Rrc)"),
673 mData->m_strConfigFileFull.c_str(), vrc2);
674 }
675 }
676 else if ( vrc != VERR_FILE_NOT_FOUND
677 && vrc != VERR_PATH_NOT_FOUND
678 )
679 rc = setError(VBOX_E_FILE_ERROR,
680 tr("Invalid machine settings file name '%s' (%Rrc)"),
681 mData->m_strConfigFileFull.c_str(),
682 vrc);
683 return rc;
684}
685
686/**
687 * Initializes the registered machine by loading the settings file.
688 * This method is separated from #init() in order to make it possible to
689 * retry the operation after VirtualBox startup instead of refusing to
690 * startup the whole VirtualBox server in case if the settings file of some
691 * registered VM is invalid or inaccessible.
692 *
693 * @note Must be always called from this object's write lock
694 * (unless called from #init() that doesn't need any locking).
695 * @note Locks the mUSBController method for writing.
696 * @note Subclasses must not call this method.
697 */
698HRESULT Machine::registeredInit()
699{
700 AssertReturn(!isSessionMachine(), E_FAIL);
701 AssertReturn(!isSnapshotMachine(), E_FAIL);
702 AssertReturn(mData->mUuid.isValid(), E_FAIL);
703 AssertReturn(!mData->mAccessible, E_FAIL);
704
705 HRESULT rc = initDataAndChildObjects();
706
707 if (SUCCEEDED(rc))
708 {
709 /* Temporarily reset the registered flag in order to let setters
710 * potentially called from loadSettings() succeed (isMutable() used in
711 * all setters will return FALSE for a Machine instance if mRegistered
712 * is TRUE). */
713 mData->mRegistered = FALSE;
714
715 try
716 {
717 // load and parse machine XML; this will throw on XML or logic errors
718 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
719
720 if (mData->mUuid != mData->pMachineConfigFile->uuid)
721 throw setError(E_FAIL,
722 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
723 mData->pMachineConfigFile->uuid.raw(),
724 mData->m_strConfigFileFull.c_str(),
725 mData->mUuid.toString().c_str(),
726 mParent->settingsFilePath().c_str());
727
728 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
729 NULL /* const Guid *puuidRegistry */);
730 if (FAILED(rc)) throw rc;
731 }
732 catch (HRESULT err)
733 {
734 /* we assume that error info is set by the thrower */
735 rc = err;
736 }
737 catch (...)
738 {
739 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
740 }
741
742 /* Restore the registered flag (even on failure) */
743 mData->mRegistered = TRUE;
744 }
745
746 if (SUCCEEDED(rc))
747 {
748 /* Set mAccessible to TRUE only if we successfully locked and loaded
749 * the settings file */
750 mData->mAccessible = TRUE;
751
752 /* commit all changes made during loading the settings file */
753 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
754 /// @todo r=klaus for some reason the settings loading logic backs up
755 // the settings, and therefore a commit is needed. Should probably be changed.
756 }
757 else
758 {
759 /* If the machine is registered, then, instead of returning a
760 * failure, we mark it as inaccessible and set the result to
761 * success to give it a try later */
762
763 /* fetch the current error info */
764 mData->mAccessError = com::ErrorInfo();
765 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
766 mData->mUuid.raw(),
767 mData->mAccessError.getText().raw()));
768
769 /* rollback all changes */
770 rollback(false /* aNotify */);
771
772 // uninit media from this machine's media registry, or else
773 // reloading the settings will fail
774 mParent->unregisterMachineMedia(getId());
775
776 /* uninitialize the common part to make sure all data is reset to
777 * default (null) values */
778 uninitDataAndChildObjects();
779
780 rc = S_OK;
781 }
782
783 return rc;
784}
785
786/**
787 * Uninitializes the instance.
788 * Called either from FinalRelease() or by the parent when it gets destroyed.
789 *
790 * @note The caller of this method must make sure that this object
791 * a) doesn't have active callers on the current thread and b) is not locked
792 * by the current thread; otherwise uninit() will hang either a) due to
793 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
794 * a dead-lock caused by this thread waiting for all callers on the other
795 * threads are done but preventing them from doing so by holding a lock.
796 */
797void Machine::uninit()
798{
799 LogFlowThisFuncEnter();
800
801 Assert(!isWriteLockOnCurrentThread());
802
803 Assert(!uRegistryNeedsSaving);
804 if (uRegistryNeedsSaving)
805 {
806 AutoCaller autoCaller(this);
807 if (SUCCEEDED(autoCaller.rc()))
808 {
809 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
810 saveSettings(NULL, Machine::SaveS_Force);
811 }
812 }
813
814 /* Enclose the state transition Ready->InUninit->NotReady */
815 AutoUninitSpan autoUninitSpan(this);
816 if (autoUninitSpan.uninitDone())
817 return;
818
819 Assert(!isSnapshotMachine());
820 Assert(!isSessionMachine());
821 Assert(!!mData);
822
823 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
824 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
825
826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
827
828 if (!mData->mSession.mMachine.isNull())
829 {
830 /* Theoretically, this can only happen if the VirtualBox server has been
831 * terminated while there were clients running that owned open direct
832 * sessions. Since in this case we are definitely called by
833 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
834 * won't happen on the client watcher thread (because it does
835 * VirtualBox::addCaller() for the duration of the
836 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
837 * cannot happen until the VirtualBox caller is released). This is
838 * important, because SessionMachine::uninit() cannot correctly operate
839 * after we return from this method (it expects the Machine instance is
840 * still valid). We'll call it ourselves below.
841 */
842 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
843 (SessionMachine*)mData->mSession.mMachine));
844
845 if (Global::IsOnlineOrTransient(mData->mMachineState))
846 {
847 LogWarningThisFunc(("Setting state to Aborted!\n"));
848 /* set machine state using SessionMachine reimplementation */
849 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
850 }
851
852 /*
853 * Uninitialize SessionMachine using public uninit() to indicate
854 * an unexpected uninitialization.
855 */
856 mData->mSession.mMachine->uninit();
857 /* SessionMachine::uninit() must set mSession.mMachine to null */
858 Assert(mData->mSession.mMachine.isNull());
859 }
860
861 // uninit media from this machine's media registry, if they're still there
862 Guid uuidMachine(getId());
863
864 /* the lock is no more necessary (SessionMachine is uninitialized) */
865 alock.release();
866
867 /* XXX This will fail with
868 * "cannot be closed because it is still attached to 1 virtual machines"
869 * because at this point we did not call uninitDataAndChildObjects() yet
870 * and therefore also removeBackReference() for all these mediums was not called! */
871
872 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
873 mParent->unregisterMachineMedia(uuidMachine);
874
875 // has machine been modified?
876 if (mData->flModifications)
877 {
878 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
879 rollback(false /* aNotify */);
880 }
881
882 if (mData->mAccessible)
883 uninitDataAndChildObjects();
884
885 /* free the essential data structure last */
886 mData.free();
887
888 LogFlowThisFuncLeave();
889}
890
891// IMachine properties
892/////////////////////////////////////////////////////////////////////////////
893
894STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
895{
896 CheckComArgOutPointerValid(aParent);
897
898 AutoLimitedCaller autoCaller(this);
899 if (FAILED(autoCaller.rc())) return autoCaller.rc();
900
901 /* mParent is constant during life time, no need to lock */
902 ComObjPtr<VirtualBox> pVirtualBox(mParent);
903 pVirtualBox.queryInterfaceTo(aParent);
904
905 return S_OK;
906}
907
908STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
909{
910 CheckComArgOutPointerValid(aAccessible);
911
912 AutoLimitedCaller autoCaller(this);
913 if (FAILED(autoCaller.rc())) return autoCaller.rc();
914
915 LogFlowThisFunc(("ENTER\n"));
916
917 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
918
919 HRESULT rc = S_OK;
920
921 if (!mData->mAccessible)
922 {
923 /* try to initialize the VM once more if not accessible */
924
925 AutoReinitSpan autoReinitSpan(this);
926 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
927
928#ifdef DEBUG
929 LogFlowThisFunc(("Dumping media backreferences\n"));
930 mParent->dumpAllBackRefs();
931#endif
932
933 if (mData->pMachineConfigFile)
934 {
935 // reset the XML file to force loadSettings() (called from registeredInit())
936 // to parse it again; the file might have changed
937 delete mData->pMachineConfigFile;
938 mData->pMachineConfigFile = NULL;
939 }
940
941 rc = registeredInit();
942
943 if (SUCCEEDED(rc) && mData->mAccessible)
944 {
945 autoReinitSpan.setSucceeded();
946
947 /* make sure interesting parties will notice the accessibility
948 * state change */
949 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
950 mParent->onMachineDataChange(mData->mUuid);
951 }
952 }
953
954 if (SUCCEEDED(rc))
955 *aAccessible = mData->mAccessible;
956
957 LogFlowThisFuncLeave();
958
959 return rc;
960}
961
962STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
963{
964 CheckComArgOutPointerValid(aAccessError);
965
966 AutoLimitedCaller autoCaller(this);
967 if (FAILED(autoCaller.rc())) return autoCaller.rc();
968
969 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
970
971 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
972 {
973 /* return shortly */
974 aAccessError = NULL;
975 return S_OK;
976 }
977
978 HRESULT rc = S_OK;
979
980 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
981 rc = errorInfo.createObject();
982 if (SUCCEEDED(rc))
983 {
984 errorInfo->init(mData->mAccessError.getResultCode(),
985 mData->mAccessError.getInterfaceID().ref(),
986 Utf8Str(mData->mAccessError.getComponent()).c_str(),
987 Utf8Str(mData->mAccessError.getText()));
988 rc = errorInfo.queryInterfaceTo(aAccessError);
989 }
990
991 return rc;
992}
993
994STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
995{
996 CheckComArgOutPointerValid(aName);
997
998 AutoCaller autoCaller(this);
999 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1000
1001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1002
1003 mUserData->s.strName.cloneTo(aName);
1004
1005 return S_OK;
1006}
1007
1008STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
1009{
1010 CheckComArgStrNotEmptyOrNull(aName);
1011
1012 AutoCaller autoCaller(this);
1013 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1014
1015 // prohibit setting a UUID only as the machine name, or else it can
1016 // never be found by findMachine()
1017 Guid test(aName);
1018
1019 if (test.isValid())
1020 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1021
1022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1023
1024 HRESULT rc = checkStateDependency(MutableStateDep);
1025 if (FAILED(rc)) return rc;
1026
1027 setModified(IsModified_MachineData);
1028 mUserData.backup();
1029 mUserData->s.strName = aName;
1030
1031 return S_OK;
1032}
1033
1034STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1035{
1036 CheckComArgOutPointerValid(aDescription);
1037
1038 AutoCaller autoCaller(this);
1039 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1040
1041 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1042
1043 mUserData->s.strDescription.cloneTo(aDescription);
1044
1045 return S_OK;
1046}
1047
1048STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1049{
1050 AutoCaller autoCaller(this);
1051 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1052
1053 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1054
1055 // this can be done in principle in any state as it doesn't affect the VM
1056 // significantly, but play safe by not messing around while complex
1057 // activities are going on
1058 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1059 if (FAILED(rc)) return rc;
1060
1061 setModified(IsModified_MachineData);
1062 mUserData.backup();
1063 mUserData->s.strDescription = aDescription;
1064
1065 return S_OK;
1066}
1067
1068STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1069{
1070 CheckComArgOutPointerValid(aId);
1071
1072 AutoLimitedCaller autoCaller(this);
1073 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1074
1075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1076
1077 mData->mUuid.toUtf16().cloneTo(aId);
1078
1079 return S_OK;
1080}
1081
1082STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1083{
1084 CheckComArgOutSafeArrayPointerValid(aGroups);
1085
1086 AutoCaller autoCaller(this);
1087 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1088
1089 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1090 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1091 size_t i = 0;
1092 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1093 it != mUserData->s.llGroups.end();
1094 ++it, i++)
1095 {
1096 Bstr tmp = *it;
1097 tmp.cloneTo(&groups[i]);
1098 }
1099 groups.detachTo(ComSafeArrayOutArg(aGroups));
1100
1101 return S_OK;
1102}
1103
1104STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1105{
1106 AutoCaller autoCaller(this);
1107 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1108
1109 StringsList llGroups;
1110 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1111 if (FAILED(rc))
1112 return rc;
1113
1114 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1115
1116 // changing machine groups is possible while the VM is offline
1117 rc = checkStateDependency(OfflineStateDep);
1118 if (FAILED(rc)) return rc;
1119
1120 setModified(IsModified_MachineData);
1121 mUserData.backup();
1122 mUserData->s.llGroups = llGroups;
1123
1124 return S_OK;
1125}
1126
1127STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1128{
1129 CheckComArgOutPointerValid(aOSTypeId);
1130
1131 AutoCaller autoCaller(this);
1132 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1133
1134 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1135
1136 mUserData->s.strOsType.cloneTo(aOSTypeId);
1137
1138 return S_OK;
1139}
1140
1141STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1142{
1143 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1144
1145 AutoCaller autoCaller(this);
1146 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1147
1148 /* look up the object by Id to check it is valid */
1149 ComPtr<IGuestOSType> guestOSType;
1150 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1151 if (FAILED(rc)) return rc;
1152
1153 /* when setting, always use the "etalon" value for consistency -- lookup
1154 * by ID is case-insensitive and the input value may have different case */
1155 Bstr osTypeId;
1156 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1157 if (FAILED(rc)) return rc;
1158
1159 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1160
1161 rc = checkStateDependency(MutableStateDep);
1162 if (FAILED(rc)) return rc;
1163
1164 setModified(IsModified_MachineData);
1165 mUserData.backup();
1166 mUserData->s.strOsType = osTypeId;
1167
1168 return S_OK;
1169}
1170
1171
1172STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1173{
1174 CheckComArgOutPointerValid(aFirmwareType);
1175
1176 AutoCaller autoCaller(this);
1177 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1178
1179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1180
1181 *aFirmwareType = mHWData->mFirmwareType;
1182
1183 return S_OK;
1184}
1185
1186STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1187{
1188 AutoCaller autoCaller(this);
1189 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1190 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1191
1192 HRESULT rc = checkStateDependency(MutableStateDep);
1193 if (FAILED(rc)) return rc;
1194
1195 setModified(IsModified_MachineData);
1196 mHWData.backup();
1197 mHWData->mFirmwareType = aFirmwareType;
1198
1199 return S_OK;
1200}
1201
1202STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1203{
1204 CheckComArgOutPointerValid(aKeyboardHIDType);
1205
1206 AutoCaller autoCaller(this);
1207 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1208
1209 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1210
1211 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1212
1213 return S_OK;
1214}
1215
1216STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1217{
1218 AutoCaller autoCaller(this);
1219 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1220 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1221
1222 HRESULT rc = checkStateDependency(MutableStateDep);
1223 if (FAILED(rc)) return rc;
1224
1225 setModified(IsModified_MachineData);
1226 mHWData.backup();
1227 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1228
1229 return S_OK;
1230}
1231
1232STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1233{
1234 CheckComArgOutPointerValid(aPointingHIDType);
1235
1236 AutoCaller autoCaller(this);
1237 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1238
1239 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1240
1241 *aPointingHIDType = mHWData->mPointingHIDType;
1242
1243 return S_OK;
1244}
1245
1246STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1247{
1248 AutoCaller autoCaller(this);
1249 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1250 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1251
1252 HRESULT rc = checkStateDependency(MutableStateDep);
1253 if (FAILED(rc)) return rc;
1254
1255 setModified(IsModified_MachineData);
1256 mHWData.backup();
1257 mHWData->mPointingHIDType = aPointingHIDType;
1258
1259 return S_OK;
1260}
1261
1262STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1263{
1264 CheckComArgOutPointerValid(aChipsetType);
1265
1266 AutoCaller autoCaller(this);
1267 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1268
1269 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1270
1271 *aChipsetType = mHWData->mChipsetType;
1272
1273 return S_OK;
1274}
1275
1276STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1277{
1278 AutoCaller autoCaller(this);
1279 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1280 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1281
1282 HRESULT rc = checkStateDependency(MutableStateDep);
1283 if (FAILED(rc)) return rc;
1284
1285 if (aChipsetType != mHWData->mChipsetType)
1286 {
1287 setModified(IsModified_MachineData);
1288 mHWData.backup();
1289 mHWData->mChipsetType = aChipsetType;
1290
1291 // Resize network adapter array, to be finalized on commit/rollback.
1292 // We must not throw away entries yet, otherwise settings are lost
1293 // without a way to roll back.
1294 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1295 size_t oldCount = mNetworkAdapters.size();
1296 if (newCount > oldCount)
1297 {
1298 mNetworkAdapters.resize(newCount);
1299 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1300 {
1301 unconst(mNetworkAdapters[slot]).createObject();
1302 mNetworkAdapters[slot]->init(this, slot);
1303 }
1304 }
1305 }
1306
1307 return S_OK;
1308}
1309
1310STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1311{
1312 CheckComArgOutPointerValid(aHWVersion);
1313
1314 AutoCaller autoCaller(this);
1315 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1316
1317 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1318
1319 mHWData->mHWVersion.cloneTo(aHWVersion);
1320
1321 return S_OK;
1322}
1323
1324STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1325{
1326 /* check known version */
1327 Utf8Str hwVersion = aHWVersion;
1328 if ( hwVersion.compare("1") != 0
1329 && hwVersion.compare("2") != 0)
1330 return setError(E_INVALIDARG,
1331 tr("Invalid hardware version: %ls\n"), aHWVersion);
1332
1333 AutoCaller autoCaller(this);
1334 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1335
1336 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1337
1338 HRESULT rc = checkStateDependency(MutableStateDep);
1339 if (FAILED(rc)) return rc;
1340
1341 setModified(IsModified_MachineData);
1342 mHWData.backup();
1343 mHWData->mHWVersion = hwVersion;
1344
1345 return S_OK;
1346}
1347
1348STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1349{
1350 CheckComArgOutPointerValid(aUUID);
1351
1352 AutoCaller autoCaller(this);
1353 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1354
1355 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1356
1357 if (mHWData->mHardwareUUID.isValid())
1358 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1359 else
1360 mData->mUuid.toUtf16().cloneTo(aUUID);
1361
1362 return S_OK;
1363}
1364
1365STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1366{
1367 Guid hardwareUUID(aUUID);
1368 if (!hardwareUUID.isValid())
1369 return E_INVALIDARG;
1370
1371 AutoCaller autoCaller(this);
1372 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1373
1374 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1375
1376 HRESULT rc = checkStateDependency(MutableStateDep);
1377 if (FAILED(rc)) return rc;
1378
1379 setModified(IsModified_MachineData);
1380 mHWData.backup();
1381 if (hardwareUUID == mData->mUuid)
1382 mHWData->mHardwareUUID.clear();
1383 else
1384 mHWData->mHardwareUUID = hardwareUUID;
1385
1386 return S_OK;
1387}
1388
1389STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1390{
1391 CheckComArgOutPointerValid(memorySize);
1392
1393 AutoCaller autoCaller(this);
1394 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1395
1396 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1397
1398 *memorySize = mHWData->mMemorySize;
1399
1400 return S_OK;
1401}
1402
1403STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1404{
1405 /* check RAM limits */
1406 if ( memorySize < MM_RAM_MIN_IN_MB
1407 || memorySize > MM_RAM_MAX_IN_MB
1408 )
1409 return setError(E_INVALIDARG,
1410 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1411 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1412
1413 AutoCaller autoCaller(this);
1414 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1415
1416 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1417
1418 HRESULT rc = checkStateDependency(MutableStateDep);
1419 if (FAILED(rc)) return rc;
1420
1421 setModified(IsModified_MachineData);
1422 mHWData.backup();
1423 mHWData->mMemorySize = memorySize;
1424
1425 return S_OK;
1426}
1427
1428STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1429{
1430 CheckComArgOutPointerValid(CPUCount);
1431
1432 AutoCaller autoCaller(this);
1433 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1434
1435 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1436
1437 *CPUCount = mHWData->mCPUCount;
1438
1439 return S_OK;
1440}
1441
1442STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1443{
1444 /* check CPU limits */
1445 if ( CPUCount < SchemaDefs::MinCPUCount
1446 || CPUCount > SchemaDefs::MaxCPUCount
1447 )
1448 return setError(E_INVALIDARG,
1449 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1450 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1451
1452 AutoCaller autoCaller(this);
1453 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1454
1455 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1456
1457 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1458 if (mHWData->mCPUHotPlugEnabled)
1459 {
1460 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1461 {
1462 if (mHWData->mCPUAttached[idx])
1463 return setError(E_INVALIDARG,
1464 tr("There is still a CPU attached to socket %lu."
1465 "Detach the CPU before removing the socket"),
1466 CPUCount, idx+1);
1467 }
1468 }
1469
1470 HRESULT rc = checkStateDependency(MutableStateDep);
1471 if (FAILED(rc)) return rc;
1472
1473 setModified(IsModified_MachineData);
1474 mHWData.backup();
1475 mHWData->mCPUCount = CPUCount;
1476
1477 return S_OK;
1478}
1479
1480STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1481{
1482 CheckComArgOutPointerValid(aExecutionCap);
1483
1484 AutoCaller autoCaller(this);
1485 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1486
1487 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1488
1489 *aExecutionCap = mHWData->mCpuExecutionCap;
1490
1491 return S_OK;
1492}
1493
1494STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1495{
1496 HRESULT rc = S_OK;
1497
1498 /* check throttle limits */
1499 if ( aExecutionCap < 1
1500 || aExecutionCap > 100
1501 )
1502 return setError(E_INVALIDARG,
1503 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1504 aExecutionCap, 1, 100);
1505
1506 AutoCaller autoCaller(this);
1507 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1508
1509 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1510
1511 alock.release();
1512 rc = onCPUExecutionCapChange(aExecutionCap);
1513 alock.acquire();
1514 if (FAILED(rc)) return rc;
1515
1516 setModified(IsModified_MachineData);
1517 mHWData.backup();
1518 mHWData->mCpuExecutionCap = aExecutionCap;
1519
1520 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1521 if (Global::IsOnline(mData->mMachineState))
1522 saveSettings(NULL);
1523
1524 return S_OK;
1525}
1526
1527
1528STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *aEnabled)
1529{
1530 CheckComArgOutPointerValid(aEnabled);
1531
1532 AutoCaller autoCaller(this);
1533 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1534
1535 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1536
1537 *aEnabled = mHWData->mCPUHotPlugEnabled;
1538
1539 return S_OK;
1540}
1541
1542STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL aEnabled)
1543{
1544 HRESULT rc = S_OK;
1545
1546 AutoCaller autoCaller(this);
1547 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1548
1549 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1550
1551 rc = checkStateDependency(MutableStateDep);
1552 if (FAILED(rc)) return rc;
1553
1554 if (mHWData->mCPUHotPlugEnabled != aEnabled)
1555 {
1556 if (aEnabled)
1557 {
1558 setModified(IsModified_MachineData);
1559 mHWData.backup();
1560
1561 /* Add the amount of CPUs currently attached */
1562 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1563 {
1564 mHWData->mCPUAttached[i] = true;
1565 }
1566 }
1567 else
1568 {
1569 /*
1570 * We can disable hotplug only if the amount of maximum CPUs is equal
1571 * to the amount of attached CPUs
1572 */
1573 unsigned cCpusAttached = 0;
1574 unsigned iHighestId = 0;
1575
1576 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1577 {
1578 if (mHWData->mCPUAttached[i])
1579 {
1580 cCpusAttached++;
1581 iHighestId = i;
1582 }
1583 }
1584
1585 if ( (cCpusAttached != mHWData->mCPUCount)
1586 || (iHighestId >= mHWData->mCPUCount))
1587 return setError(E_INVALIDARG,
1588 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1589
1590 setModified(IsModified_MachineData);
1591 mHWData.backup();
1592 }
1593 }
1594
1595 mHWData->mCPUHotPlugEnabled = aEnabled;
1596
1597 return rc;
1598}
1599
1600STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *aEnabled)
1601{
1602#ifdef VBOX_WITH_USB_CARDREADER
1603 CheckComArgOutPointerValid(aEnabled);
1604
1605 AutoCaller autoCaller(this);
1606 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1607
1608 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1609
1610 *aEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1611
1612 return S_OK;
1613#else
1614 NOREF(aEnabled);
1615 return E_NOTIMPL;
1616#endif
1617}
1618
1619STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL aEnabled)
1620{
1621#ifdef VBOX_WITH_USB_CARDREADER
1622 AutoCaller autoCaller(this);
1623 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1624 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1625
1626 HRESULT rc = checkStateDependency(MutableStateDep);
1627 if (FAILED(rc)) return rc;
1628
1629 setModified(IsModified_MachineData);
1630 mHWData.backup();
1631 mHWData->mEmulatedUSBCardReaderEnabled = aEnabled;
1632
1633 return S_OK;
1634#else
1635 NOREF(aEnabled);
1636 return E_NOTIMPL;
1637#endif
1638}
1639
1640STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *aEnabled)
1641{
1642#ifdef VBOX_WITH_USB_VIDEO
1643 CheckComArgOutPointerValid(aEnabled);
1644
1645 AutoCaller autoCaller(this);
1646 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1647
1648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1649
1650 *aEnabled = mHWData->mEmulatedUSBWebcamEnabled;
1651
1652 return S_OK;
1653#else
1654 NOREF(aEnabled);
1655 return E_NOTIMPL;
1656#endif
1657}
1658
1659STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL aEnabled)
1660{
1661#ifdef VBOX_WITH_USB_VIDEO
1662 AutoCaller autoCaller(this);
1663 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1664 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1665
1666 HRESULT rc = checkStateDependency(MutableStateDep);
1667 if (FAILED(rc)) return rc;
1668
1669 setModified(IsModified_MachineData);
1670 mHWData.backup();
1671 mHWData->mEmulatedUSBWebcamEnabled = aEnabled;
1672
1673 return S_OK;
1674#else
1675 NOREF(aEnabled);
1676 return E_NOTIMPL;
1677#endif
1678}
1679
1680STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *aEnabled)
1681{
1682 CheckComArgOutPointerValid(aEnabled);
1683
1684 AutoCaller autoCaller(this);
1685 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1686 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1687
1688 *aEnabled = mHWData->mHPETEnabled;
1689
1690 return S_OK;
1691}
1692
1693STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL aEnabled)
1694{
1695 HRESULT rc = S_OK;
1696
1697 AutoCaller autoCaller(this);
1698 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1699 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1700
1701 rc = checkStateDependency(MutableStateDep);
1702 if (FAILED(rc)) return rc;
1703
1704 setModified(IsModified_MachineData);
1705 mHWData.backup();
1706
1707 mHWData->mHPETEnabled = aEnabled;
1708
1709 return rc;
1710}
1711
1712STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
1713{
1714 AutoCaller autoCaller(this);
1715 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1716
1717 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1718
1719 *fEnabled = mHWData->mVideoCaptureEnabled;
1720 return S_OK;
1721}
1722
1723STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1724{
1725 HRESULT rc = S_OK;
1726
1727 AutoCaller autoCaller(this);
1728 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1729 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1730
1731 setModified(IsModified_MachineData);
1732 mHWData.backup();
1733 mHWData->mVideoCaptureEnabled = fEnabled;
1734
1735 alock.release();
1736 rc = onVideoCaptureChange();
1737 alock.acquire();
1738 if (FAILED(rc))
1739 {
1740 /*
1741 * Normally we would do the actual change _after_ onVideoCaptureChange() succeeded.
1742 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1743 * determine if it should start or stop capturing. Therefore we need to manually
1744 * undo change.
1745 */
1746 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1747 return rc;
1748 }
1749
1750 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1751 if (Global::IsOnline(mData->mMachineState))
1752 saveSettings(NULL);
1753
1754 return rc;
1755}
1756
1757STDMETHODIMP Machine::COMGETTER(VideoCaptureScreens)(ComSafeArrayOut(BOOL, aScreens))
1758{
1759 CheckComArgOutSafeArrayPointerValid(aScreens);
1760
1761 AutoCaller autoCaller(this);
1762 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1763
1764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1765
1766 SafeArray<BOOL> screens(mHWData->mMonitorCount);
1767 for (unsigned i = 0; i < screens.size(); i++)
1768 screens[i] = mHWData->maVideoCaptureScreens[i];
1769 screens.detachTo(ComSafeArrayOutArg(aScreens));
1770 return S_OK;
1771}
1772
1773STDMETHODIMP Machine::COMSETTER(VideoCaptureScreens)(ComSafeArrayIn(BOOL, aScreens))
1774{
1775 SafeArray<BOOL> screens(ComSafeArrayInArg(aScreens));
1776 AssertReturn(screens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1777 bool fChanged = false;
1778
1779 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1780
1781 for (unsigned i = 0; i < screens.size(); i++)
1782 {
1783 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(screens[i]))
1784 {
1785 mHWData->maVideoCaptureScreens[i] = RT_BOOL(screens[i]);
1786 fChanged = true;
1787 }
1788 }
1789 if (fChanged)
1790 {
1791 alock.release();
1792 HRESULT rc = onVideoCaptureChange();
1793 alock.acquire();
1794 if (FAILED(rc)) return rc;
1795 setModified(IsModified_MachineData);
1796
1797 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1798 if (Global::IsOnline(mData->mMachineState))
1799 saveSettings(NULL);
1800 }
1801
1802 return S_OK;
1803}
1804
1805STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR *apFile)
1806{
1807 AutoCaller autoCaller(this);
1808 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1809
1810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1811 if (mHWData->mVideoCaptureFile.isEmpty())
1812 {
1813 Utf8Str defaultFile;
1814 getDefaultVideoCaptureFile(defaultFile);
1815 defaultFile.cloneTo(apFile);
1816 }
1817 else
1818 mHWData->mVideoCaptureFile.cloneTo(apFile);
1819 return S_OK;
1820}
1821
1822STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1823{
1824 Utf8Str strFile(aFile);
1825 AutoCaller autoCaller(this);
1826 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1827
1828 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1829
1830 if ( Global::IsOnline(mData->mMachineState)
1831 && mHWData->mVideoCaptureEnabled)
1832 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1833
1834 if (!RTPathStartsWithRoot(strFile.c_str()))
1835 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1836
1837 if (!strFile.isEmpty())
1838 {
1839 Utf8Str defaultFile;
1840 getDefaultVideoCaptureFile(defaultFile);
1841 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1842 strFile.setNull();
1843 }
1844
1845 setModified(IsModified_MachineData);
1846 mHWData.backup();
1847 mHWData->mVideoCaptureFile = strFile;
1848
1849 return S_OK;
1850}
1851
1852STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *aHorzRes)
1853{
1854 AutoCaller autoCaller(this);
1855 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1856
1857 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1858 *aHorzRes = mHWData->mVideoCaptureWidth;
1859 return S_OK;
1860}
1861
1862STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG aHorzRes)
1863{
1864 AutoCaller autoCaller(this);
1865 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1866
1867 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1868
1869 if ( Global::IsOnline(mData->mMachineState)
1870 && mHWData->mVideoCaptureEnabled)
1871 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1872
1873 setModified(IsModified_MachineData);
1874 mHWData.backup();
1875 mHWData->mVideoCaptureWidth = aHorzRes;
1876
1877 return S_OK;
1878}
1879
1880STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *aVertRes)
1881{
1882 AutoCaller autoCaller(this);
1883 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1884
1885 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1886 *aVertRes = mHWData->mVideoCaptureHeight;
1887 return S_OK;
1888}
1889
1890STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG aVertRes)
1891{
1892 AutoCaller autoCaller(this);
1893 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1894
1895 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1896
1897 if ( Global::IsOnline(mData->mMachineState)
1898 && mHWData->mVideoCaptureEnabled)
1899 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1900
1901 setModified(IsModified_MachineData);
1902 mHWData.backup();
1903 mHWData->mVideoCaptureHeight = aVertRes;
1904
1905 return S_OK;
1906}
1907
1908STDMETHODIMP Machine::COMGETTER(VideoCaptureRate)(ULONG *aRate)
1909{
1910 AutoCaller autoCaller(this);
1911 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1912
1913 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1914 *aRate = mHWData->mVideoCaptureRate;
1915 return S_OK;
1916}
1917
1918STDMETHODIMP Machine::COMSETTER(VideoCaptureRate)(ULONG aRate)
1919{
1920 AutoCaller autoCaller(this);
1921 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1922
1923 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1924
1925 if ( Global::IsOnline(mData->mMachineState)
1926 && mHWData->mVideoCaptureEnabled)
1927 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1928
1929 setModified(IsModified_MachineData);
1930 mHWData.backup();
1931 mHWData->mVideoCaptureRate = aRate;
1932
1933 return S_OK;
1934}
1935
1936STDMETHODIMP Machine::COMGETTER(VideoCaptureFPS)(ULONG *aFPS)
1937{
1938 AutoCaller autoCaller(this);
1939 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1940
1941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1942 *aFPS = mHWData->mVideoCaptureFPS;
1943 return S_OK;
1944}
1945
1946STDMETHODIMP Machine::COMSETTER(VideoCaptureFPS)(ULONG aFPS)
1947{
1948 AutoCaller autoCaller(this);
1949 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1950
1951 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1952
1953 if ( Global::IsOnline(mData->mMachineState)
1954 && mHWData->mVideoCaptureEnabled)
1955 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1956
1957 setModified(IsModified_MachineData);
1958 mHWData.backup();
1959 mHWData->mVideoCaptureFPS = aFPS;
1960
1961 return S_OK;
1962}
1963
1964STDMETHODIMP Machine::COMGETTER(GraphicsControllerType)(GraphicsControllerType_T *aGraphicsControllerType)
1965{
1966 CheckComArgOutPointerValid(aGraphicsControllerType);
1967
1968 AutoCaller autoCaller(this);
1969 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1970
1971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1972
1973 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1974
1975 return S_OK;
1976}
1977
1978STDMETHODIMP Machine::COMSETTER(GraphicsControllerType)(GraphicsControllerType_T aGraphicsControllerType)
1979{
1980 switch (aGraphicsControllerType)
1981 {
1982 case GraphicsControllerType_Null:
1983 case GraphicsControllerType_VBoxVGA:
1984 break;
1985 default:
1986 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1987 }
1988
1989 AutoCaller autoCaller(this);
1990 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1991
1992 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1993
1994 HRESULT rc = checkStateDependency(MutableStateDep);
1995 if (FAILED(rc)) return rc;
1996
1997 setModified(IsModified_MachineData);
1998 mHWData.backup();
1999 mHWData->mGraphicsControllerType = aGraphicsControllerType;
2000
2001 return S_OK;
2002}
2003
2004STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
2005{
2006 CheckComArgOutPointerValid(memorySize);
2007
2008 AutoCaller autoCaller(this);
2009 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2010
2011 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2012
2013 *memorySize = mHWData->mVRAMSize;
2014
2015 return S_OK;
2016}
2017
2018STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
2019{
2020 /* check VRAM limits */
2021 if (memorySize < SchemaDefs::MinGuestVRAM ||
2022 memorySize > SchemaDefs::MaxGuestVRAM)
2023 return setError(E_INVALIDARG,
2024 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2025 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2026
2027 AutoCaller autoCaller(this);
2028 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2029
2030 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2031
2032 HRESULT rc = checkStateDependency(MutableStateDep);
2033 if (FAILED(rc)) return rc;
2034
2035 setModified(IsModified_MachineData);
2036 mHWData.backup();
2037 mHWData->mVRAMSize = memorySize;
2038
2039 return S_OK;
2040}
2041
2042/** @todo this method should not be public */
2043STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
2044{
2045 CheckComArgOutPointerValid(memoryBalloonSize);
2046
2047 AutoCaller autoCaller(this);
2048 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2049
2050 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2051
2052 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
2053
2054 return S_OK;
2055}
2056
2057/**
2058 * Set the memory balloon size.
2059 *
2060 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2061 * we have to make sure that we never call IGuest from here.
2062 */
2063STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
2064{
2065 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2066#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2067 /* check limits */
2068 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2069 return setError(E_INVALIDARG,
2070 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2071 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2072
2073 AutoCaller autoCaller(this);
2074 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2075
2076 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2077
2078 setModified(IsModified_MachineData);
2079 mHWData.backup();
2080 mHWData->mMemoryBalloonSize = memoryBalloonSize;
2081
2082 return S_OK;
2083#else
2084 NOREF(memoryBalloonSize);
2085 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2086#endif
2087}
2088
2089STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *aEnabled)
2090{
2091 CheckComArgOutPointerValid(aEnabled);
2092
2093 AutoCaller autoCaller(this);
2094 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2095
2096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2097
2098 *aEnabled = mHWData->mPageFusionEnabled;
2099 return S_OK;
2100}
2101
2102STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL aEnabled)
2103{
2104#ifdef VBOX_WITH_PAGE_SHARING
2105 AutoCaller autoCaller(this);
2106 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2107
2108 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2109
2110 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2111 setModified(IsModified_MachineData);
2112 mHWData.backup();
2113 mHWData->mPageFusionEnabled = aEnabled;
2114 return S_OK;
2115#else
2116 NOREF(aEnabled);
2117 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2118#endif
2119}
2120
2121STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *aEnabled)
2122{
2123 CheckComArgOutPointerValid(aEnabled);
2124
2125 AutoCaller autoCaller(this);
2126 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2127
2128 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2129
2130 *aEnabled = mHWData->mAccelerate3DEnabled;
2131
2132 return S_OK;
2133}
2134
2135STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
2136{
2137 AutoCaller autoCaller(this);
2138 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2139
2140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2141
2142 HRESULT rc = checkStateDependency(MutableStateDep);
2143 if (FAILED(rc)) return rc;
2144
2145 /** @todo check validity! */
2146
2147 setModified(IsModified_MachineData);
2148 mHWData.backup();
2149 mHWData->mAccelerate3DEnabled = enable;
2150
2151 return S_OK;
2152}
2153
2154
2155STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *aEnabled)
2156{
2157 CheckComArgOutPointerValid(aEnabled);
2158
2159 AutoCaller autoCaller(this);
2160 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2161
2162 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2163
2164 *aEnabled = mHWData->mAccelerate2DVideoEnabled;
2165
2166 return S_OK;
2167}
2168
2169STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
2170{
2171 AutoCaller autoCaller(this);
2172 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2173
2174 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2175
2176 HRESULT rc = checkStateDependency(MutableStateDep);
2177 if (FAILED(rc)) return rc;
2178
2179 /** @todo check validity! */
2180
2181 setModified(IsModified_MachineData);
2182 mHWData.backup();
2183 mHWData->mAccelerate2DVideoEnabled = enable;
2184
2185 return S_OK;
2186}
2187
2188STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
2189{
2190 CheckComArgOutPointerValid(monitorCount);
2191
2192 AutoCaller autoCaller(this);
2193 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2194
2195 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2196
2197 *monitorCount = mHWData->mMonitorCount;
2198
2199 return S_OK;
2200}
2201
2202STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
2203{
2204 /* make sure monitor count is a sensible number */
2205 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
2206 return setError(E_INVALIDARG,
2207 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2208 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
2209
2210 AutoCaller autoCaller(this);
2211 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2212
2213 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2214
2215 HRESULT rc = checkStateDependency(MutableStateDep);
2216 if (FAILED(rc)) return rc;
2217
2218 setModified(IsModified_MachineData);
2219 mHWData.backup();
2220 mHWData->mMonitorCount = monitorCount;
2221
2222 return S_OK;
2223}
2224
2225STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2226{
2227 CheckComArgOutPointerValid(biosSettings);
2228
2229 AutoCaller autoCaller(this);
2230 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2231
2232 /* mBIOSSettings is constant during life time, no need to lock */
2233 mBIOSSettings.queryInterfaceTo(biosSettings);
2234
2235 return S_OK;
2236}
2237
2238STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2239{
2240 CheckComArgOutPointerValid(aVal);
2241
2242 AutoCaller autoCaller(this);
2243 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2244
2245 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2246
2247 switch (property)
2248 {
2249 case CPUPropertyType_PAE:
2250 *aVal = mHWData->mPAEEnabled;
2251 break;
2252
2253 case CPUPropertyType_Synthetic:
2254 *aVal = mHWData->mSyntheticCpu;
2255 break;
2256
2257 case CPUPropertyType_LongMode:
2258 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2259 *aVal = TRUE;
2260 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2261 *aVal = FALSE;
2262#if HC_ARCH_BITS == 64
2263 else
2264 *aVal = TRUE;
2265#else
2266 else
2267 {
2268 *aVal = FALSE;
2269
2270 ComPtr<IGuestOSType> ptrGuestOSType;
2271 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2272 if (SUCCEEDED(hrc2))
2273 {
2274 BOOL fIs64Bit = FALSE;
2275 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2276 if (SUCCEEDED(hrc2) && fIs64Bit)
2277 {
2278 ComObjPtr<Host> ptrHost = mParent->host();
2279 alock.release();
2280
2281 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal); AssertComRC(hrc2);
2282 if (FAILED(hrc2))
2283 *aVal = FALSE;
2284 }
2285 }
2286 }
2287#endif
2288 break;
2289
2290 default:
2291 return E_INVALIDARG;
2292 }
2293 return S_OK;
2294}
2295
2296STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2297{
2298 AutoCaller autoCaller(this);
2299 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2300
2301 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2302
2303 HRESULT rc = checkStateDependency(MutableStateDep);
2304 if (FAILED(rc)) return rc;
2305
2306 switch (property)
2307 {
2308 case CPUPropertyType_PAE:
2309 setModified(IsModified_MachineData);
2310 mHWData.backup();
2311 mHWData->mPAEEnabled = !!aVal;
2312 break;
2313
2314 case CPUPropertyType_Synthetic:
2315 setModified(IsModified_MachineData);
2316 mHWData.backup();
2317 mHWData->mSyntheticCpu = !!aVal;
2318 break;
2319
2320 case CPUPropertyType_LongMode:
2321 setModified(IsModified_MachineData);
2322 mHWData.backup();
2323 mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2324 break;
2325
2326 default:
2327 return E_INVALIDARG;
2328 }
2329 return S_OK;
2330}
2331
2332STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2333{
2334 CheckComArgOutPointerValid(aValEax);
2335 CheckComArgOutPointerValid(aValEbx);
2336 CheckComArgOutPointerValid(aValEcx);
2337 CheckComArgOutPointerValid(aValEdx);
2338
2339 AutoCaller autoCaller(this);
2340 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2341
2342 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2343
2344 switch(aId)
2345 {
2346 case 0x0:
2347 case 0x1:
2348 case 0x2:
2349 case 0x3:
2350 case 0x4:
2351 case 0x5:
2352 case 0x6:
2353 case 0x7:
2354 case 0x8:
2355 case 0x9:
2356 case 0xA:
2357 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2358 return E_INVALIDARG;
2359
2360 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2361 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2362 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2363 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2364 break;
2365
2366 case 0x80000000:
2367 case 0x80000001:
2368 case 0x80000002:
2369 case 0x80000003:
2370 case 0x80000004:
2371 case 0x80000005:
2372 case 0x80000006:
2373 case 0x80000007:
2374 case 0x80000008:
2375 case 0x80000009:
2376 case 0x8000000A:
2377 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2378 return E_INVALIDARG;
2379
2380 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2381 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2382 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2383 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2384 break;
2385
2386 default:
2387 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2388 }
2389 return S_OK;
2390}
2391
2392STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2393{
2394 AutoCaller autoCaller(this);
2395 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2396
2397 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2398
2399 HRESULT rc = checkStateDependency(MutableStateDep);
2400 if (FAILED(rc)) return rc;
2401
2402 switch(aId)
2403 {
2404 case 0x0:
2405 case 0x1:
2406 case 0x2:
2407 case 0x3:
2408 case 0x4:
2409 case 0x5:
2410 case 0x6:
2411 case 0x7:
2412 case 0x8:
2413 case 0x9:
2414 case 0xA:
2415 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2416 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2417 setModified(IsModified_MachineData);
2418 mHWData.backup();
2419 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2420 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2421 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2422 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2423 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2424 break;
2425
2426 case 0x80000000:
2427 case 0x80000001:
2428 case 0x80000002:
2429 case 0x80000003:
2430 case 0x80000004:
2431 case 0x80000005:
2432 case 0x80000006:
2433 case 0x80000007:
2434 case 0x80000008:
2435 case 0x80000009:
2436 case 0x8000000A:
2437 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2438 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2439 setModified(IsModified_MachineData);
2440 mHWData.backup();
2441 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2442 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2443 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2444 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2445 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2446 break;
2447
2448 default:
2449 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2450 }
2451 return S_OK;
2452}
2453
2454STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2455{
2456 AutoCaller autoCaller(this);
2457 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2458
2459 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2460
2461 HRESULT rc = checkStateDependency(MutableStateDep);
2462 if (FAILED(rc)) return rc;
2463
2464 switch(aId)
2465 {
2466 case 0x0:
2467 case 0x1:
2468 case 0x2:
2469 case 0x3:
2470 case 0x4:
2471 case 0x5:
2472 case 0x6:
2473 case 0x7:
2474 case 0x8:
2475 case 0x9:
2476 case 0xA:
2477 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2478 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2479 setModified(IsModified_MachineData);
2480 mHWData.backup();
2481 /* Invalidate leaf. */
2482 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2483 break;
2484
2485 case 0x80000000:
2486 case 0x80000001:
2487 case 0x80000002:
2488 case 0x80000003:
2489 case 0x80000004:
2490 case 0x80000005:
2491 case 0x80000006:
2492 case 0x80000007:
2493 case 0x80000008:
2494 case 0x80000009:
2495 case 0x8000000A:
2496 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2497 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2498 setModified(IsModified_MachineData);
2499 mHWData.backup();
2500 /* Invalidate leaf. */
2501 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2502 break;
2503
2504 default:
2505 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2506 }
2507 return S_OK;
2508}
2509
2510STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2511{
2512 AutoCaller autoCaller(this);
2513 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2514
2515 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2516
2517 HRESULT rc = checkStateDependency(MutableStateDep);
2518 if (FAILED(rc)) return rc;
2519
2520 setModified(IsModified_MachineData);
2521 mHWData.backup();
2522
2523 /* Invalidate all standard leafs. */
2524 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2525 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2526
2527 /* Invalidate all extended leafs. */
2528 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2529 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2530
2531 return S_OK;
2532}
2533
2534STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2535{
2536 CheckComArgOutPointerValid(aVal);
2537
2538 AutoCaller autoCaller(this);
2539 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2540
2541 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2542
2543 switch(property)
2544 {
2545 case HWVirtExPropertyType_Enabled:
2546 *aVal = mHWData->mHWVirtExEnabled;
2547 break;
2548
2549 case HWVirtExPropertyType_Exclusive:
2550 *aVal = mHWData->mHWVirtExExclusive;
2551 break;
2552
2553 case HWVirtExPropertyType_VPID:
2554 *aVal = mHWData->mHWVirtExVPIDEnabled;
2555 break;
2556
2557 case HWVirtExPropertyType_NestedPaging:
2558 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2559 break;
2560
2561 case HWVirtExPropertyType_UnrestrictedExecution:
2562 *aVal = mHWData->mHWVirtExUXEnabled;
2563 break;
2564
2565 case HWVirtExPropertyType_LargePages:
2566 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2567#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2568 *aVal = FALSE;
2569#endif
2570 break;
2571
2572 case HWVirtExPropertyType_Force:
2573 *aVal = mHWData->mHWVirtExForceEnabled;
2574 break;
2575
2576 default:
2577 return E_INVALIDARG;
2578 }
2579 return S_OK;
2580}
2581
2582STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2583{
2584 AutoCaller autoCaller(this);
2585 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2586
2587 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2588
2589 HRESULT rc = checkStateDependency(MutableStateDep);
2590 if (FAILED(rc)) return rc;
2591
2592 switch(property)
2593 {
2594 case HWVirtExPropertyType_Enabled:
2595 setModified(IsModified_MachineData);
2596 mHWData.backup();
2597 mHWData->mHWVirtExEnabled = !!aVal;
2598 break;
2599
2600 case HWVirtExPropertyType_Exclusive:
2601 setModified(IsModified_MachineData);
2602 mHWData.backup();
2603 mHWData->mHWVirtExExclusive = !!aVal;
2604 break;
2605
2606 case HWVirtExPropertyType_VPID:
2607 setModified(IsModified_MachineData);
2608 mHWData.backup();
2609 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2610 break;
2611
2612 case HWVirtExPropertyType_NestedPaging:
2613 setModified(IsModified_MachineData);
2614 mHWData.backup();
2615 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2616 break;
2617
2618 case HWVirtExPropertyType_UnrestrictedExecution:
2619 setModified(IsModified_MachineData);
2620 mHWData.backup();
2621 mHWData->mHWVirtExUXEnabled = !!aVal;
2622 break;
2623
2624 case HWVirtExPropertyType_LargePages:
2625 setModified(IsModified_MachineData);
2626 mHWData.backup();
2627 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2628 break;
2629
2630 case HWVirtExPropertyType_Force:
2631 setModified(IsModified_MachineData);
2632 mHWData.backup();
2633 mHWData->mHWVirtExForceEnabled = !!aVal;
2634 break;
2635
2636 default:
2637 return E_INVALIDARG;
2638 }
2639
2640 return S_OK;
2641}
2642
2643STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2644{
2645 CheckComArgOutPointerValid(aSnapshotFolder);
2646
2647 AutoCaller autoCaller(this);
2648 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2649
2650 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2651
2652 Utf8Str strFullSnapshotFolder;
2653 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2654 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2655
2656 return S_OK;
2657}
2658
2659STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2660{
2661 /* @todo (r=dmik):
2662 * 1. Allow to change the name of the snapshot folder containing snapshots
2663 * 2. Rename the folder on disk instead of just changing the property
2664 * value (to be smart and not to leave garbage). Note that it cannot be
2665 * done here because the change may be rolled back. Thus, the right
2666 * place is #saveSettings().
2667 */
2668
2669 AutoCaller autoCaller(this);
2670 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2671
2672 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2673
2674 HRESULT rc = checkStateDependency(MutableStateDep);
2675 if (FAILED(rc)) return rc;
2676
2677 if (!mData->mCurrentSnapshot.isNull())
2678 return setError(E_FAIL,
2679 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2680
2681 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2682
2683 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2684 if (strSnapshotFolder.isEmpty())
2685 strSnapshotFolder = "Snapshots";
2686 int vrc = calculateFullPath(strSnapshotFolder,
2687 strSnapshotFolder);
2688 if (RT_FAILURE(vrc))
2689 return setError(E_FAIL,
2690 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2691 aSnapshotFolder, vrc);
2692
2693 setModified(IsModified_MachineData);
2694 mUserData.backup();
2695
2696 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2697
2698 return S_OK;
2699}
2700
2701STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2702{
2703 CheckComArgOutSafeArrayPointerValid(aAttachments);
2704
2705 AutoCaller autoCaller(this);
2706 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2707
2708 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2709
2710 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2711 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2712
2713 return S_OK;
2714}
2715
2716STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2717{
2718 CheckComArgOutPointerValid(vrdeServer);
2719
2720 AutoCaller autoCaller(this);
2721 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2722
2723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2724
2725 Assert(!!mVRDEServer);
2726 mVRDEServer.queryInterfaceTo(vrdeServer);
2727
2728 return S_OK;
2729}
2730
2731STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2732{
2733 CheckComArgOutPointerValid(audioAdapter);
2734
2735 AutoCaller autoCaller(this);
2736 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2737
2738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2739
2740 mAudioAdapter.queryInterfaceTo(audioAdapter);
2741 return S_OK;
2742}
2743
2744STDMETHODIMP Machine::COMGETTER(USBControllers)(ComSafeArrayOut(IUSBController *, aUSBControllers))
2745{
2746#ifdef VBOX_WITH_VUSB
2747 CheckComArgOutPointerValid(aUSBControllers);
2748
2749 AutoCaller autoCaller(this);
2750 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2751
2752 clearError();
2753 MultiResult rc(S_OK);
2754
2755# ifdef VBOX_WITH_USB
2756 rc = mParent->host()->checkUSBProxyService();
2757 if (FAILED(rc)) return rc;
2758# endif
2759
2760 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2761
2762 SafeIfaceArray<IUSBController> ctrls(*mUSBControllers.data());
2763 ctrls.detachTo(ComSafeArrayOutArg(aUSBControllers));
2764 return S_OK;
2765#else
2766 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2767 * extended error info to indicate that USB is simply not available
2768 * (w/o treating it as a failure), for example, as in OSE */
2769 NOREF(aUSBControllers);
2770 ReturnComNotImplemented();
2771#endif /* VBOX_WITH_VUSB */
2772}
2773
2774STDMETHODIMP Machine::COMGETTER(USBDeviceFilters)(IUSBDeviceFilters **aUSBDeviceFilters)
2775{
2776#ifdef VBOX_WITH_VUSB
2777 CheckComArgOutPointerValid(aUSBDeviceFilters);
2778
2779 AutoCaller autoCaller(this);
2780 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2781
2782 clearError();
2783 MultiResult rc(S_OK);
2784
2785# ifdef VBOX_WITH_USB
2786 rc = mParent->host()->checkUSBProxyService();
2787 if (FAILED(rc)) return rc;
2788# endif
2789
2790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2791
2792 return rc = mUSBDeviceFilters.queryInterfaceTo(aUSBDeviceFilters);
2793#else
2794 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2795 * extended error info to indicate that USB is simply not available
2796 * (w/o treating it as a failure), for example, as in OSE */
2797 NOREF(aUSBDeviceFilters);
2798 ReturnComNotImplemented();
2799#endif /* VBOX_WITH_VUSB */
2800}
2801
2802STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2803{
2804 CheckComArgOutPointerValid(aFilePath);
2805
2806 AutoLimitedCaller autoCaller(this);
2807 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2808
2809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2810
2811 mData->m_strConfigFileFull.cloneTo(aFilePath);
2812 return S_OK;
2813}
2814
2815STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2816{
2817 CheckComArgOutPointerValid(aModified);
2818
2819 AutoCaller autoCaller(this);
2820 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2821
2822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2823
2824 HRESULT rc = checkStateDependency(MutableStateDep);
2825 if (FAILED(rc)) return rc;
2826
2827 if (!mData->pMachineConfigFile->fileExists())
2828 // this is a new machine, and no config file exists yet:
2829 *aModified = TRUE;
2830 else
2831 *aModified = (mData->flModifications != 0);
2832
2833 return S_OK;
2834}
2835
2836STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2837{
2838 CheckComArgOutPointerValid(aSessionState);
2839
2840 AutoCaller autoCaller(this);
2841 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2842
2843 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2844
2845 *aSessionState = mData->mSession.mState;
2846
2847 return S_OK;
2848}
2849
2850STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2851{
2852 CheckComArgOutPointerValid(aSessionType);
2853
2854 AutoCaller autoCaller(this);
2855 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2856
2857 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2858
2859 mData->mSession.mType.cloneTo(aSessionType);
2860
2861 return S_OK;
2862}
2863
2864STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2865{
2866 CheckComArgOutPointerValid(aSessionPID);
2867
2868 AutoCaller autoCaller(this);
2869 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2870
2871 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2872
2873 *aSessionPID = mData->mSession.mPID;
2874
2875 return S_OK;
2876}
2877
2878STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2879{
2880 CheckComArgOutPointerValid(machineState);
2881
2882 AutoCaller autoCaller(this);
2883 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2884
2885 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2886
2887 *machineState = mData->mMachineState;
2888
2889 return S_OK;
2890}
2891
2892STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2893{
2894 CheckComArgOutPointerValid(aLastStateChange);
2895
2896 AutoCaller autoCaller(this);
2897 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2898
2899 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2900
2901 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2902
2903 return S_OK;
2904}
2905
2906STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2907{
2908 CheckComArgOutPointerValid(aStateFilePath);
2909
2910 AutoCaller autoCaller(this);
2911 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2912
2913 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2914
2915 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2916
2917 return S_OK;
2918}
2919
2920STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2921{
2922 CheckComArgOutPointerValid(aLogFolder);
2923
2924 AutoCaller autoCaller(this);
2925 AssertComRCReturnRC(autoCaller.rc());
2926
2927 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2928
2929 Utf8Str logFolder;
2930 getLogFolder(logFolder);
2931 logFolder.cloneTo(aLogFolder);
2932
2933 return S_OK;
2934}
2935
2936STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2937{
2938 CheckComArgOutPointerValid(aCurrentSnapshot);
2939
2940 AutoCaller autoCaller(this);
2941 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2942
2943 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2944
2945 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2946
2947 return S_OK;
2948}
2949
2950STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2951{
2952 CheckComArgOutPointerValid(aSnapshotCount);
2953
2954 AutoCaller autoCaller(this);
2955 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2956
2957 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2958
2959 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2960 ? 0
2961 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2962
2963 return S_OK;
2964}
2965
2966STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2967{
2968 CheckComArgOutPointerValid(aCurrentStateModified);
2969
2970 AutoCaller autoCaller(this);
2971 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2972
2973 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2974
2975 /* Note: for machines with no snapshots, we always return FALSE
2976 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2977 * reasons :) */
2978
2979 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2980 ? FALSE
2981 : mData->mCurrentStateModified;
2982
2983 return S_OK;
2984}
2985
2986STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2987{
2988 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2989
2990 AutoCaller autoCaller(this);
2991 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2992
2993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2994
2995 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2996 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2997
2998 return S_OK;
2999}
3000
3001STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
3002{
3003 CheckComArgOutPointerValid(aClipboardMode);
3004
3005 AutoCaller autoCaller(this);
3006 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3007
3008 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3009
3010 *aClipboardMode = mHWData->mClipboardMode;
3011
3012 return S_OK;
3013}
3014
3015STDMETHODIMP Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
3016{
3017 HRESULT rc = S_OK;
3018
3019 AutoCaller autoCaller(this);
3020 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3021
3022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3023
3024 alock.release();
3025 rc = onClipboardModeChange(aClipboardMode);
3026 alock.acquire();
3027 if (FAILED(rc)) return rc;
3028
3029 setModified(IsModified_MachineData);
3030 mHWData.backup();
3031 mHWData->mClipboardMode = aClipboardMode;
3032
3033 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
3034 if (Global::IsOnline(mData->mMachineState))
3035 saveSettings(NULL);
3036
3037 return S_OK;
3038}
3039
3040STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
3041{
3042 CheckComArgOutPointerValid(aDragAndDropMode);
3043
3044 AutoCaller autoCaller(this);
3045 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3046
3047 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3048
3049 *aDragAndDropMode = mHWData->mDragAndDropMode;
3050
3051 return S_OK;
3052}
3053
3054STDMETHODIMP Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
3055{
3056 HRESULT rc = S_OK;
3057
3058 AutoCaller autoCaller(this);
3059 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3060
3061 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3062
3063 alock.release();
3064 rc = onDragAndDropModeChange(aDragAndDropMode);
3065 alock.acquire();
3066 if (FAILED(rc)) return rc;
3067
3068 setModified(IsModified_MachineData);
3069 mHWData.backup();
3070 mHWData->mDragAndDropMode = aDragAndDropMode;
3071
3072 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
3073 if (Global::IsOnline(mData->mMachineState))
3074 saveSettings(NULL);
3075
3076 return S_OK;
3077}
3078
3079STDMETHODIMP Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
3080{
3081 CheckComArgOutPointerValid(aPatterns);
3082
3083 AutoCaller autoCaller(this);
3084 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3085
3086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3087
3088 try
3089 {
3090 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
3091 }
3092 catch (...)
3093 {
3094 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
3095 }
3096
3097 return S_OK;
3098}
3099
3100STDMETHODIMP Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
3101{
3102 AutoCaller autoCaller(this);
3103 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3104
3105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3106
3107 HRESULT rc = checkStateDependency(MutableStateDep);
3108 if (FAILED(rc)) return rc;
3109
3110 setModified(IsModified_MachineData);
3111 mHWData.backup();
3112 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
3113 return rc;
3114}
3115
3116STDMETHODIMP Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
3117{
3118 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
3119
3120 AutoCaller autoCaller(this);
3121 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3122
3123 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3124
3125 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
3126 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
3127
3128 return S_OK;
3129}
3130
3131STDMETHODIMP Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
3132{
3133 CheckComArgOutPointerValid(aEnabled);
3134
3135 AutoCaller autoCaller(this);
3136 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3137
3138 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3139
3140 *aEnabled = mUserData->s.fTeleporterEnabled;
3141
3142 return S_OK;
3143}
3144
3145STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
3146{
3147 AutoCaller autoCaller(this);
3148 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3149
3150 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3151
3152 /* Only allow it to be set to true when PoweredOff or Aborted.
3153 (Clearing it is always permitted.) */
3154 if ( aEnabled
3155 && mData->mRegistered
3156 && ( !isSessionMachine()
3157 || ( mData->mMachineState != MachineState_PoweredOff
3158 && mData->mMachineState != MachineState_Teleported
3159 && mData->mMachineState != MachineState_Aborted
3160 )
3161 )
3162 )
3163 return setError(VBOX_E_INVALID_VM_STATE,
3164 tr("The machine is not powered off (state is %s)"),
3165 Global::stringifyMachineState(mData->mMachineState));
3166
3167 setModified(IsModified_MachineData);
3168 mUserData.backup();
3169 mUserData->s.fTeleporterEnabled = !!aEnabled;
3170
3171 return S_OK;
3172}
3173
3174STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
3175{
3176 CheckComArgOutPointerValid(aPort);
3177
3178 AutoCaller autoCaller(this);
3179 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3180
3181 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3182
3183 *aPort = (ULONG)mUserData->s.uTeleporterPort;
3184
3185 return S_OK;
3186}
3187
3188STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
3189{
3190 if (aPort >= _64K)
3191 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
3192
3193 AutoCaller autoCaller(this);
3194 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3195
3196 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3197
3198 HRESULT rc = checkStateDependency(MutableStateDep);
3199 if (FAILED(rc)) return rc;
3200
3201 setModified(IsModified_MachineData);
3202 mUserData.backup();
3203 mUserData->s.uTeleporterPort = (uint32_t)aPort;
3204
3205 return S_OK;
3206}
3207
3208STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
3209{
3210 CheckComArgOutPointerValid(aAddress);
3211
3212 AutoCaller autoCaller(this);
3213 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3214
3215 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3216
3217 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
3218
3219 return S_OK;
3220}
3221
3222STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
3223{
3224 AutoCaller autoCaller(this);
3225 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3226
3227 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3228
3229 HRESULT rc = checkStateDependency(MutableStateDep);
3230 if (FAILED(rc)) return rc;
3231
3232 setModified(IsModified_MachineData);
3233 mUserData.backup();
3234 mUserData->s.strTeleporterAddress = aAddress;
3235
3236 return S_OK;
3237}
3238
3239STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
3240{
3241 CheckComArgOutPointerValid(aPassword);
3242
3243 AutoCaller autoCaller(this);
3244 HRESULT hrc = autoCaller.rc();
3245 if (SUCCEEDED(hrc))
3246 {
3247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3248 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
3249 }
3250
3251 return hrc;
3252}
3253
3254STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
3255{
3256 /*
3257 * Hash the password first.
3258 */
3259 Utf8Str strPassword(aPassword);
3260 if (!strPassword.isEmpty())
3261 {
3262 if (VBoxIsPasswordHashed(&strPassword))
3263 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3264 VBoxHashPassword(&strPassword);
3265 }
3266
3267 /*
3268 * Do the update.
3269 */
3270 AutoCaller autoCaller(this);
3271 HRESULT hrc = autoCaller.rc();
3272 if (SUCCEEDED(hrc))
3273 {
3274 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3275 hrc = checkStateDependency(MutableStateDep);
3276 if (SUCCEEDED(hrc))
3277 {
3278 setModified(IsModified_MachineData);
3279 mUserData.backup();
3280 mUserData->s.strTeleporterPassword = strPassword;
3281 }
3282 }
3283
3284 return hrc;
3285}
3286
3287STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
3288{
3289 CheckComArgOutPointerValid(aState);
3290
3291 AutoCaller autoCaller(this);
3292 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3293
3294 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3295
3296 *aState = mUserData->s.enmFaultToleranceState;
3297 return S_OK;
3298}
3299
3300STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
3301{
3302 AutoCaller autoCaller(this);
3303 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3304
3305 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3306
3307 /* @todo deal with running state change. */
3308 HRESULT rc = checkStateDependency(MutableStateDep);
3309 if (FAILED(rc)) return rc;
3310
3311 setModified(IsModified_MachineData);
3312 mUserData.backup();
3313 mUserData->s.enmFaultToleranceState = aState;
3314 return S_OK;
3315}
3316
3317STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3318{
3319 CheckComArgOutPointerValid(aAddress);
3320
3321 AutoCaller autoCaller(this);
3322 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3323
3324 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3325
3326 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3327 return S_OK;
3328}
3329
3330STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3331{
3332 AutoCaller autoCaller(this);
3333 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3334
3335 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3336
3337 /* @todo deal with running state change. */
3338 HRESULT rc = checkStateDependency(MutableStateDep);
3339 if (FAILED(rc)) return rc;
3340
3341 setModified(IsModified_MachineData);
3342 mUserData.backup();
3343 mUserData->s.strFaultToleranceAddress = aAddress;
3344 return S_OK;
3345}
3346
3347STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3348{
3349 CheckComArgOutPointerValid(aPort);
3350
3351 AutoCaller autoCaller(this);
3352 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3353
3354 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3355
3356 *aPort = mUserData->s.uFaultTolerancePort;
3357 return S_OK;
3358}
3359
3360STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3361{
3362 AutoCaller autoCaller(this);
3363 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3364
3365 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3366
3367 /* @todo deal with running state change. */
3368 HRESULT rc = checkStateDependency(MutableStateDep);
3369 if (FAILED(rc)) return rc;
3370
3371 setModified(IsModified_MachineData);
3372 mUserData.backup();
3373 mUserData->s.uFaultTolerancePort = aPort;
3374 return S_OK;
3375}
3376
3377STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3378{
3379 CheckComArgOutPointerValid(aPassword);
3380
3381 AutoCaller autoCaller(this);
3382 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3383
3384 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3385
3386 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3387
3388 return S_OK;
3389}
3390
3391STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3392{
3393 AutoCaller autoCaller(this);
3394 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3395
3396 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3397
3398 /* @todo deal with running state change. */
3399 HRESULT rc = checkStateDependency(MutableStateDep);
3400 if (FAILED(rc)) return rc;
3401
3402 setModified(IsModified_MachineData);
3403 mUserData.backup();
3404 mUserData->s.strFaultTolerancePassword = aPassword;
3405
3406 return S_OK;
3407}
3408
3409STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3410{
3411 CheckComArgOutPointerValid(aInterval);
3412
3413 AutoCaller autoCaller(this);
3414 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3415
3416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3417
3418 *aInterval = mUserData->s.uFaultToleranceInterval;
3419 return S_OK;
3420}
3421
3422STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3423{
3424 AutoCaller autoCaller(this);
3425 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3426
3427 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3428
3429 /* @todo deal with running state change. */
3430 HRESULT rc = checkStateDependency(MutableStateDep);
3431 if (FAILED(rc)) return rc;
3432
3433 setModified(IsModified_MachineData);
3434 mUserData.backup();
3435 mUserData->s.uFaultToleranceInterval = aInterval;
3436 return S_OK;
3437}
3438
3439STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3440{
3441 CheckComArgOutPointerValid(aEnabled);
3442
3443 AutoCaller autoCaller(this);
3444 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3445
3446 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3447
3448 *aEnabled = mUserData->s.fRTCUseUTC;
3449
3450 return S_OK;
3451}
3452
3453STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3454{
3455 AutoCaller autoCaller(this);
3456 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3457
3458 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3459
3460 /* Only allow it to be set to true when PoweredOff or Aborted.
3461 (Clearing it is always permitted.) */
3462 if ( aEnabled
3463 && mData->mRegistered
3464 && ( !isSessionMachine()
3465 || ( mData->mMachineState != MachineState_PoweredOff
3466 && mData->mMachineState != MachineState_Teleported
3467 && mData->mMachineState != MachineState_Aborted
3468 )
3469 )
3470 )
3471 return setError(VBOX_E_INVALID_VM_STATE,
3472 tr("The machine is not powered off (state is %s)"),
3473 Global::stringifyMachineState(mData->mMachineState));
3474
3475 setModified(IsModified_MachineData);
3476 mUserData.backup();
3477 mUserData->s.fRTCUseUTC = !!aEnabled;
3478
3479 return S_OK;
3480}
3481
3482STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3483{
3484 CheckComArgOutPointerValid(aEnabled);
3485
3486 AutoCaller autoCaller(this);
3487 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3488
3489 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3490
3491 *aEnabled = mHWData->mIOCacheEnabled;
3492
3493 return S_OK;
3494}
3495
3496STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3497{
3498 AutoCaller autoCaller(this);
3499 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3500
3501 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3502
3503 HRESULT rc = checkStateDependency(MutableStateDep);
3504 if (FAILED(rc)) return rc;
3505
3506 setModified(IsModified_MachineData);
3507 mHWData.backup();
3508 mHWData->mIOCacheEnabled = aEnabled;
3509
3510 return S_OK;
3511}
3512
3513STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3514{
3515 CheckComArgOutPointerValid(aIOCacheSize);
3516
3517 AutoCaller autoCaller(this);
3518 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3519
3520 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3521
3522 *aIOCacheSize = mHWData->mIOCacheSize;
3523
3524 return S_OK;
3525}
3526
3527STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3528{
3529 AutoCaller autoCaller(this);
3530 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3531
3532 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3533
3534 HRESULT rc = checkStateDependency(MutableStateDep);
3535 if (FAILED(rc)) return rc;
3536
3537 setModified(IsModified_MachineData);
3538 mHWData.backup();
3539 mHWData->mIOCacheSize = aIOCacheSize;
3540
3541 return S_OK;
3542}
3543
3544
3545/**
3546 * @note Locks objects!
3547 */
3548STDMETHODIMP Machine::LockMachine(ISession *aSession,
3549 LockType_T lockType)
3550{
3551 CheckComArgNotNull(aSession);
3552
3553 AutoCaller autoCaller(this);
3554 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3555
3556 /* check the session state */
3557 SessionState_T state;
3558 HRESULT rc = aSession->COMGETTER(State)(&state);
3559 if (FAILED(rc)) return rc;
3560
3561 if (state != SessionState_Unlocked)
3562 return setError(VBOX_E_INVALID_OBJECT_STATE,
3563 tr("The given session is busy"));
3564
3565 // get the client's IInternalSessionControl interface
3566 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3567 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3568 E_INVALIDARG);
3569
3570 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3571
3572 if (!mData->mRegistered)
3573 return setError(E_UNEXPECTED,
3574 tr("The machine '%s' is not registered"),
3575 mUserData->s.strName.c_str());
3576
3577 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3578
3579 SessionState_T oldState = mData->mSession.mState;
3580 /* Hack: in case the session is closing and there is a progress object
3581 * which allows waiting for the session to be closed, take the opportunity
3582 * and do a limited wait (max. 1 second). This helps a lot when the system
3583 * is busy and thus session closing can take a little while. */
3584 if ( mData->mSession.mState == SessionState_Unlocking
3585 && mData->mSession.mProgress)
3586 {
3587 alock.release();
3588 mData->mSession.mProgress->WaitForCompletion(1000);
3589 alock.acquire();
3590 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3591 }
3592
3593 // try again now
3594 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3595 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3596 )
3597 {
3598 // OK, share the session... we are now dealing with three processes:
3599 // 1) VBoxSVC (where this code runs);
3600 // 2) process C: the caller's client process (who wants a shared session);
3601 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3602
3603 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3604 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3605 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3606 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3607 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3608
3609 /*
3610 * Release the lock before calling the client process. It's safe here
3611 * since the only thing to do after we get the lock again is to add
3612 * the remote control to the list (which doesn't directly influence
3613 * anything).
3614 */
3615 alock.release();
3616
3617 // get the console of the session holding the write lock (this is a remote call)
3618 ComPtr<IConsole> pConsoleW;
3619 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3620 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3621 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3622 if (FAILED(rc))
3623 // the failure may occur w/o any error info (from RPC), so provide one
3624 return setError(VBOX_E_VM_ERROR,
3625 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3626
3627 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3628
3629 // share the session machine and W's console with the caller's session
3630 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3631 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3632 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3633
3634 if (FAILED(rc))
3635 // the failure may occur w/o any error info (from RPC), so provide one
3636 return setError(VBOX_E_VM_ERROR,
3637 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3638 alock.acquire();
3639
3640 // need to revalidate the state after acquiring the lock again
3641 if (mData->mSession.mState != SessionState_Locked)
3642 {
3643 pSessionControl->Uninitialize();
3644 return setError(VBOX_E_INVALID_SESSION_STATE,
3645 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3646 mUserData->s.strName.c_str());
3647 }
3648
3649 // add the caller's session to the list
3650 mData->mSession.mRemoteControls.push_back(pSessionControl);
3651 }
3652 else if ( mData->mSession.mState == SessionState_Locked
3653 || mData->mSession.mState == SessionState_Unlocking
3654 )
3655 {
3656 // sharing not permitted, or machine still unlocking:
3657 return setError(VBOX_E_INVALID_OBJECT_STATE,
3658 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3659 mUserData->s.strName.c_str());
3660 }
3661 else
3662 {
3663 // machine is not locked: then write-lock the machine (create the session machine)
3664
3665 // must not be busy
3666 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3667
3668 // get the caller's session PID
3669 RTPROCESS pid = NIL_RTPROCESS;
3670 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3671 pSessionControl->GetPID((ULONG*)&pid);
3672 Assert(pid != NIL_RTPROCESS);
3673
3674 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3675
3676 if (fLaunchingVMProcess)
3677 {
3678 // this machine is awaiting for a spawning session to be opened:
3679 // then the calling process must be the one that got started by
3680 // LaunchVMProcess()
3681
3682 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3683 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3684
3685 if (mData->mSession.mPID != pid)
3686 return setError(E_ACCESSDENIED,
3687 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3688 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3689 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3690 }
3691
3692 // create the mutable SessionMachine from the current machine
3693 ComObjPtr<SessionMachine> sessionMachine;
3694 sessionMachine.createObject();
3695 rc = sessionMachine->init(this);
3696 AssertComRC(rc);
3697
3698 /* NOTE: doing return from this function after this point but
3699 * before the end is forbidden since it may call SessionMachine::uninit()
3700 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3701 * lock while still holding the Machine lock in alock so that a deadlock
3702 * is possible due to the wrong lock order. */
3703
3704 if (SUCCEEDED(rc))
3705 {
3706 /*
3707 * Set the session state to Spawning to protect against subsequent
3708 * attempts to open a session and to unregister the machine after
3709 * we release the lock.
3710 */
3711 SessionState_T origState = mData->mSession.mState;
3712 mData->mSession.mState = SessionState_Spawning;
3713
3714 /*
3715 * Release the lock before calling the client process -- it will call
3716 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3717 * because the state is Spawning, so that LaunchVMProcess() and
3718 * LockMachine() calls will fail. This method, called before we
3719 * acquire the lock again, will fail because of the wrong PID.
3720 *
3721 * Note that mData->mSession.mRemoteControls accessed outside
3722 * the lock may not be modified when state is Spawning, so it's safe.
3723 */
3724 alock.release();
3725
3726 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3727 rc = pSessionControl->AssignMachine(sessionMachine, lockType);
3728 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3729
3730 /* The failure may occur w/o any error info (from RPC), so provide one */
3731 if (FAILED(rc))
3732 setError(VBOX_E_VM_ERROR,
3733 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3734
3735 if ( SUCCEEDED(rc)
3736 && fLaunchingVMProcess
3737 )
3738 {
3739 /* complete the remote session initialization */
3740
3741 /* get the console from the direct session */
3742 ComPtr<IConsole> console;
3743 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3744 ComAssertComRC(rc);
3745
3746 if (SUCCEEDED(rc) && !console)
3747 {
3748 ComAssert(!!console);
3749 rc = E_FAIL;
3750 }
3751
3752 /* assign machine & console to the remote session */
3753 if (SUCCEEDED(rc))
3754 {
3755 /*
3756 * after LaunchVMProcess(), the first and the only
3757 * entry in remoteControls is that remote session
3758 */
3759 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3760 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3761 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3762
3763 /* The failure may occur w/o any error info (from RPC), so provide one */
3764 if (FAILED(rc))
3765 setError(VBOX_E_VM_ERROR,
3766 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3767 }
3768
3769 if (FAILED(rc))
3770 pSessionControl->Uninitialize();
3771 }
3772
3773 /* acquire the lock again */
3774 alock.acquire();
3775
3776 /* Restore the session state */
3777 mData->mSession.mState = origState;
3778 }
3779
3780 // finalize spawning anyway (this is why we don't return on errors above)
3781 if (fLaunchingVMProcess)
3782 {
3783 /* Note that the progress object is finalized later */
3784 /** @todo Consider checking mData->mSession.mProgress for cancellation
3785 * around here. */
3786
3787 /* We don't reset mSession.mPID here because it is necessary for
3788 * SessionMachine::uninit() to reap the child process later. */
3789
3790 if (FAILED(rc))
3791 {
3792 /* Close the remote session, remove the remote control from the list
3793 * and reset session state to Closed (@note keep the code in sync
3794 * with the relevant part in openSession()). */
3795
3796 Assert(mData->mSession.mRemoteControls.size() == 1);
3797 if (mData->mSession.mRemoteControls.size() == 1)
3798 {
3799 ErrorInfoKeeper eik;
3800 mData->mSession.mRemoteControls.front()->Uninitialize();
3801 }
3802
3803 mData->mSession.mRemoteControls.clear();
3804 mData->mSession.mState = SessionState_Unlocked;
3805 }
3806 }
3807 else
3808 {
3809 /* memorize PID of the directly opened session */
3810 if (SUCCEEDED(rc))
3811 mData->mSession.mPID = pid;
3812 }
3813
3814 if (SUCCEEDED(rc))
3815 {
3816 /* memorize the direct session control and cache IUnknown for it */
3817 mData->mSession.mDirectControl = pSessionControl;
3818 mData->mSession.mState = SessionState_Locked;
3819 /* associate the SessionMachine with this Machine */
3820 mData->mSession.mMachine = sessionMachine;
3821
3822 /* request an IUnknown pointer early from the remote party for later
3823 * identity checks (it will be internally cached within mDirectControl
3824 * at least on XPCOM) */
3825 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3826 NOREF(unk);
3827 }
3828
3829 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3830 * would break the lock order */
3831 alock.release();
3832
3833 /* uninitialize the created session machine on failure */
3834 if (FAILED(rc))
3835 sessionMachine->uninit();
3836
3837 }
3838
3839 if (SUCCEEDED(rc))
3840 {
3841 /*
3842 * tell the client watcher thread to update the set of
3843 * machines that have open sessions
3844 */
3845 mParent->updateClientWatcher();
3846
3847 if (oldState != SessionState_Locked)
3848 /* fire an event */
3849 mParent->onSessionStateChange(getId(), SessionState_Locked);
3850 }
3851
3852 return rc;
3853}
3854
3855/**
3856 * @note Locks objects!
3857 */
3858STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3859 IN_BSTR aFrontend,
3860 IN_BSTR aEnvironment,
3861 IProgress **aProgress)
3862{
3863 CheckComArgStr(aFrontend);
3864 Utf8Str strFrontend(aFrontend);
3865 Utf8Str strEnvironment(aEnvironment);
3866 /* "emergencystop" doesn't need the session, so skip the checks/interface
3867 * retrieval. This code doesn't quite fit in here, but introducing a
3868 * special API method would be even more effort, and would require explicit
3869 * support by every API client. It's better to hide the feature a bit. */
3870 if (strFrontend != "emergencystop")
3871 CheckComArgNotNull(aSession);
3872 CheckComArgOutPointerValid(aProgress);
3873
3874 AutoCaller autoCaller(this);
3875 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3876
3877 HRESULT rc = S_OK;
3878 if (strFrontend.isEmpty())
3879 {
3880 Bstr bstrFrontend;
3881 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3882 if (FAILED(rc))
3883 return rc;
3884 strFrontend = bstrFrontend;
3885 if (strFrontend.isEmpty())
3886 {
3887 ComPtr<ISystemProperties> systemProperties;
3888 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3889 if (FAILED(rc))
3890 return rc;
3891 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3892 if (FAILED(rc))
3893 return rc;
3894 strFrontend = bstrFrontend;
3895 }
3896 /* paranoia - emergencystop is not a valid default */
3897 if (strFrontend == "emergencystop")
3898 strFrontend = Utf8Str::Empty;
3899 }
3900 /* default frontend: Qt GUI */
3901 if (strFrontend.isEmpty())
3902 strFrontend = "GUI/Qt";
3903
3904 if (strFrontend != "emergencystop")
3905 {
3906 /* check the session state */
3907 SessionState_T state;
3908 rc = aSession->COMGETTER(State)(&state);
3909 if (FAILED(rc))
3910 return rc;
3911
3912 if (state != SessionState_Unlocked)
3913 return setError(VBOX_E_INVALID_OBJECT_STATE,
3914 tr("The given session is busy"));
3915
3916 /* get the IInternalSessionControl interface */
3917 ComPtr<IInternalSessionControl> control(aSession);
3918 ComAssertMsgRet(!control.isNull(),
3919 ("No IInternalSessionControl interface"),
3920 E_INVALIDARG);
3921
3922 /* get the teleporter enable state for the progress object init. */
3923 BOOL fTeleporterEnabled;
3924 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3925 if (FAILED(rc))
3926 return rc;
3927
3928 /* create a progress object */
3929 ComObjPtr<ProgressProxy> progress;
3930 progress.createObject();
3931 rc = progress->init(mParent,
3932 static_cast<IMachine*>(this),
3933 Bstr(tr("Starting VM")).raw(),
3934 TRUE /* aCancelable */,
3935 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3936 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3937 2 /* uFirstOperationWeight */,
3938 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3939
3940 if (SUCCEEDED(rc))
3941 {
3942 rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
3943 if (SUCCEEDED(rc))
3944 {
3945 progress.queryInterfaceTo(aProgress);
3946
3947 /* signal the client watcher thread */
3948 mParent->updateClientWatcher();
3949
3950 /* fire an event */
3951 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3952 }
3953 }
3954 }
3955 else
3956 {
3957 /* no progress object - either instant success or failure */
3958 *aProgress = NULL;
3959
3960 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3961
3962 if (mData->mSession.mState != SessionState_Locked)
3963 return setError(VBOX_E_INVALID_OBJECT_STATE,
3964 tr("The machine '%s' is not locked by a session"),
3965 mUserData->s.strName.c_str());
3966
3967 /* must have a VM process associated - do not kill normal API clients
3968 * with an open session */
3969 if (!Global::IsOnline(mData->mMachineState))
3970 return setError(VBOX_E_INVALID_OBJECT_STATE,
3971 tr("The machine '%s' does not have a VM process"),
3972 mUserData->s.strName.c_str());
3973
3974 /* forcibly terminate the VM process */
3975 if (mData->mSession.mPID != NIL_RTPROCESS)
3976 RTProcTerminate(mData->mSession.mPID);
3977
3978 /* signal the client watcher thread, as most likely the client has
3979 * been terminated */
3980 mParent->updateClientWatcher();
3981 }
3982
3983 return rc;
3984}
3985
3986STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3987{
3988 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3989 return setError(E_INVALIDARG,
3990 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3991 aPosition, SchemaDefs::MaxBootPosition);
3992
3993 if (aDevice == DeviceType_USB)
3994 return setError(E_NOTIMPL,
3995 tr("Booting from USB device is currently not supported"));
3996
3997 AutoCaller autoCaller(this);
3998 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3999
4000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4001
4002 HRESULT rc = checkStateDependency(MutableStateDep);
4003 if (FAILED(rc)) return rc;
4004
4005 setModified(IsModified_MachineData);
4006 mHWData.backup();
4007 mHWData->mBootOrder[aPosition - 1] = aDevice;
4008
4009 return S_OK;
4010}
4011
4012STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
4013{
4014 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
4015 return setError(E_INVALIDARG,
4016 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
4017 aPosition, SchemaDefs::MaxBootPosition);
4018
4019 AutoCaller autoCaller(this);
4020 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4021
4022 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4023
4024 *aDevice = mHWData->mBootOrder[aPosition - 1];
4025
4026 return S_OK;
4027}
4028
4029STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
4030 LONG aControllerPort,
4031 LONG aDevice,
4032 DeviceType_T aType,
4033 IMedium *aMedium)
4034{
4035 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4036 aControllerName, aControllerPort, aDevice, aType, aMedium));
4037
4038 CheckComArgStrNotEmptyOrNull(aControllerName);
4039
4040 AutoCaller autoCaller(this);
4041 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4042
4043 // request the host lock first, since might be calling Host methods for getting host drives;
4044 // next, protect the media tree all the while we're in here, as well as our member variables
4045 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
4046 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4047
4048 HRESULT rc = checkStateDependency(MutableStateDep);
4049 if (FAILED(rc)) return rc;
4050
4051 /// @todo NEWMEDIA implicit machine registration
4052 if (!mData->mRegistered)
4053 return setError(VBOX_E_INVALID_OBJECT_STATE,
4054 tr("Cannot attach storage devices to an unregistered machine"));
4055
4056 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4057
4058 /* Check for an existing controller. */
4059 ComObjPtr<StorageController> ctl;
4060 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4061 if (FAILED(rc)) return rc;
4062
4063 StorageControllerType_T ctrlType;
4064 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4065 if (FAILED(rc))
4066 return setError(E_FAIL,
4067 tr("Could not get type of controller '%ls'"),
4068 aControllerName);
4069
4070 bool fSilent = false;
4071 Utf8Str strReconfig;
4072
4073 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4074 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4075 if (FAILED(rc))
4076 return rc;
4077 if ( mData->mMachineState == MachineState_Paused
4078 && strReconfig == "1")
4079 fSilent = true;
4080
4081 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4082 bool fHotplug = false;
4083 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4084 fHotplug = true;
4085
4086 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4087 return setError(VBOX_E_INVALID_VM_STATE,
4088 tr("Controller '%ls' does not support hotplugging"),
4089 aControllerName);
4090
4091 // check that the port and device are not out of range
4092 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
4093 if (FAILED(rc)) return rc;
4094
4095 /* check if the device slot is already busy */
4096 MediumAttachment *pAttachTemp;
4097 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
4098 aControllerName,
4099 aControllerPort,
4100 aDevice)))
4101 {
4102 Medium *pMedium = pAttachTemp->getMedium();
4103 if (pMedium)
4104 {
4105 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4106 return setError(VBOX_E_OBJECT_IN_USE,
4107 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4108 pMedium->getLocationFull().c_str(),
4109 aControllerPort,
4110 aDevice,
4111 aControllerName);
4112 }
4113 else
4114 return setError(VBOX_E_OBJECT_IN_USE,
4115 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4116 aControllerPort, aDevice, aControllerName);
4117 }
4118
4119 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
4120 if (aMedium && medium.isNull())
4121 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4122
4123 AutoCaller mediumCaller(medium);
4124 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4125
4126 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
4127
4128 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
4129 && !medium.isNull()
4130 )
4131 return setError(VBOX_E_OBJECT_IN_USE,
4132 tr("Medium '%s' is already attached to this virtual machine"),
4133 medium->getLocationFull().c_str());
4134
4135 if (!medium.isNull())
4136 {
4137 MediumType_T mtype = medium->getType();
4138 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
4139 // For DVDs it's not written to the config file, so needs no global config
4140 // version bump. For floppies it's a new attribute "type", which is ignored
4141 // by older VirtualBox version, so needs no global config version bump either.
4142 // For hard disks this type is not accepted.
4143 if (mtype == MediumType_MultiAttach)
4144 {
4145 // This type is new with VirtualBox 4.0 and therefore requires settings
4146 // version 1.11 in the settings backend. Unfortunately it is not enough to do
4147 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
4148 // two reasons: The medium type is a property of the media registry tree, which
4149 // can reside in the global config file (for pre-4.0 media); we would therefore
4150 // possibly need to bump the global config version. We don't want to do that though
4151 // because that might make downgrading to pre-4.0 impossible.
4152 // As a result, we can only use these two new types if the medium is NOT in the
4153 // global registry:
4154 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
4155 if ( medium->isInRegistry(uuidGlobalRegistry)
4156 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
4157 )
4158 return setError(VBOX_E_INVALID_OBJECT_STATE,
4159 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
4160 "to machines that were created with VirtualBox 4.0 or later"),
4161 medium->getLocationFull().c_str());
4162 }
4163 }
4164
4165 bool fIndirect = false;
4166 if (!medium.isNull())
4167 fIndirect = medium->isReadOnly();
4168 bool associate = true;
4169
4170 do
4171 {
4172 if ( aType == DeviceType_HardDisk
4173 && mMediaData.isBackedUp())
4174 {
4175 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4176
4177 /* check if the medium was attached to the VM before we started
4178 * changing attachments in which case the attachment just needs to
4179 * be restored */
4180 if ((pAttachTemp = findAttachment(oldAtts, medium)))
4181 {
4182 AssertReturn(!fIndirect, E_FAIL);
4183
4184 /* see if it's the same bus/channel/device */
4185 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
4186 {
4187 /* the simplest case: restore the whole attachment
4188 * and return, nothing else to do */
4189 mMediaData->mAttachments.push_back(pAttachTemp);
4190
4191 /* Reattach the medium to the VM. */
4192 if (fHotplug || fSilent)
4193 {
4194 mediumLock.release();
4195 treeLock.release();
4196 alock.release();
4197
4198 MediumLockList *pMediumLockList(new MediumLockList());
4199
4200 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4201 true /* fMediumLockWrite */,
4202 NULL,
4203 *pMediumLockList);
4204 alock.acquire();
4205 if (FAILED(rc))
4206 delete pMediumLockList;
4207 else
4208 {
4209 mData->mSession.mLockedMedia.Unlock();
4210 alock.release();
4211 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4212 mData->mSession.mLockedMedia.Lock();
4213 alock.acquire();
4214 }
4215 alock.release();
4216
4217 if (SUCCEEDED(rc))
4218 {
4219 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4220 /* Remove lock list in case of error. */
4221 if (FAILED(rc))
4222 {
4223 mData->mSession.mLockedMedia.Unlock();
4224 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4225 mData->mSession.mLockedMedia.Lock();
4226 }
4227 }
4228 }
4229
4230 return S_OK;
4231 }
4232
4233 /* bus/channel/device differ; we need a new attachment object,
4234 * but don't try to associate it again */
4235 associate = false;
4236 break;
4237 }
4238 }
4239
4240 /* go further only if the attachment is to be indirect */
4241 if (!fIndirect)
4242 break;
4243
4244 /* perform the so called smart attachment logic for indirect
4245 * attachments. Note that smart attachment is only applicable to base
4246 * hard disks. */
4247
4248 if (medium->getParent().isNull())
4249 {
4250 /* first, investigate the backup copy of the current hard disk
4251 * attachments to make it possible to re-attach existing diffs to
4252 * another device slot w/o losing their contents */
4253 if (mMediaData.isBackedUp())
4254 {
4255 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4256
4257 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4258 uint32_t foundLevel = 0;
4259
4260 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
4261 it != oldAtts.end();
4262 ++it)
4263 {
4264 uint32_t level = 0;
4265 MediumAttachment *pAttach = *it;
4266 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4267 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4268 if (pMedium.isNull())
4269 continue;
4270
4271 if (pMedium->getBase(&level) == medium)
4272 {
4273 /* skip the hard disk if its currently attached (we
4274 * cannot attach the same hard disk twice) */
4275 if (findAttachment(mMediaData->mAttachments,
4276 pMedium))
4277 continue;
4278
4279 /* matched device, channel and bus (i.e. attached to the
4280 * same place) will win and immediately stop the search;
4281 * otherwise the attachment that has the youngest
4282 * descendant of medium will be used
4283 */
4284 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
4285 {
4286 /* the simplest case: restore the whole attachment
4287 * and return, nothing else to do */
4288 mMediaData->mAttachments.push_back(*it);
4289
4290 /* Reattach the medium to the VM. */
4291 if (fHotplug || fSilent)
4292 {
4293 mediumLock.release();
4294 treeLock.release();
4295 alock.release();
4296
4297 MediumLockList *pMediumLockList(new MediumLockList());
4298
4299 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4300 true /* fMediumLockWrite */,
4301 NULL,
4302 *pMediumLockList);
4303 alock.acquire();
4304 if (FAILED(rc))
4305 delete pMediumLockList;
4306 else
4307 {
4308 mData->mSession.mLockedMedia.Unlock();
4309 alock.release();
4310 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4311 mData->mSession.mLockedMedia.Lock();
4312 alock.acquire();
4313 }
4314 alock.release();
4315
4316 if (SUCCEEDED(rc))
4317 {
4318 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4319 /* Remove lock list in case of error. */
4320 if (FAILED(rc))
4321 {
4322 mData->mSession.mLockedMedia.Unlock();
4323 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4324 mData->mSession.mLockedMedia.Lock();
4325 }
4326 }
4327 }
4328
4329 return S_OK;
4330 }
4331 else if ( foundIt == oldAtts.end()
4332 || level > foundLevel /* prefer younger */
4333 )
4334 {
4335 foundIt = it;
4336 foundLevel = level;
4337 }
4338 }
4339 }
4340
4341 if (foundIt != oldAtts.end())
4342 {
4343 /* use the previously attached hard disk */
4344 medium = (*foundIt)->getMedium();
4345 mediumCaller.attach(medium);
4346 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4347 mediumLock.attach(medium);
4348 /* not implicit, doesn't require association with this VM */
4349 fIndirect = false;
4350 associate = false;
4351 /* go right to the MediumAttachment creation */
4352 break;
4353 }
4354 }
4355
4356 /* must give up the medium lock and medium tree lock as below we
4357 * go over snapshots, which needs a lock with higher lock order. */
4358 mediumLock.release();
4359 treeLock.release();
4360
4361 /* then, search through snapshots for the best diff in the given
4362 * hard disk's chain to base the new diff on */
4363
4364 ComObjPtr<Medium> base;
4365 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4366 while (snap)
4367 {
4368 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4369
4370 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
4371
4372 MediumAttachment *pAttachFound = NULL;
4373 uint32_t foundLevel = 0;
4374
4375 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
4376 it != snapAtts.end();
4377 ++it)
4378 {
4379 MediumAttachment *pAttach = *it;
4380 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4381 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4382 if (pMedium.isNull())
4383 continue;
4384
4385 uint32_t level = 0;
4386 if (pMedium->getBase(&level) == medium)
4387 {
4388 /* matched device, channel and bus (i.e. attached to the
4389 * same place) will win and immediately stop the search;
4390 * otherwise the attachment that has the youngest
4391 * descendant of medium will be used
4392 */
4393 if ( pAttach->getDevice() == aDevice
4394 && pAttach->getPort() == aControllerPort
4395 && pAttach->getControllerName() == aControllerName
4396 )
4397 {
4398 pAttachFound = pAttach;
4399 break;
4400 }
4401 else if ( !pAttachFound
4402 || level > foundLevel /* prefer younger */
4403 )
4404 {
4405 pAttachFound = pAttach;
4406 foundLevel = level;
4407 }
4408 }
4409 }
4410
4411 if (pAttachFound)
4412 {
4413 base = pAttachFound->getMedium();
4414 break;
4415 }
4416
4417 snap = snap->getParent();
4418 }
4419
4420 /* re-lock medium tree and the medium, as we need it below */
4421 treeLock.acquire();
4422 mediumLock.acquire();
4423
4424 /* found a suitable diff, use it as a base */
4425 if (!base.isNull())
4426 {
4427 medium = base;
4428 mediumCaller.attach(medium);
4429 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4430 mediumLock.attach(medium);
4431 }
4432 }
4433
4434 Utf8Str strFullSnapshotFolder;
4435 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4436
4437 ComObjPtr<Medium> diff;
4438 diff.createObject();
4439 // store this diff in the same registry as the parent
4440 Guid uuidRegistryParent;
4441 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4442 {
4443 // parent image has no registry: this can happen if we're attaching a new immutable
4444 // image that has not yet been attached (medium then points to the base and we're
4445 // creating the diff image for the immutable, and the parent is not yet registered);
4446 // put the parent in the machine registry then
4447 mediumLock.release();
4448 treeLock.release();
4449 alock.release();
4450 addMediumToRegistry(medium);
4451 alock.acquire();
4452 treeLock.acquire();
4453 mediumLock.acquire();
4454 medium->getFirstRegistryMachineId(uuidRegistryParent);
4455 }
4456 rc = diff->init(mParent,
4457 medium->getPreferredDiffFormat(),
4458 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4459 uuidRegistryParent);
4460 if (FAILED(rc)) return rc;
4461
4462 /* Apply the normal locking logic to the entire chain. */
4463 MediumLockList *pMediumLockList(new MediumLockList());
4464 mediumLock.release();
4465 treeLock.release();
4466 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4467 true /* fMediumLockWrite */,
4468 medium,
4469 *pMediumLockList);
4470 treeLock.acquire();
4471 mediumLock.acquire();
4472 if (SUCCEEDED(rc))
4473 {
4474 mediumLock.release();
4475 treeLock.release();
4476 rc = pMediumLockList->Lock();
4477 treeLock.acquire();
4478 mediumLock.acquire();
4479 if (FAILED(rc))
4480 setError(rc,
4481 tr("Could not lock medium when creating diff '%s'"),
4482 diff->getLocationFull().c_str());
4483 else
4484 {
4485 /* will release the lock before the potentially lengthy
4486 * operation, so protect with the special state */
4487 MachineState_T oldState = mData->mMachineState;
4488 setMachineState(MachineState_SettingUp);
4489
4490 mediumLock.release();
4491 treeLock.release();
4492 alock.release();
4493
4494 rc = medium->createDiffStorage(diff,
4495 MediumVariant_Standard,
4496 pMediumLockList,
4497 NULL /* aProgress */,
4498 true /* aWait */);
4499
4500 alock.acquire();
4501 treeLock.acquire();
4502 mediumLock.acquire();
4503
4504 setMachineState(oldState);
4505 }
4506 }
4507
4508 /* Unlock the media and free the associated memory. */
4509 delete pMediumLockList;
4510
4511 if (FAILED(rc)) return rc;
4512
4513 /* use the created diff for the actual attachment */
4514 medium = diff;
4515 mediumCaller.attach(medium);
4516 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4517 mediumLock.attach(medium);
4518 }
4519 while (0);
4520
4521 ComObjPtr<MediumAttachment> attachment;
4522 attachment.createObject();
4523 rc = attachment->init(this,
4524 medium,
4525 aControllerName,
4526 aControllerPort,
4527 aDevice,
4528 aType,
4529 fIndirect,
4530 false /* fPassthrough */,
4531 false /* fTempEject */,
4532 false /* fNonRotational */,
4533 false /* fDiscard */,
4534 Utf8Str::Empty);
4535 if (FAILED(rc)) return rc;
4536
4537 if (associate && !medium.isNull())
4538 {
4539 // as the last step, associate the medium to the VM
4540 rc = medium->addBackReference(mData->mUuid);
4541 // here we can fail because of Deleting, or being in process of creating a Diff
4542 if (FAILED(rc)) return rc;
4543
4544 mediumLock.release();
4545 treeLock.release();
4546 alock.release();
4547 addMediumToRegistry(medium);
4548 alock.acquire();
4549 treeLock.acquire();
4550 mediumLock.acquire();
4551 }
4552
4553 /* success: finally remember the attachment */
4554 setModified(IsModified_Storage);
4555 mMediaData.backup();
4556 mMediaData->mAttachments.push_back(attachment);
4557
4558 mediumLock.release();
4559 treeLock.release();
4560 alock.release();
4561
4562 if (fHotplug || fSilent)
4563 {
4564 if (!medium.isNull())
4565 {
4566 MediumLockList *pMediumLockList(new MediumLockList());
4567
4568 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4569 true /* fMediumLockWrite */,
4570 NULL,
4571 *pMediumLockList);
4572 alock.acquire();
4573 if (FAILED(rc))
4574 delete pMediumLockList;
4575 else
4576 {
4577 mData->mSession.mLockedMedia.Unlock();
4578 alock.release();
4579 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4580 mData->mSession.mLockedMedia.Lock();
4581 alock.acquire();
4582 }
4583 alock.release();
4584 }
4585
4586 if (SUCCEEDED(rc))
4587 {
4588 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4589 /* Remove lock list in case of error. */
4590 if (FAILED(rc))
4591 {
4592 mData->mSession.mLockedMedia.Unlock();
4593 mData->mSession.mLockedMedia.Remove(attachment);
4594 mData->mSession.mLockedMedia.Lock();
4595 }
4596 }
4597 }
4598
4599 mParent->saveModifiedRegistries();
4600
4601 return rc;
4602}
4603
4604STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4605 LONG aDevice)
4606{
4607 CheckComArgStrNotEmptyOrNull(aControllerName);
4608
4609 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4610 aControllerName, aControllerPort, aDevice));
4611
4612 AutoCaller autoCaller(this);
4613 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4614
4615 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4616
4617 HRESULT rc = checkStateDependency(MutableStateDep);
4618 if (FAILED(rc)) return rc;
4619
4620 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4621
4622 /* Check for an existing controller. */
4623 ComObjPtr<StorageController> ctl;
4624 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4625 if (FAILED(rc)) return rc;
4626
4627 StorageControllerType_T ctrlType;
4628 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4629 if (FAILED(rc))
4630 return setError(E_FAIL,
4631 tr("Could not get type of controller '%ls'"),
4632 aControllerName);
4633
4634 bool fSilent = false;
4635 Utf8Str strReconfig;
4636
4637 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4638 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4639 if (FAILED(rc))
4640 return rc;
4641 if ( mData->mMachineState == MachineState_Paused
4642 && strReconfig == "1")
4643 fSilent = true;
4644
4645 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4646 bool fHotplug = false;
4647 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4648 fHotplug = true;
4649
4650 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4651 return setError(VBOX_E_INVALID_VM_STATE,
4652 tr("Controller '%ls' does not support hotplugging"),
4653 aControllerName);
4654
4655 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4656 aControllerName,
4657 aControllerPort,
4658 aDevice);
4659 if (!pAttach)
4660 return setError(VBOX_E_OBJECT_NOT_FOUND,
4661 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4662 aDevice, aControllerPort, aControllerName);
4663
4664 /*
4665 * The VM has to detach the device before we delete any implicit diffs.
4666 * If this fails we can roll back without loosing data.
4667 */
4668 if (fHotplug || fSilent)
4669 {
4670 alock.release();
4671 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4672 alock.acquire();
4673 }
4674 if (FAILED(rc)) return rc;
4675
4676 /* If we are here everything went well and we can delete the implicit now. */
4677 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4678
4679 alock.release();
4680
4681 mParent->saveModifiedRegistries();
4682
4683 return rc;
4684}
4685
4686STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4687 LONG aDevice, BOOL aPassthrough)
4688{
4689 CheckComArgStrNotEmptyOrNull(aControllerName);
4690
4691 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4692 aControllerName, aControllerPort, aDevice, aPassthrough));
4693
4694 AutoCaller autoCaller(this);
4695 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4696
4697 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4698
4699 HRESULT rc = checkStateDependency(MutableStateDep);
4700 if (FAILED(rc)) return rc;
4701
4702 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4703
4704 if (Global::IsOnlineOrTransient(mData->mMachineState))
4705 return setError(VBOX_E_INVALID_VM_STATE,
4706 tr("Invalid machine state: %s"),
4707 Global::stringifyMachineState(mData->mMachineState));
4708
4709 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4710 aControllerName,
4711 aControllerPort,
4712 aDevice);
4713 if (!pAttach)
4714 return setError(VBOX_E_OBJECT_NOT_FOUND,
4715 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4716 aDevice, aControllerPort, aControllerName);
4717
4718
4719 setModified(IsModified_Storage);
4720 mMediaData.backup();
4721
4722 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4723
4724 if (pAttach->getType() != DeviceType_DVD)
4725 return setError(E_INVALIDARG,
4726 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4727 aDevice, aControllerPort, aControllerName);
4728 pAttach->updatePassthrough(!!aPassthrough);
4729
4730 return S_OK;
4731}
4732
4733STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4734 LONG aDevice, BOOL aTemporaryEject)
4735{
4736 CheckComArgStrNotEmptyOrNull(aControllerName);
4737
4738 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4739 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4740
4741 AutoCaller autoCaller(this);
4742 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4743
4744 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4745
4746 HRESULT rc = checkStateDependency(MutableStateDep);
4747 if (FAILED(rc)) return rc;
4748
4749 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4750 aControllerName,
4751 aControllerPort,
4752 aDevice);
4753 if (!pAttach)
4754 return setError(VBOX_E_OBJECT_NOT_FOUND,
4755 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4756 aDevice, aControllerPort, aControllerName);
4757
4758
4759 setModified(IsModified_Storage);
4760 mMediaData.backup();
4761
4762 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4763
4764 if (pAttach->getType() != DeviceType_DVD)
4765 return setError(E_INVALIDARG,
4766 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4767 aDevice, aControllerPort, aControllerName);
4768 pAttach->updateTempEject(!!aTemporaryEject);
4769
4770 return S_OK;
4771}
4772
4773STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4774 LONG aDevice, BOOL aNonRotational)
4775{
4776 CheckComArgStrNotEmptyOrNull(aControllerName);
4777
4778 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4779 aControllerName, aControllerPort, aDevice, aNonRotational));
4780
4781 AutoCaller autoCaller(this);
4782 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4783
4784 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4785
4786 HRESULT rc = checkStateDependency(MutableStateDep);
4787 if (FAILED(rc)) return rc;
4788
4789 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4790
4791 if (Global::IsOnlineOrTransient(mData->mMachineState))
4792 return setError(VBOX_E_INVALID_VM_STATE,
4793 tr("Invalid machine state: %s"),
4794 Global::stringifyMachineState(mData->mMachineState));
4795
4796 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4797 aControllerName,
4798 aControllerPort,
4799 aDevice);
4800 if (!pAttach)
4801 return setError(VBOX_E_OBJECT_NOT_FOUND,
4802 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4803 aDevice, aControllerPort, aControllerName);
4804
4805
4806 setModified(IsModified_Storage);
4807 mMediaData.backup();
4808
4809 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4810
4811 if (pAttach->getType() != DeviceType_HardDisk)
4812 return setError(E_INVALIDARG,
4813 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"),
4814 aDevice, aControllerPort, aControllerName);
4815 pAttach->updateNonRotational(!!aNonRotational);
4816
4817 return S_OK;
4818}
4819
4820STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4821 LONG aDevice, BOOL aDiscard)
4822{
4823 CheckComArgStrNotEmptyOrNull(aControllerName);
4824
4825 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4826 aControllerName, aControllerPort, aDevice, aDiscard));
4827
4828 AutoCaller autoCaller(this);
4829 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4830
4831 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4832
4833 HRESULT rc = checkStateDependency(MutableStateDep);
4834 if (FAILED(rc)) return rc;
4835
4836 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4837
4838 if (Global::IsOnlineOrTransient(mData->mMachineState))
4839 return setError(VBOX_E_INVALID_VM_STATE,
4840 tr("Invalid machine state: %s"),
4841 Global::stringifyMachineState(mData->mMachineState));
4842
4843 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4844 aControllerName,
4845 aControllerPort,
4846 aDevice);
4847 if (!pAttach)
4848 return setError(VBOX_E_OBJECT_NOT_FOUND,
4849 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4850 aDevice, aControllerPort, aControllerName);
4851
4852
4853 setModified(IsModified_Storage);
4854 mMediaData.backup();
4855
4856 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4857
4858 if (pAttach->getType() != DeviceType_HardDisk)
4859 return setError(E_INVALIDARG,
4860 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"),
4861 aDevice, aControllerPort, aControllerName);
4862 pAttach->updateDiscard(!!aDiscard);
4863
4864 return S_OK;
4865}
4866
4867STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4868 LONG aDevice)
4869{
4870 int rc = S_OK;
4871 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4872 aControllerName, aControllerPort, aDevice));
4873
4874 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4875
4876 return rc;
4877}
4878
4879STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4880 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4881{
4882 CheckComArgStrNotEmptyOrNull(aControllerName);
4883
4884 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4885 aControllerName, aControllerPort, aDevice));
4886
4887 AutoCaller autoCaller(this);
4888 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4889
4890 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4891
4892 HRESULT rc = checkStateDependency(MutableStateDep);
4893 if (FAILED(rc)) return rc;
4894
4895 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4896
4897 if (Global::IsOnlineOrTransient(mData->mMachineState))
4898 return setError(VBOX_E_INVALID_VM_STATE,
4899 tr("Invalid machine state: %s"),
4900 Global::stringifyMachineState(mData->mMachineState));
4901
4902 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4903 aControllerName,
4904 aControllerPort,
4905 aDevice);
4906 if (!pAttach)
4907 return setError(VBOX_E_OBJECT_NOT_FOUND,
4908 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4909 aDevice, aControllerPort, aControllerName);
4910
4911
4912 setModified(IsModified_Storage);
4913 mMediaData.backup();
4914
4915 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4916 if (aBandwidthGroup && group.isNull())
4917 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4918
4919 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4920
4921 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4922 if (strBandwidthGroupOld.isNotEmpty())
4923 {
4924 /* Get the bandwidth group object and release it - this must not fail. */
4925 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4926 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4927 Assert(SUCCEEDED(rc));
4928
4929 pBandwidthGroupOld->release();
4930 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4931 }
4932
4933 if (!group.isNull())
4934 {
4935 group->reference();
4936 pAttach->updateBandwidthGroup(group->getName());
4937 }
4938
4939 return S_OK;
4940}
4941
4942STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4943 LONG aControllerPort,
4944 LONG aDevice,
4945 DeviceType_T aType)
4946{
4947 HRESULT rc = S_OK;
4948
4949 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4950 aControllerName, aControllerPort, aDevice, aType));
4951
4952 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4953
4954 return rc;
4955}
4956
4957
4958
4959STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4960 LONG aControllerPort,
4961 LONG aDevice,
4962 BOOL aForce)
4963{
4964 int rc = S_OK;
4965 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4966 aControllerName, aControllerPort, aForce));
4967
4968 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4969
4970 return rc;
4971}
4972
4973STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4974 LONG aControllerPort,
4975 LONG aDevice,
4976 IMedium *aMedium,
4977 BOOL aForce)
4978{
4979 int rc = S_OK;
4980 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4981 aControllerName, aControllerPort, aDevice, aForce));
4982
4983 CheckComArgStrNotEmptyOrNull(aControllerName);
4984
4985 AutoCaller autoCaller(this);
4986 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4987
4988 // request the host lock first, since might be calling Host methods for getting host drives;
4989 // next, protect the media tree all the while we're in here, as well as our member variables
4990 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4991 this->lockHandle(),
4992 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4993
4994 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4995 aControllerName,
4996 aControllerPort,
4997 aDevice);
4998 if (pAttach.isNull())
4999 return setError(VBOX_E_OBJECT_NOT_FOUND,
5000 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
5001 aDevice, aControllerPort, aControllerName);
5002
5003 /* Remember previously mounted medium. The medium before taking the
5004 * backup is not necessarily the same thing. */
5005 ComObjPtr<Medium> oldmedium;
5006 oldmedium = pAttach->getMedium();
5007
5008 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
5009 if (aMedium && pMedium.isNull())
5010 return setError(E_INVALIDARG, "The given medium pointer is invalid");
5011
5012 AutoCaller mediumCaller(pMedium);
5013 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
5014
5015 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
5016 if (pMedium)
5017 {
5018 DeviceType_T mediumType = pAttach->getType();
5019 switch (mediumType)
5020 {
5021 case DeviceType_DVD:
5022 case DeviceType_Floppy:
5023 break;
5024
5025 default:
5026 return setError(VBOX_E_INVALID_OBJECT_STATE,
5027 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
5028 aControllerPort,
5029 aDevice,
5030 aControllerName);
5031 }
5032 }
5033
5034 setModified(IsModified_Storage);
5035 mMediaData.backup();
5036
5037 {
5038 // The backup operation makes the pAttach reference point to the
5039 // old settings. Re-get the correct reference.
5040 pAttach = findAttachment(mMediaData->mAttachments,
5041 aControllerName,
5042 aControllerPort,
5043 aDevice);
5044 if (!oldmedium.isNull())
5045 oldmedium->removeBackReference(mData->mUuid);
5046 if (!pMedium.isNull())
5047 {
5048 pMedium->addBackReference(mData->mUuid);
5049
5050 mediumLock.release();
5051 multiLock.release();
5052 addMediumToRegistry(pMedium);
5053 multiLock.acquire();
5054 mediumLock.acquire();
5055 }
5056
5057 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5058 pAttach->updateMedium(pMedium);
5059 }
5060
5061 setModified(IsModified_Storage);
5062
5063 mediumLock.release();
5064 multiLock.release();
5065 rc = onMediumChange(pAttach, aForce);
5066 multiLock.acquire();
5067 mediumLock.acquire();
5068
5069 /* On error roll back this change only. */
5070 if (FAILED(rc))
5071 {
5072 if (!pMedium.isNull())
5073 pMedium->removeBackReference(mData->mUuid);
5074 pAttach = findAttachment(mMediaData->mAttachments,
5075 aControllerName,
5076 aControllerPort,
5077 aDevice);
5078 /* If the attachment is gone in the meantime, bail out. */
5079 if (pAttach.isNull())
5080 return rc;
5081 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5082 if (!oldmedium.isNull())
5083 oldmedium->addBackReference(mData->mUuid);
5084 pAttach->updateMedium(oldmedium);
5085 }
5086
5087 mediumLock.release();
5088 multiLock.release();
5089
5090 mParent->saveModifiedRegistries();
5091
5092 return rc;
5093}
5094
5095STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
5096 LONG aControllerPort,
5097 LONG aDevice,
5098 IMedium **aMedium)
5099{
5100 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5101 aControllerName, aControllerPort, aDevice));
5102
5103 CheckComArgStrNotEmptyOrNull(aControllerName);
5104 CheckComArgOutPointerValid(aMedium);
5105
5106 AutoCaller autoCaller(this);
5107 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5108
5109 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5110
5111 *aMedium = NULL;
5112
5113 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5114 aControllerName,
5115 aControllerPort,
5116 aDevice);
5117 if (pAttach.isNull())
5118 return setError(VBOX_E_OBJECT_NOT_FOUND,
5119 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5120 aDevice, aControllerPort, aControllerName);
5121
5122 pAttach->getMedium().queryInterfaceTo(aMedium);
5123
5124 return S_OK;
5125}
5126
5127STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
5128{
5129 CheckComArgOutPointerValid(port);
5130 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
5131
5132 AutoCaller autoCaller(this);
5133 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5134
5135 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5136
5137 mSerialPorts[slot].queryInterfaceTo(port);
5138
5139 return S_OK;
5140}
5141
5142STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
5143{
5144 CheckComArgOutPointerValid(port);
5145 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
5146
5147 AutoCaller autoCaller(this);
5148 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5149
5150 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5151
5152 mParallelPorts[slot].queryInterfaceTo(port);
5153
5154 return S_OK;
5155}
5156
5157STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
5158{
5159 CheckComArgOutPointerValid(adapter);
5160 /* Do not assert if slot is out of range, just return the advertised
5161 status. testdriver/vbox.py triggers this in logVmInfo. */
5162 if (slot >= mNetworkAdapters.size())
5163 return setError(E_INVALIDARG,
5164 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
5165 slot, mNetworkAdapters.size());
5166
5167 AutoCaller autoCaller(this);
5168 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5169
5170 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5171
5172 mNetworkAdapters[slot].queryInterfaceTo(adapter);
5173
5174 return S_OK;
5175}
5176
5177STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
5178{
5179 CheckComArgOutSafeArrayPointerValid(aKeys);
5180
5181 AutoCaller autoCaller(this);
5182 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5183
5184 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5185
5186 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
5187 int i = 0;
5188 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5189 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5190 ++it, ++i)
5191 {
5192 const Utf8Str &strKey = it->first;
5193 strKey.cloneTo(&saKeys[i]);
5194 }
5195 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
5196
5197 return S_OK;
5198 }
5199
5200 /**
5201 * @note Locks this object for reading.
5202 */
5203STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
5204 BSTR *aValue)
5205{
5206 CheckComArgStrNotEmptyOrNull(aKey);
5207 CheckComArgOutPointerValid(aValue);
5208
5209 AutoCaller autoCaller(this);
5210 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5211
5212 /* start with nothing found */
5213 Bstr bstrResult("");
5214
5215 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5216
5217 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
5218 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5219 // found:
5220 bstrResult = it->second; // source is a Utf8Str
5221
5222 /* return the result to caller (may be empty) */
5223 bstrResult.cloneTo(aValue);
5224
5225 return S_OK;
5226}
5227
5228 /**
5229 * @note Locks mParent for writing + this object for writing.
5230 */
5231STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
5232{
5233 CheckComArgStrNotEmptyOrNull(aKey);
5234
5235 AutoCaller autoCaller(this);
5236 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5237
5238 Utf8Str strKey(aKey);
5239 Utf8Str strValue(aValue);
5240 Utf8Str strOldValue; // empty
5241
5242 // locking note: we only hold the read lock briefly to look up the old value,
5243 // then release it and call the onExtraCanChange callbacks. There is a small
5244 // chance of a race insofar as the callback might be called twice if two callers
5245 // change the same key at the same time, but that's a much better solution
5246 // than the deadlock we had here before. The actual changing of the extradata
5247 // is then performed under the write lock and race-free.
5248
5249 // look up the old value first; if nothing has changed then we need not do anything
5250 {
5251 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5252 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
5253 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5254 strOldValue = it->second;
5255 }
5256
5257 bool fChanged;
5258 if ((fChanged = (strOldValue != strValue)))
5259 {
5260 // ask for permission from all listeners outside the locks;
5261 // onExtraDataCanChange() only briefly requests the VirtualBox
5262 // lock to copy the list of callbacks to invoke
5263 Bstr error;
5264 Bstr bstrValue(aValue);
5265
5266 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
5267 {
5268 const char *sep = error.isEmpty() ? "" : ": ";
5269 CBSTR err = error.raw();
5270 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
5271 sep, err));
5272 return setError(E_ACCESSDENIED,
5273 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
5274 aKey,
5275 bstrValue.raw(),
5276 sep,
5277 err);
5278 }
5279
5280 // data is changing and change not vetoed: then write it out under the lock
5281 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5282
5283 if (isSnapshotMachine())
5284 {
5285 HRESULT rc = checkStateDependency(MutableStateDep);
5286 if (FAILED(rc)) return rc;
5287 }
5288
5289 if (strValue.isEmpty())
5290 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
5291 else
5292 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
5293 // creates a new key if needed
5294
5295 bool fNeedsGlobalSaveSettings = false;
5296 saveSettings(&fNeedsGlobalSaveSettings);
5297
5298 if (fNeedsGlobalSaveSettings)
5299 {
5300 // save the global settings; for that we should hold only the VirtualBox lock
5301 alock.release();
5302 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5303 mParent->saveSettings();
5304 }
5305 }
5306
5307 // fire notification outside the lock
5308 if (fChanged)
5309 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
5310
5311 return S_OK;
5312}
5313
5314STDMETHODIMP Machine::SaveSettings()
5315{
5316 AutoCaller autoCaller(this);
5317 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5318
5319 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5320
5321 /* when there was auto-conversion, we want to save the file even if
5322 * the VM is saved */
5323 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
5324 if (FAILED(rc)) return rc;
5325
5326 /* the settings file path may never be null */
5327 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5328
5329 /* save all VM data excluding snapshots */
5330 bool fNeedsGlobalSaveSettings = false;
5331 rc = saveSettings(&fNeedsGlobalSaveSettings);
5332 mlock.release();
5333
5334 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5335 {
5336 // save the global settings; for that we should hold only the VirtualBox lock
5337 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5338 rc = mParent->saveSettings();
5339 }
5340
5341 return rc;
5342}
5343
5344STDMETHODIMP Machine::DiscardSettings()
5345{
5346 AutoCaller autoCaller(this);
5347 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5348
5349 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5350
5351 HRESULT rc = checkStateDependency(MutableStateDep);
5352 if (FAILED(rc)) return rc;
5353
5354 /*
5355 * during this rollback, the session will be notified if data has
5356 * been actually changed
5357 */
5358 rollback(true /* aNotify */);
5359
5360 return S_OK;
5361}
5362
5363/** @note Locks objects! */
5364STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
5365 ComSafeArrayOut(IMedium*, aMedia))
5366{
5367 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5368 AutoLimitedCaller autoCaller(this);
5369 AssertComRCReturnRC(autoCaller.rc());
5370
5371 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5372
5373 Guid id(getId());
5374
5375 if (mData->mSession.mState != SessionState_Unlocked)
5376 return setError(VBOX_E_INVALID_OBJECT_STATE,
5377 tr("Cannot unregister the machine '%s' while it is locked"),
5378 mUserData->s.strName.c_str());
5379
5380 // wait for state dependents to drop to zero
5381 ensureNoStateDependencies();
5382
5383 if (!mData->mAccessible)
5384 {
5385 // inaccessible maschines can only be unregistered; uninitialize ourselves
5386 // here because currently there may be no unregistered that are inaccessible
5387 // (this state combination is not supported). Note releasing the caller and
5388 // leaving the lock before calling uninit()
5389 alock.release();
5390 autoCaller.release();
5391
5392 uninit();
5393
5394 mParent->unregisterMachine(this, id);
5395 // calls VirtualBox::saveSettings()
5396
5397 return S_OK;
5398 }
5399
5400 HRESULT rc = S_OK;
5401
5402 // discard saved state
5403 if (mData->mMachineState == MachineState_Saved)
5404 {
5405 // add the saved state file to the list of files the caller should delete
5406 Assert(!mSSData->strStateFilePath.isEmpty());
5407 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5408
5409 mSSData->strStateFilePath.setNull();
5410
5411 // unconditionally set the machine state to powered off, we now
5412 // know no session has locked the machine
5413 mData->mMachineState = MachineState_PoweredOff;
5414 }
5415
5416 size_t cSnapshots = 0;
5417 if (mData->mFirstSnapshot)
5418 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5419 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
5420 // fail now before we start detaching media
5421 return setError(VBOX_E_INVALID_OBJECT_STATE,
5422 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5423 mUserData->s.strName.c_str(), cSnapshots);
5424
5425 // This list collects the medium objects from all medium attachments
5426 // which we will detach from the machine and its snapshots, in a specific
5427 // order which allows for closing all media without getting "media in use"
5428 // errors, simply by going through the list from the front to the back:
5429 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5430 // and must be closed before the parent media from the snapshots, or closing the parents
5431 // will fail because they still have children);
5432 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5433 // the root ("first") snapshot of the machine.
5434 MediaList llMedia;
5435
5436 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5437 && mMediaData->mAttachments.size()
5438 )
5439 {
5440 // we have media attachments: detach them all and add the Medium objects to our list
5441 if (cleanupMode != CleanupMode_UnregisterOnly)
5442 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5443 else
5444 return setError(VBOX_E_INVALID_OBJECT_STATE,
5445 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5446 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5447 }
5448
5449 if (cSnapshots)
5450 {
5451 // autoCleanup must be true here, or we would have failed above
5452
5453 // add the media from the medium attachments of the snapshots to llMedia
5454 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5455 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5456 // into the children first
5457
5458 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5459 MachineState_T oldState = mData->mMachineState;
5460 mData->mMachineState = MachineState_DeletingSnapshot;
5461
5462 // make a copy of the first snapshot so the refcount does not drop to 0
5463 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5464 // because of the AutoCaller voodoo)
5465 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5466
5467 // GO!
5468 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5469
5470 mData->mMachineState = oldState;
5471 }
5472
5473 if (FAILED(rc))
5474 {
5475 rollbackMedia();
5476 return rc;
5477 }
5478
5479 // commit all the media changes made above
5480 commitMedia();
5481
5482 mData->mRegistered = false;
5483
5484 // machine lock no longer needed
5485 alock.release();
5486
5487 // return media to caller
5488 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5489 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5490
5491 mParent->unregisterMachine(this, id);
5492 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5493
5494 return S_OK;
5495}
5496
5497struct Machine::DeleteTask
5498{
5499 ComObjPtr<Machine> pMachine;
5500 RTCList<ComPtr<IMedium> > llMediums;
5501 StringsList llFilesToDelete;
5502 ComObjPtr<Progress> pProgress;
5503};
5504
5505STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5506{
5507 LogFlowFuncEnter();
5508
5509 AutoCaller autoCaller(this);
5510 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5511
5512 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5513
5514 HRESULT rc = checkStateDependency(MutableStateDep);
5515 if (FAILED(rc)) return rc;
5516
5517 if (mData->mRegistered)
5518 return setError(VBOX_E_INVALID_VM_STATE,
5519 tr("Cannot delete settings of a registered machine"));
5520
5521 DeleteTask *pTask = new DeleteTask;
5522 pTask->pMachine = this;
5523 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5524
5525 // collect files to delete
5526 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5527
5528 for (size_t i = 0; i < sfaMedia.size(); ++i)
5529 {
5530 IMedium *pIMedium(sfaMedia[i]);
5531 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5532 if (pMedium.isNull())
5533 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5534 SafeArray<BSTR> ids;
5535 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5536 if (FAILED(rc)) return rc;
5537 /* At this point the medium should not have any back references
5538 * anymore. If it has it is attached to another VM and *must* not
5539 * deleted. */
5540 if (ids.size() < 1)
5541 pTask->llMediums.append(pMedium);
5542 }
5543 if (mData->pMachineConfigFile->fileExists())
5544 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5545
5546 pTask->pProgress.createObject();
5547 pTask->pProgress->init(getVirtualBox(),
5548 static_cast<IMachine*>(this) /* aInitiator */,
5549 Bstr(tr("Deleting files")).raw(),
5550 true /* fCancellable */,
5551 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5552 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5553
5554 int vrc = RTThreadCreate(NULL,
5555 Machine::deleteThread,
5556 (void*)pTask,
5557 0,
5558 RTTHREADTYPE_MAIN_WORKER,
5559 0,
5560 "MachineDelete");
5561
5562 pTask->pProgress.queryInterfaceTo(aProgress);
5563
5564 if (RT_FAILURE(vrc))
5565 {
5566 delete pTask;
5567 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5568 }
5569
5570 LogFlowFuncLeave();
5571
5572 return S_OK;
5573}
5574
5575/**
5576 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5577 * calls Machine::deleteTaskWorker() on the actual machine object.
5578 * @param Thread
5579 * @param pvUser
5580 * @return
5581 */
5582/*static*/
5583DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5584{
5585 LogFlowFuncEnter();
5586
5587 DeleteTask *pTask = (DeleteTask*)pvUser;
5588 Assert(pTask);
5589 Assert(pTask->pMachine);
5590 Assert(pTask->pProgress);
5591
5592 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5593 pTask->pProgress->notifyComplete(rc);
5594
5595 delete pTask;
5596
5597 LogFlowFuncLeave();
5598
5599 NOREF(Thread);
5600
5601 return VINF_SUCCESS;
5602}
5603
5604/**
5605 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5606 * @param task
5607 * @return
5608 */
5609HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5610{
5611 AutoCaller autoCaller(this);
5612 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5613
5614 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5615
5616 HRESULT rc = S_OK;
5617
5618 try
5619 {
5620 ULONG uLogHistoryCount = 3;
5621 ComPtr<ISystemProperties> systemProperties;
5622 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5623 if (FAILED(rc)) throw rc;
5624
5625 if (!systemProperties.isNull())
5626 {
5627 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5628 if (FAILED(rc)) throw rc;
5629 }
5630
5631 MachineState_T oldState = mData->mMachineState;
5632 setMachineState(MachineState_SettingUp);
5633 alock.release();
5634 for (size_t i = 0; i < task.llMediums.size(); ++i)
5635 {
5636 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5637 {
5638 AutoCaller mac(pMedium);
5639 if (FAILED(mac.rc())) throw mac.rc();
5640 Utf8Str strLocation = pMedium->getLocationFull();
5641 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5642 if (FAILED(rc)) throw rc;
5643 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5644 }
5645 ComPtr<IProgress> pProgress2;
5646 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5647 if (FAILED(rc)) throw rc;
5648 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5649 if (FAILED(rc)) throw rc;
5650 /* Check the result of the asynchrony process. */
5651 LONG iRc;
5652 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5653 if (FAILED(rc)) throw rc;
5654 /* If the thread of the progress object has an error, then
5655 * retrieve the error info from there, or it'll be lost. */
5656 if (FAILED(iRc))
5657 throw setError(ProgressErrorInfo(pProgress2));
5658 }
5659 setMachineState(oldState);
5660 alock.acquire();
5661
5662 // delete the files pushed on the task list by Machine::Delete()
5663 // (this includes saved states of the machine and snapshots and
5664 // medium storage files from the IMedium list passed in, and the
5665 // machine XML file)
5666 StringsList::const_iterator it = task.llFilesToDelete.begin();
5667 while (it != task.llFilesToDelete.end())
5668 {
5669 const Utf8Str &strFile = *it;
5670 LogFunc(("Deleting file %s\n", strFile.c_str()));
5671 int vrc = RTFileDelete(strFile.c_str());
5672 if (RT_FAILURE(vrc))
5673 throw setError(VBOX_E_IPRT_ERROR,
5674 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5675
5676 ++it;
5677 if (it == task.llFilesToDelete.end())
5678 {
5679 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5680 if (FAILED(rc)) throw rc;
5681 break;
5682 }
5683
5684 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5685 if (FAILED(rc)) throw rc;
5686 }
5687
5688 /* delete the settings only when the file actually exists */
5689 if (mData->pMachineConfigFile->fileExists())
5690 {
5691 /* Delete any backup or uncommitted XML files. Ignore failures.
5692 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5693 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5694 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5695 RTFileDelete(otherXml.c_str());
5696 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5697 RTFileDelete(otherXml.c_str());
5698
5699 /* delete the Logs folder, nothing important should be left
5700 * there (we don't check for errors because the user might have
5701 * some private files there that we don't want to delete) */
5702 Utf8Str logFolder;
5703 getLogFolder(logFolder);
5704 Assert(logFolder.length());
5705 if (RTDirExists(logFolder.c_str()))
5706 {
5707 /* Delete all VBox.log[.N] files from the Logs folder
5708 * (this must be in sync with the rotation logic in
5709 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5710 * files that may have been created by the GUI. */
5711 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5712 logFolder.c_str(), RTPATH_DELIMITER);
5713 RTFileDelete(log.c_str());
5714 log = Utf8StrFmt("%s%cVBox.png",
5715 logFolder.c_str(), RTPATH_DELIMITER);
5716 RTFileDelete(log.c_str());
5717 for (int i = uLogHistoryCount; i > 0; i--)
5718 {
5719 log = Utf8StrFmt("%s%cVBox.log.%d",
5720 logFolder.c_str(), RTPATH_DELIMITER, i);
5721 RTFileDelete(log.c_str());
5722 log = Utf8StrFmt("%s%cVBox.png.%d",
5723 logFolder.c_str(), RTPATH_DELIMITER, i);
5724 RTFileDelete(log.c_str());
5725 }
5726
5727 RTDirRemove(logFolder.c_str());
5728 }
5729
5730 /* delete the Snapshots folder, nothing important should be left
5731 * there (we don't check for errors because the user might have
5732 * some private files there that we don't want to delete) */
5733 Utf8Str strFullSnapshotFolder;
5734 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5735 Assert(!strFullSnapshotFolder.isEmpty());
5736 if (RTDirExists(strFullSnapshotFolder.c_str()))
5737 RTDirRemove(strFullSnapshotFolder.c_str());
5738
5739 // delete the directory that contains the settings file, but only
5740 // if it matches the VM name
5741 Utf8Str settingsDir;
5742 if (isInOwnDir(&settingsDir))
5743 RTDirRemove(settingsDir.c_str());
5744 }
5745
5746 alock.release();
5747
5748 mParent->saveModifiedRegistries();
5749 }
5750 catch (HRESULT aRC) { rc = aRC; }
5751
5752 return rc;
5753}
5754
5755STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5756{
5757 CheckComArgOutPointerValid(aSnapshot);
5758
5759 AutoCaller autoCaller(this);
5760 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5761
5762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5763
5764 ComObjPtr<Snapshot> pSnapshot;
5765 HRESULT rc;
5766
5767 if (!aNameOrId || !*aNameOrId)
5768 // null case (caller wants root snapshot): findSnapshotById() handles this
5769 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5770 else
5771 {
5772 Guid uuid(aNameOrId);
5773 if (uuid.isValid())
5774 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5775 else
5776 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5777 }
5778 pSnapshot.queryInterfaceTo(aSnapshot);
5779
5780 return rc;
5781}
5782
5783STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5784{
5785 CheckComArgStrNotEmptyOrNull(aName);
5786 CheckComArgStrNotEmptyOrNull(aHostPath);
5787
5788 AutoCaller autoCaller(this);
5789 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5790
5791 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5792
5793 HRESULT rc = checkStateDependency(MutableStateDep);
5794 if (FAILED(rc)) return rc;
5795
5796 Utf8Str strName(aName);
5797
5798 ComObjPtr<SharedFolder> sharedFolder;
5799 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5800 if (SUCCEEDED(rc))
5801 return setError(VBOX_E_OBJECT_IN_USE,
5802 tr("Shared folder named '%s' already exists"),
5803 strName.c_str());
5804
5805 sharedFolder.createObject();
5806 rc = sharedFolder->init(getMachine(),
5807 strName,
5808 aHostPath,
5809 !!aWritable,
5810 !!aAutoMount,
5811 true /* fFailOnError */);
5812 if (FAILED(rc)) return rc;
5813
5814 setModified(IsModified_SharedFolders);
5815 mHWData.backup();
5816 mHWData->mSharedFolders.push_back(sharedFolder);
5817
5818 /* inform the direct session if any */
5819 alock.release();
5820 onSharedFolderChange();
5821
5822 return S_OK;
5823}
5824
5825STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5826{
5827 CheckComArgStrNotEmptyOrNull(aName);
5828
5829 AutoCaller autoCaller(this);
5830 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5831
5832 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5833
5834 HRESULT rc = checkStateDependency(MutableStateDep);
5835 if (FAILED(rc)) return rc;
5836
5837 ComObjPtr<SharedFolder> sharedFolder;
5838 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5839 if (FAILED(rc)) return rc;
5840
5841 setModified(IsModified_SharedFolders);
5842 mHWData.backup();
5843 mHWData->mSharedFolders.remove(sharedFolder);
5844
5845 /* inform the direct session if any */
5846 alock.release();
5847 onSharedFolderChange();
5848
5849 return S_OK;
5850}
5851
5852STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5853{
5854 CheckComArgOutPointerValid(aCanShow);
5855
5856 /* start with No */
5857 *aCanShow = FALSE;
5858
5859 AutoCaller autoCaller(this);
5860 AssertComRCReturnRC(autoCaller.rc());
5861
5862 ComPtr<IInternalSessionControl> directControl;
5863 {
5864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5865
5866 if (mData->mSession.mState != SessionState_Locked)
5867 return setError(VBOX_E_INVALID_VM_STATE,
5868 tr("Machine is not locked for session (session state: %s)"),
5869 Global::stringifySessionState(mData->mSession.mState));
5870
5871 directControl = mData->mSession.mDirectControl;
5872 }
5873
5874 /* ignore calls made after #OnSessionEnd() is called */
5875 if (!directControl)
5876 return S_OK;
5877
5878 LONG64 dummy;
5879 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5880}
5881
5882STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5883{
5884 CheckComArgOutPointerValid(aWinId);
5885
5886 AutoCaller autoCaller(this);
5887 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5888
5889 ComPtr<IInternalSessionControl> directControl;
5890 {
5891 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5892
5893 if (mData->mSession.mState != SessionState_Locked)
5894 return setError(E_FAIL,
5895 tr("Machine is not locked for session (session state: %s)"),
5896 Global::stringifySessionState(mData->mSession.mState));
5897
5898 directControl = mData->mSession.mDirectControl;
5899 }
5900
5901 /* ignore calls made after #OnSessionEnd() is called */
5902 if (!directControl)
5903 return S_OK;
5904
5905 BOOL dummy;
5906 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5907}
5908
5909#ifdef VBOX_WITH_GUEST_PROPS
5910/**
5911 * Look up a guest property in VBoxSVC's internal structures.
5912 */
5913HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5914 BSTR *aValue,
5915 LONG64 *aTimestamp,
5916 BSTR *aFlags) const
5917{
5918 using namespace guestProp;
5919
5920 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5921 Utf8Str strName(aName);
5922 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(strName);
5923
5924 if (it != mHWData->mGuestProperties.end())
5925 {
5926 char szFlags[MAX_FLAGS_LEN + 1];
5927 it->second.strValue.cloneTo(aValue);
5928 *aTimestamp = it->second.mTimestamp;
5929 writeFlags(it->second.mFlags, szFlags);
5930 Bstr(szFlags).cloneTo(aFlags);
5931 }
5932
5933 return S_OK;
5934}
5935
5936/**
5937 * Query the VM that a guest property belongs to for the property.
5938 * @returns E_ACCESSDENIED if the VM process is not available or not
5939 * currently handling queries and the lookup should then be done in
5940 * VBoxSVC.
5941 */
5942HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5943 BSTR *aValue,
5944 LONG64 *aTimestamp,
5945 BSTR *aFlags) const
5946{
5947 HRESULT rc;
5948 ComPtr<IInternalSessionControl> directControl;
5949 directControl = mData->mSession.mDirectControl;
5950
5951 /* fail if we were called after #OnSessionEnd() is called. This is a
5952 * silly race condition. */
5953
5954 if (!directControl)
5955 rc = E_ACCESSDENIED;
5956 else
5957 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5958 false /* isSetter */,
5959 aValue, aTimestamp, aFlags);
5960 return rc;
5961}
5962#endif // VBOX_WITH_GUEST_PROPS
5963
5964STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5965 BSTR *aValue,
5966 LONG64 *aTimestamp,
5967 BSTR *aFlags)
5968{
5969#ifndef VBOX_WITH_GUEST_PROPS
5970 ReturnComNotImplemented();
5971#else // VBOX_WITH_GUEST_PROPS
5972 CheckComArgStrNotEmptyOrNull(aName);
5973 CheckComArgOutPointerValid(aValue);
5974 CheckComArgOutPointerValid(aTimestamp);
5975 CheckComArgOutPointerValid(aFlags);
5976
5977 AutoCaller autoCaller(this);
5978 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5979
5980 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5981 if (rc == E_ACCESSDENIED)
5982 /* The VM is not running or the service is not (yet) accessible */
5983 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5984 return rc;
5985#endif // VBOX_WITH_GUEST_PROPS
5986}
5987
5988STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5989{
5990 LONG64 dummyTimestamp;
5991 Bstr dummyFlags;
5992 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5993}
5994
5995STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5996{
5997 Bstr dummyValue;
5998 Bstr dummyFlags;
5999 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
6000}
6001
6002#ifdef VBOX_WITH_GUEST_PROPS
6003/**
6004 * Set a guest property in VBoxSVC's internal structures.
6005 */
6006HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
6007 IN_BSTR aFlags)
6008{
6009 using namespace guestProp;
6010
6011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6012 HRESULT rc = S_OK;
6013
6014 rc = checkStateDependency(MutableStateDep);
6015 if (FAILED(rc)) return rc;
6016
6017 try
6018 {
6019 Utf8Str utf8Name(aName);
6020 Utf8Str utf8Flags(aFlags);
6021 uint32_t fFlags = NILFLAG;
6022 if ( aFlags != NULL
6023 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags)))
6024 return setError(E_INVALIDARG,
6025 tr("Invalid guest property flag values: '%ls'"),
6026 aFlags);
6027
6028 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
6029 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
6030 if (it == mHWData->mGuestProperties.end())
6031 {
6032 if (!fDelete)
6033 {
6034 setModified(IsModified_MachineData);
6035 mHWData.backupEx();
6036
6037 RTTIMESPEC time;
6038 HWData::GuestProperty prop;
6039 prop.strValue = aValue;
6040 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6041 prop.mFlags = fFlags;
6042 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
6043 }
6044 }
6045 else
6046 {
6047 if (it->second.mFlags & (RDONLYHOST))
6048 {
6049 rc = setError(E_ACCESSDENIED,
6050 tr("The property '%ls' cannot be changed by the host"),
6051 aName);
6052 }
6053 else
6054 {
6055 setModified(IsModified_MachineData);
6056 mHWData.backupEx();
6057
6058 /* The backupEx() operation invalidates our iterator,
6059 * so get a new one. */
6060 it = mHWData->mGuestProperties.find(utf8Name);
6061 Assert(it != mHWData->mGuestProperties.end());
6062
6063 if (!fDelete)
6064 {
6065 RTTIMESPEC time;
6066 it->second.strValue = aValue;
6067 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6068 it->second.mFlags = fFlags;
6069 }
6070 else
6071 mHWData->mGuestProperties.erase(it);
6072 }
6073 }
6074
6075 if ( SUCCEEDED(rc)
6076 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
6077 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
6078 RTSTR_MAX,
6079 utf8Name.c_str(),
6080 RTSTR_MAX,
6081 NULL)
6082 )
6083 )
6084 {
6085 alock.release();
6086
6087 mParent->onGuestPropertyChange(mData->mUuid, aName,
6088 aValue ? aValue : Bstr("").raw(),
6089 aFlags ? aFlags : Bstr("").raw());
6090 }
6091 }
6092 catch (std::bad_alloc &)
6093 {
6094 rc = E_OUTOFMEMORY;
6095 }
6096
6097 return rc;
6098}
6099
6100/**
6101 * Set a property on the VM that that property belongs to.
6102 * @returns E_ACCESSDENIED if the VM process is not available or not
6103 * currently handling queries and the setting should then be done in
6104 * VBoxSVC.
6105 */
6106HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
6107 IN_BSTR aFlags)
6108{
6109 HRESULT rc;
6110
6111 try
6112 {
6113 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
6114
6115 BSTR dummy = NULL; /* will not be changed (setter) */
6116 LONG64 dummy64;
6117 if (!directControl)
6118 rc = E_ACCESSDENIED;
6119 else
6120 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
6121 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
6122 true /* isSetter */,
6123 &dummy, &dummy64, &dummy);
6124 }
6125 catch (std::bad_alloc &)
6126 {
6127 rc = E_OUTOFMEMORY;
6128 }
6129
6130 return rc;
6131}
6132#endif // VBOX_WITH_GUEST_PROPS
6133
6134STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
6135 IN_BSTR aFlags)
6136{
6137#ifndef VBOX_WITH_GUEST_PROPS
6138 ReturnComNotImplemented();
6139#else // VBOX_WITH_GUEST_PROPS
6140 CheckComArgStrNotEmptyOrNull(aName);
6141 CheckComArgMaybeNull(aFlags);
6142 CheckComArgMaybeNull(aValue);
6143
6144 AutoCaller autoCaller(this);
6145 if (FAILED(autoCaller.rc()))
6146 return autoCaller.rc();
6147
6148 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
6149 if (rc == E_ACCESSDENIED)
6150 /* The VM is not running or the service is not (yet) accessible */
6151 rc = setGuestPropertyToService(aName, aValue, aFlags);
6152 return rc;
6153#endif // VBOX_WITH_GUEST_PROPS
6154}
6155
6156STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
6157{
6158 return SetGuestProperty(aName, aValue, NULL);
6159}
6160
6161STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
6162{
6163 return SetGuestProperty(aName, NULL, NULL);
6164}
6165
6166#ifdef VBOX_WITH_GUEST_PROPS
6167/**
6168 * Enumerate the guest properties in VBoxSVC's internal structures.
6169 */
6170HRESULT Machine::enumerateGuestPropertiesInService
6171 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6172 ComSafeArrayOut(BSTR, aValues),
6173 ComSafeArrayOut(LONG64, aTimestamps),
6174 ComSafeArrayOut(BSTR, aFlags))
6175{
6176 using namespace guestProp;
6177
6178 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6179 Utf8Str strPatterns(aPatterns);
6180
6181 HWData::GuestPropertyMap propMap;
6182
6183 /*
6184 * Look for matching patterns and build up a list.
6185 */
6186 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
6187 while (it != mHWData->mGuestProperties.end())
6188 {
6189 if ( strPatterns.isEmpty()
6190 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6191 RTSTR_MAX,
6192 it->first.c_str(),
6193 RTSTR_MAX,
6194 NULL)
6195 )
6196 {
6197 propMap.insert(*it);
6198 }
6199
6200 it++;
6201 }
6202
6203 alock.release();
6204
6205 /*
6206 * And build up the arrays for returning the property information.
6207 */
6208 size_t cEntries = propMap.size();
6209 SafeArray<BSTR> names(cEntries);
6210 SafeArray<BSTR> values(cEntries);
6211 SafeArray<LONG64> timestamps(cEntries);
6212 SafeArray<BSTR> flags(cEntries);
6213 size_t iProp = 0;
6214
6215 it = propMap.begin();
6216 while (it != propMap.end())
6217 {
6218 char szFlags[MAX_FLAGS_LEN + 1];
6219 it->first.cloneTo(&names[iProp]);
6220 it->second.strValue.cloneTo(&values[iProp]);
6221 timestamps[iProp] = it->second.mTimestamp;
6222 writeFlags(it->second.mFlags, szFlags);
6223 Bstr(szFlags).cloneTo(&flags[iProp++]);
6224 it++;
6225 }
6226 names.detachTo(ComSafeArrayOutArg(aNames));
6227 values.detachTo(ComSafeArrayOutArg(aValues));
6228 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
6229 flags.detachTo(ComSafeArrayOutArg(aFlags));
6230 return S_OK;
6231}
6232
6233/**
6234 * Enumerate the properties managed by a VM.
6235 * @returns E_ACCESSDENIED if the VM process is not available or not
6236 * currently handling queries and the setting should then be done in
6237 * VBoxSVC.
6238 */
6239HRESULT Machine::enumerateGuestPropertiesOnVM
6240 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6241 ComSafeArrayOut(BSTR, aValues),
6242 ComSafeArrayOut(LONG64, aTimestamps),
6243 ComSafeArrayOut(BSTR, aFlags))
6244{
6245 HRESULT rc;
6246 ComPtr<IInternalSessionControl> directControl;
6247 directControl = mData->mSession.mDirectControl;
6248
6249 if (!directControl)
6250 rc = E_ACCESSDENIED;
6251 else
6252 rc = directControl->EnumerateGuestProperties
6253 (aPatterns, ComSafeArrayOutArg(aNames),
6254 ComSafeArrayOutArg(aValues),
6255 ComSafeArrayOutArg(aTimestamps),
6256 ComSafeArrayOutArg(aFlags));
6257 return rc;
6258}
6259#endif // VBOX_WITH_GUEST_PROPS
6260
6261STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
6262 ComSafeArrayOut(BSTR, aNames),
6263 ComSafeArrayOut(BSTR, aValues),
6264 ComSafeArrayOut(LONG64, aTimestamps),
6265 ComSafeArrayOut(BSTR, aFlags))
6266{
6267#ifndef VBOX_WITH_GUEST_PROPS
6268 ReturnComNotImplemented();
6269#else // VBOX_WITH_GUEST_PROPS
6270 CheckComArgMaybeNull(aPatterns);
6271 CheckComArgOutSafeArrayPointerValid(aNames);
6272 CheckComArgOutSafeArrayPointerValid(aValues);
6273 CheckComArgOutSafeArrayPointerValid(aTimestamps);
6274 CheckComArgOutSafeArrayPointerValid(aFlags);
6275
6276 AutoCaller autoCaller(this);
6277 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6278
6279 HRESULT rc = enumerateGuestPropertiesOnVM
6280 (aPatterns, ComSafeArrayOutArg(aNames),
6281 ComSafeArrayOutArg(aValues),
6282 ComSafeArrayOutArg(aTimestamps),
6283 ComSafeArrayOutArg(aFlags));
6284 if (rc == E_ACCESSDENIED)
6285 /* The VM is not running or the service is not (yet) accessible */
6286 rc = enumerateGuestPropertiesInService
6287 (aPatterns, ComSafeArrayOutArg(aNames),
6288 ComSafeArrayOutArg(aValues),
6289 ComSafeArrayOutArg(aTimestamps),
6290 ComSafeArrayOutArg(aFlags));
6291 return rc;
6292#endif // VBOX_WITH_GUEST_PROPS
6293}
6294
6295STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
6296 ComSafeArrayOut(IMediumAttachment*, aAttachments))
6297{
6298 MediaData::AttachmentList atts;
6299
6300 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
6301 if (FAILED(rc)) return rc;
6302
6303 SafeIfaceArray<IMediumAttachment> attachments(atts);
6304 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
6305
6306 return S_OK;
6307}
6308
6309STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
6310 LONG aControllerPort,
6311 LONG aDevice,
6312 IMediumAttachment **aAttachment)
6313{
6314 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
6315 aControllerName, aControllerPort, aDevice));
6316
6317 CheckComArgStrNotEmptyOrNull(aControllerName);
6318 CheckComArgOutPointerValid(aAttachment);
6319
6320 AutoCaller autoCaller(this);
6321 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6322
6323 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6324
6325 *aAttachment = NULL;
6326
6327 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
6328 aControllerName,
6329 aControllerPort,
6330 aDevice);
6331 if (pAttach.isNull())
6332 return setError(VBOX_E_OBJECT_NOT_FOUND,
6333 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
6334 aDevice, aControllerPort, aControllerName);
6335
6336 pAttach.queryInterfaceTo(aAttachment);
6337
6338 return S_OK;
6339}
6340
6341STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
6342 StorageBus_T aConnectionType,
6343 IStorageController **controller)
6344{
6345 CheckComArgStrNotEmptyOrNull(aName);
6346
6347 if ( (aConnectionType <= StorageBus_Null)
6348 || (aConnectionType > StorageBus_SAS))
6349 return setError(E_INVALIDARG,
6350 tr("Invalid connection type: %d"),
6351 aConnectionType);
6352
6353 AutoCaller autoCaller(this);
6354 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6355
6356 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6357
6358 HRESULT rc = checkStateDependency(MutableStateDep);
6359 if (FAILED(rc)) return rc;
6360
6361 /* try to find one with the name first. */
6362 ComObjPtr<StorageController> ctrl;
6363
6364 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
6365 if (SUCCEEDED(rc))
6366 return setError(VBOX_E_OBJECT_IN_USE,
6367 tr("Storage controller named '%ls' already exists"),
6368 aName);
6369
6370 ctrl.createObject();
6371
6372 /* get a new instance number for the storage controller */
6373 ULONG ulInstance = 0;
6374 bool fBootable = true;
6375 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6376 it != mStorageControllers->end();
6377 ++it)
6378 {
6379 if ((*it)->getStorageBus() == aConnectionType)
6380 {
6381 ULONG ulCurInst = (*it)->getInstance();
6382
6383 if (ulCurInst >= ulInstance)
6384 ulInstance = ulCurInst + 1;
6385
6386 /* Only one controller of each type can be marked as bootable. */
6387 if ((*it)->getBootable())
6388 fBootable = false;
6389 }
6390 }
6391
6392 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6393 if (FAILED(rc)) return rc;
6394
6395 setModified(IsModified_Storage);
6396 mStorageControllers.backup();
6397 mStorageControllers->push_back(ctrl);
6398
6399 ctrl.queryInterfaceTo(controller);
6400
6401 /* inform the direct session if any */
6402 alock.release();
6403 onStorageControllerChange();
6404
6405 return S_OK;
6406}
6407
6408STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6409 IStorageController **aStorageController)
6410{
6411 CheckComArgStrNotEmptyOrNull(aName);
6412
6413 AutoCaller autoCaller(this);
6414 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6415
6416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6417
6418 ComObjPtr<StorageController> ctrl;
6419
6420 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6421 if (SUCCEEDED(rc))
6422 ctrl.queryInterfaceTo(aStorageController);
6423
6424 return rc;
6425}
6426
6427STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6428 IStorageController **aStorageController)
6429{
6430 AutoCaller autoCaller(this);
6431 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6432
6433 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6434
6435 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6436 it != mStorageControllers->end();
6437 ++it)
6438 {
6439 if ((*it)->getInstance() == aInstance)
6440 {
6441 (*it).queryInterfaceTo(aStorageController);
6442 return S_OK;
6443 }
6444 }
6445
6446 return setError(VBOX_E_OBJECT_NOT_FOUND,
6447 tr("Could not find a storage controller with instance number '%lu'"),
6448 aInstance);
6449}
6450
6451STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6452{
6453 AutoCaller autoCaller(this);
6454 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6455
6456 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6457
6458 HRESULT rc = checkStateDependency(MutableStateDep);
6459 if (FAILED(rc)) return rc;
6460
6461 ComObjPtr<StorageController> ctrl;
6462
6463 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6464 if (SUCCEEDED(rc))
6465 {
6466 /* Ensure that only one controller of each type is marked as bootable. */
6467 if (fBootable == TRUE)
6468 {
6469 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6470 it != mStorageControllers->end();
6471 ++it)
6472 {
6473 ComObjPtr<StorageController> aCtrl = (*it);
6474
6475 if ( (aCtrl->getName() != Utf8Str(aName))
6476 && aCtrl->getBootable() == TRUE
6477 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6478 && aCtrl->getControllerType() == ctrl->getControllerType())
6479 {
6480 aCtrl->setBootable(FALSE);
6481 break;
6482 }
6483 }
6484 }
6485
6486 if (SUCCEEDED(rc))
6487 {
6488 ctrl->setBootable(fBootable);
6489 setModified(IsModified_Storage);
6490 }
6491 }
6492
6493 if (SUCCEEDED(rc))
6494 {
6495 /* inform the direct session if any */
6496 alock.release();
6497 onStorageControllerChange();
6498 }
6499
6500 return rc;
6501}
6502
6503STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6504{
6505 CheckComArgStrNotEmptyOrNull(aName);
6506
6507 AutoCaller autoCaller(this);
6508 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6509
6510 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6511
6512 HRESULT rc = checkStateDependency(MutableStateDep);
6513 if (FAILED(rc)) return rc;
6514
6515 ComObjPtr<StorageController> ctrl;
6516 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6517 if (FAILED(rc)) return rc;
6518
6519 {
6520 /* find all attached devices to the appropriate storage controller and detach them all */
6521 // make a temporary list because detachDevice invalidates iterators into
6522 // mMediaData->mAttachments
6523 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6524
6525 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6526 it != llAttachments2.end();
6527 ++it)
6528 {
6529 MediumAttachment *pAttachTemp = *it;
6530
6531 AutoCaller localAutoCaller(pAttachTemp);
6532 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6533
6534 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6535
6536 if (pAttachTemp->getControllerName() == aName)
6537 {
6538 rc = detachDevice(pAttachTemp, alock, NULL);
6539 if (FAILED(rc)) return rc;
6540 }
6541 }
6542 }
6543
6544 /* We can remove it now. */
6545 setModified(IsModified_Storage);
6546 mStorageControllers.backup();
6547
6548 ctrl->unshare();
6549
6550 mStorageControllers->remove(ctrl);
6551
6552 /* inform the direct session if any */
6553 alock.release();
6554 onStorageControllerChange();
6555
6556 return S_OK;
6557}
6558
6559STDMETHODIMP Machine::AddUSBController(IN_BSTR aName, USBControllerType_T aType,
6560 IUSBController **controller)
6561{
6562 if ( (aType <= USBControllerType_Null)
6563 || (aType >= USBControllerType_Last))
6564 return setError(E_INVALIDARG,
6565 tr("Invalid USB controller type: %d"),
6566 aType);
6567
6568 AutoCaller autoCaller(this);
6569 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6570
6571 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6572
6573 HRESULT rc = checkStateDependency(MutableStateDep);
6574 if (FAILED(rc)) return rc;
6575
6576 /* try to find one with the same type first. */
6577 ComObjPtr<USBController> ctrl;
6578
6579 rc = getUSBControllerByName(aName, ctrl, false /* aSetError */);
6580 if (SUCCEEDED(rc))
6581 return setError(VBOX_E_OBJECT_IN_USE,
6582 tr("USB controller named '%ls' already exists"),
6583 aName);
6584
6585 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6586 ULONG maxInstances;
6587 rc = mParent->getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6588 if (FAILED(rc))
6589 return rc;
6590
6591 ULONG cInstances = getUSBControllerCountByType(aType);
6592 if (cInstances >= maxInstances)
6593 return setError(E_INVALIDARG,
6594 tr("Too many USB controllers of this type"));
6595
6596 ctrl.createObject();
6597
6598 rc = ctrl->init(this, aName, aType);
6599 if (FAILED(rc)) return rc;
6600
6601 setModified(IsModified_USB);
6602 mUSBControllers.backup();
6603 mUSBControllers->push_back(ctrl);
6604
6605 ctrl.queryInterfaceTo(controller);
6606
6607 /* inform the direct session if any */
6608 alock.release();
6609 onUSBControllerChange();
6610
6611 return S_OK;
6612}
6613
6614STDMETHODIMP Machine::GetUSBControllerByName(IN_BSTR aName, IUSBController **aUSBController)
6615{
6616 CheckComArgStrNotEmptyOrNull(aName);
6617
6618 AutoCaller autoCaller(this);
6619 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6620
6621 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6622
6623 ComObjPtr<USBController> ctrl;
6624
6625 HRESULT rc = getUSBControllerByName(aName, ctrl, true /* aSetError */);
6626 if (SUCCEEDED(rc))
6627 ctrl.queryInterfaceTo(aUSBController);
6628
6629 return rc;
6630}
6631
6632STDMETHODIMP Machine::GetUSBControllerCountByType(USBControllerType_T aType,
6633 ULONG *aControllers)
6634{
6635 CheckComArgOutPointerValid(aControllers);
6636
6637 if ( (aType <= USBControllerType_Null)
6638 || (aType >= USBControllerType_Last))
6639 return setError(E_INVALIDARG,
6640 tr("Invalid USB controller type: %d"),
6641 aType);
6642
6643 AutoCaller autoCaller(this);
6644 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6645
6646 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6647
6648 ComObjPtr<USBController> ctrl;
6649
6650 *aControllers = getUSBControllerCountByType(aType);
6651
6652 return S_OK;
6653}
6654
6655STDMETHODIMP Machine::RemoveUSBController(IN_BSTR aName)
6656{
6657 CheckComArgStrNotEmptyOrNull(aName);
6658
6659 AutoCaller autoCaller(this);
6660 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6661
6662 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6663
6664 HRESULT rc = checkStateDependency(MutableStateDep);
6665 if (FAILED(rc)) return rc;
6666
6667 ComObjPtr<USBController> ctrl;
6668 rc = getUSBControllerByName(aName, ctrl, true /* aSetError */);
6669 if (FAILED(rc)) return rc;
6670
6671 setModified(IsModified_USB);
6672 mUSBControllers.backup();
6673
6674 ctrl->unshare();
6675
6676 mUSBControllers->remove(ctrl);
6677
6678 /* inform the direct session if any */
6679 alock.release();
6680 onUSBControllerChange();
6681
6682 return S_OK;
6683}
6684
6685STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6686 ULONG *puOriginX,
6687 ULONG *puOriginY,
6688 ULONG *puWidth,
6689 ULONG *puHeight,
6690 BOOL *pfEnabled)
6691{
6692 LogFlowThisFunc(("\n"));
6693
6694 CheckComArgNotNull(puOriginX);
6695 CheckComArgNotNull(puOriginY);
6696 CheckComArgNotNull(puWidth);
6697 CheckComArgNotNull(puHeight);
6698 CheckComArgNotNull(pfEnabled);
6699
6700 uint32_t u32OriginX= 0;
6701 uint32_t u32OriginY= 0;
6702 uint32_t u32Width = 0;
6703 uint32_t u32Height = 0;
6704 uint16_t u16Flags = 0;
6705
6706 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6707 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6708 if (RT_FAILURE(vrc))
6709 {
6710#ifdef RT_OS_WINDOWS
6711 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6712 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6713 * So just assign fEnable to TRUE again.
6714 * The right fix would be to change GUI API wrappers to make sure that parameters
6715 * are changed only if API succeeds.
6716 */
6717 *pfEnabled = TRUE;
6718#endif
6719 return setError(VBOX_E_IPRT_ERROR,
6720 tr("Saved guest size is not available (%Rrc)"),
6721 vrc);
6722 }
6723
6724 *puOriginX = u32OriginX;
6725 *puOriginY = u32OriginY;
6726 *puWidth = u32Width;
6727 *puHeight = u32Height;
6728 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6729
6730 return S_OK;
6731}
6732
6733STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6734{
6735 LogFlowThisFunc(("\n"));
6736
6737 CheckComArgNotNull(aSize);
6738 CheckComArgNotNull(aWidth);
6739 CheckComArgNotNull(aHeight);
6740
6741 if (aScreenId != 0)
6742 return E_NOTIMPL;
6743
6744 AutoCaller autoCaller(this);
6745 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6746
6747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6748
6749 uint8_t *pu8Data = NULL;
6750 uint32_t cbData = 0;
6751 uint32_t u32Width = 0;
6752 uint32_t u32Height = 0;
6753
6754 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6755
6756 if (RT_FAILURE(vrc))
6757 return setError(VBOX_E_IPRT_ERROR,
6758 tr("Saved screenshot data is not available (%Rrc)"),
6759 vrc);
6760
6761 *aSize = cbData;
6762 *aWidth = u32Width;
6763 *aHeight = u32Height;
6764
6765 freeSavedDisplayScreenshot(pu8Data);
6766
6767 return S_OK;
6768}
6769
6770STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6771{
6772 LogFlowThisFunc(("\n"));
6773
6774 CheckComArgNotNull(aWidth);
6775 CheckComArgNotNull(aHeight);
6776 CheckComArgOutSafeArrayPointerValid(aData);
6777
6778 if (aScreenId != 0)
6779 return E_NOTIMPL;
6780
6781 AutoCaller autoCaller(this);
6782 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6783
6784 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6785
6786 uint8_t *pu8Data = NULL;
6787 uint32_t cbData = 0;
6788 uint32_t u32Width = 0;
6789 uint32_t u32Height = 0;
6790
6791 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6792
6793 if (RT_FAILURE(vrc))
6794 return setError(VBOX_E_IPRT_ERROR,
6795 tr("Saved screenshot data is not available (%Rrc)"),
6796 vrc);
6797
6798 *aWidth = u32Width;
6799 *aHeight = u32Height;
6800
6801 com::SafeArray<BYTE> bitmap(cbData);
6802 /* Convert pixels to format expected by the API caller. */
6803 if (aBGR)
6804 {
6805 /* [0] B, [1] G, [2] R, [3] A. */
6806 for (unsigned i = 0; i < cbData; i += 4)
6807 {
6808 bitmap[i] = pu8Data[i];
6809 bitmap[i + 1] = pu8Data[i + 1];
6810 bitmap[i + 2] = pu8Data[i + 2];
6811 bitmap[i + 3] = 0xff;
6812 }
6813 }
6814 else
6815 {
6816 /* [0] R, [1] G, [2] B, [3] A. */
6817 for (unsigned i = 0; i < cbData; i += 4)
6818 {
6819 bitmap[i] = pu8Data[i + 2];
6820 bitmap[i + 1] = pu8Data[i + 1];
6821 bitmap[i + 2] = pu8Data[i];
6822 bitmap[i + 3] = 0xff;
6823 }
6824 }
6825 bitmap.detachTo(ComSafeArrayOutArg(aData));
6826
6827 freeSavedDisplayScreenshot(pu8Data);
6828
6829 return S_OK;
6830}
6831
6832
6833STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6834{
6835 LogFlowThisFunc(("\n"));
6836
6837 CheckComArgNotNull(aWidth);
6838 CheckComArgNotNull(aHeight);
6839 CheckComArgOutSafeArrayPointerValid(aData);
6840
6841 if (aScreenId != 0)
6842 return E_NOTIMPL;
6843
6844 AutoCaller autoCaller(this);
6845 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6846
6847 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6848
6849 uint8_t *pu8Data = NULL;
6850 uint32_t cbData = 0;
6851 uint32_t u32Width = 0;
6852 uint32_t u32Height = 0;
6853
6854 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6855
6856 if (RT_FAILURE(vrc))
6857 return setError(VBOX_E_IPRT_ERROR,
6858 tr("Saved screenshot data is not available (%Rrc)"),
6859 vrc);
6860
6861 *aWidth = u32Width;
6862 *aHeight = u32Height;
6863
6864 HRESULT rc = S_OK;
6865 uint8_t *pu8PNG = NULL;
6866 uint32_t cbPNG = 0;
6867 uint32_t cxPNG = 0;
6868 uint32_t cyPNG = 0;
6869
6870 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6871
6872 if (RT_SUCCESS(vrc))
6873 {
6874 com::SafeArray<BYTE> screenData(cbPNG);
6875 screenData.initFrom(pu8PNG, cbPNG);
6876 if (pu8PNG)
6877 RTMemFree(pu8PNG);
6878 screenData.detachTo(ComSafeArrayOutArg(aData));
6879 }
6880 else
6881 {
6882 if (pu8PNG)
6883 RTMemFree(pu8PNG);
6884 return setError(VBOX_E_IPRT_ERROR,
6885 tr("Could not convert screenshot to PNG (%Rrc)"),
6886 vrc);
6887 }
6888
6889 freeSavedDisplayScreenshot(pu8Data);
6890
6891 return rc;
6892}
6893
6894STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6895{
6896 LogFlowThisFunc(("\n"));
6897
6898 CheckComArgNotNull(aSize);
6899 CheckComArgNotNull(aWidth);
6900 CheckComArgNotNull(aHeight);
6901
6902 if (aScreenId != 0)
6903 return E_NOTIMPL;
6904
6905 AutoCaller autoCaller(this);
6906 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6907
6908 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6909
6910 uint8_t *pu8Data = NULL;
6911 uint32_t cbData = 0;
6912 uint32_t u32Width = 0;
6913 uint32_t u32Height = 0;
6914
6915 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6916
6917 if (RT_FAILURE(vrc))
6918 return setError(VBOX_E_IPRT_ERROR,
6919 tr("Saved screenshot data is not available (%Rrc)"),
6920 vrc);
6921
6922 *aSize = cbData;
6923 *aWidth = u32Width;
6924 *aHeight = u32Height;
6925
6926 freeSavedDisplayScreenshot(pu8Data);
6927
6928 return S_OK;
6929}
6930
6931STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6932{
6933 LogFlowThisFunc(("\n"));
6934
6935 CheckComArgNotNull(aWidth);
6936 CheckComArgNotNull(aHeight);
6937 CheckComArgOutSafeArrayPointerValid(aData);
6938
6939 if (aScreenId != 0)
6940 return E_NOTIMPL;
6941
6942 AutoCaller autoCaller(this);
6943 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6944
6945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6946
6947 uint8_t *pu8Data = NULL;
6948 uint32_t cbData = 0;
6949 uint32_t u32Width = 0;
6950 uint32_t u32Height = 0;
6951
6952 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6953
6954 if (RT_FAILURE(vrc))
6955 return setError(VBOX_E_IPRT_ERROR,
6956 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6957 vrc);
6958
6959 *aWidth = u32Width;
6960 *aHeight = u32Height;
6961
6962 com::SafeArray<BYTE> png(cbData);
6963 png.initFrom(pu8Data, cbData);
6964 png.detachTo(ComSafeArrayOutArg(aData));
6965
6966 freeSavedDisplayScreenshot(pu8Data);
6967
6968 return S_OK;
6969}
6970
6971STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6972{
6973 HRESULT rc = S_OK;
6974 LogFlowThisFunc(("\n"));
6975
6976 AutoCaller autoCaller(this);
6977 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6978
6979 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6980
6981 if (!mHWData->mCPUHotPlugEnabled)
6982 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6983
6984 if (aCpu >= mHWData->mCPUCount)
6985 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6986
6987 if (mHWData->mCPUAttached[aCpu])
6988 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6989
6990 alock.release();
6991 rc = onCPUChange(aCpu, false);
6992 alock.acquire();
6993 if (FAILED(rc)) return rc;
6994
6995 setModified(IsModified_MachineData);
6996 mHWData.backup();
6997 mHWData->mCPUAttached[aCpu] = true;
6998
6999 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
7000 if (Global::IsOnline(mData->mMachineState))
7001 saveSettings(NULL);
7002
7003 return S_OK;
7004}
7005
7006STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
7007{
7008 HRESULT rc = S_OK;
7009 LogFlowThisFunc(("\n"));
7010
7011 AutoCaller autoCaller(this);
7012 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7013
7014 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7015
7016 if (!mHWData->mCPUHotPlugEnabled)
7017 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
7018
7019 if (aCpu >= SchemaDefs::MaxCPUCount)
7020 return setError(E_INVALIDARG,
7021 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
7022 SchemaDefs::MaxCPUCount);
7023
7024 if (!mHWData->mCPUAttached[aCpu])
7025 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
7026
7027 /* CPU 0 can't be detached */
7028 if (aCpu == 0)
7029 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
7030
7031 alock.release();
7032 rc = onCPUChange(aCpu, true);
7033 alock.acquire();
7034 if (FAILED(rc)) return rc;
7035
7036 setModified(IsModified_MachineData);
7037 mHWData.backup();
7038 mHWData->mCPUAttached[aCpu] = false;
7039
7040 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
7041 if (Global::IsOnline(mData->mMachineState))
7042 saveSettings(NULL);
7043
7044 return S_OK;
7045}
7046
7047STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
7048{
7049 LogFlowThisFunc(("\n"));
7050
7051 CheckComArgNotNull(aCpuAttached);
7052
7053 *aCpuAttached = false;
7054
7055 AutoCaller autoCaller(this);
7056 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7057
7058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7059
7060 /* If hotplug is enabled the CPU is always enabled. */
7061 if (!mHWData->mCPUHotPlugEnabled)
7062 {
7063 if (aCpu < mHWData->mCPUCount)
7064 *aCpuAttached = true;
7065 }
7066 else
7067 {
7068 if (aCpu < SchemaDefs::MaxCPUCount)
7069 *aCpuAttached = mHWData->mCPUAttached[aCpu];
7070 }
7071
7072 return S_OK;
7073}
7074
7075STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
7076{
7077 CheckComArgOutPointerValid(aName);
7078
7079 AutoCaller autoCaller(this);
7080 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7081
7082 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7083
7084 Utf8Str log = queryLogFilename(aIdx);
7085 if (!RTFileExists(log.c_str()))
7086 log.setNull();
7087 log.cloneTo(aName);
7088
7089 return S_OK;
7090}
7091
7092STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
7093{
7094 LogFlowThisFunc(("\n"));
7095 CheckComArgOutSafeArrayPointerValid(aData);
7096 if (aSize < 0)
7097 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
7098
7099 AutoCaller autoCaller(this);
7100 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7101
7102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7103
7104 HRESULT rc = S_OK;
7105 Utf8Str log = queryLogFilename(aIdx);
7106
7107 /* do not unnecessarily hold the lock while doing something which does
7108 * not need the lock and potentially takes a long time. */
7109 alock.release();
7110
7111 /* Limit the chunk size to 32K for now, as that gives better performance
7112 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
7113 * One byte expands to approx. 25 bytes of breathtaking XML. */
7114 size_t cbData = (size_t)RT_MIN(aSize, 32768);
7115 com::SafeArray<BYTE> logData(cbData);
7116
7117 RTFILE LogFile;
7118 int vrc = RTFileOpen(&LogFile, log.c_str(),
7119 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
7120 if (RT_SUCCESS(vrc))
7121 {
7122 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
7123 if (RT_SUCCESS(vrc))
7124 logData.resize(cbData);
7125 else
7126 rc = setError(VBOX_E_IPRT_ERROR,
7127 tr("Could not read log file '%s' (%Rrc)"),
7128 log.c_str(), vrc);
7129 RTFileClose(LogFile);
7130 }
7131 else
7132 rc = setError(VBOX_E_IPRT_ERROR,
7133 tr("Could not open log file '%s' (%Rrc)"),
7134 log.c_str(), vrc);
7135
7136 if (FAILED(rc))
7137 logData.resize(0);
7138 logData.detachTo(ComSafeArrayOutArg(aData));
7139
7140 return rc;
7141}
7142
7143
7144/**
7145 * Currently this method doesn't attach device to the running VM,
7146 * just makes sure it's plugged on next VM start.
7147 */
7148STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
7149{
7150 AutoCaller autoCaller(this);
7151 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7152
7153 // lock scope
7154 {
7155 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7156
7157 HRESULT rc = checkStateDependency(MutableStateDep);
7158 if (FAILED(rc)) return rc;
7159
7160 ChipsetType_T aChipset = ChipsetType_PIIX3;
7161 COMGETTER(ChipsetType)(&aChipset);
7162
7163 if (aChipset != ChipsetType_ICH9)
7164 {
7165 return setError(E_INVALIDARG,
7166 tr("Host PCI attachment only supported with ICH9 chipset"));
7167 }
7168
7169 // check if device with this host PCI address already attached
7170 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7171 it != mHWData->mPCIDeviceAssignments.end();
7172 ++it)
7173 {
7174 LONG iHostAddress = -1;
7175 ComPtr<PCIDeviceAttachment> pAttach;
7176 pAttach = *it;
7177 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7178 if (iHostAddress == hostAddress)
7179 return setError(E_INVALIDARG,
7180 tr("Device with host PCI address already attached to this VM"));
7181 }
7182
7183 ComObjPtr<PCIDeviceAttachment> pda;
7184 char name[32];
7185
7186 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
7187 Bstr bname(name);
7188 pda.createObject();
7189 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
7190 setModified(IsModified_MachineData);
7191 mHWData.backup();
7192 mHWData->mPCIDeviceAssignments.push_back(pda);
7193 }
7194
7195 return S_OK;
7196}
7197
7198/**
7199 * Currently this method doesn't detach device from the running VM,
7200 * just makes sure it's not plugged on next VM start.
7201 */
7202STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
7203{
7204 AutoCaller autoCaller(this);
7205 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7206
7207 ComObjPtr<PCIDeviceAttachment> pAttach;
7208 bool fRemoved = false;
7209 HRESULT rc;
7210
7211 // lock scope
7212 {
7213 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7214
7215 rc = checkStateDependency(MutableStateDep);
7216 if (FAILED(rc)) return rc;
7217
7218 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7219 it != mHWData->mPCIDeviceAssignments.end();
7220 ++it)
7221 {
7222 LONG iHostAddress = -1;
7223 pAttach = *it;
7224 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7225 if (iHostAddress != -1 && iHostAddress == hostAddress)
7226 {
7227 setModified(IsModified_MachineData);
7228 mHWData.backup();
7229 mHWData->mPCIDeviceAssignments.remove(pAttach);
7230 fRemoved = true;
7231 break;
7232 }
7233 }
7234 }
7235
7236
7237 /* Fire event outside of the lock */
7238 if (fRemoved)
7239 {
7240 Assert(!pAttach.isNull());
7241 ComPtr<IEventSource> es;
7242 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
7243 Assert(SUCCEEDED(rc));
7244 Bstr mid;
7245 rc = this->COMGETTER(Id)(mid.asOutParam());
7246 Assert(SUCCEEDED(rc));
7247 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
7248 }
7249
7250 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
7251 tr("No host PCI device %08x attached"),
7252 hostAddress
7253 );
7254}
7255
7256STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
7257{
7258 CheckComArgOutSafeArrayPointerValid(aAssignments);
7259
7260 AutoCaller autoCaller(this);
7261 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7262
7263 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7264
7265 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
7266 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
7267
7268 return S_OK;
7269}
7270
7271STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
7272{
7273 CheckComArgOutPointerValid(aBandwidthControl);
7274
7275 AutoCaller autoCaller(this);
7276 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7277
7278 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
7279
7280 return S_OK;
7281}
7282
7283STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
7284{
7285 CheckComArgOutPointerValid(pfEnabled);
7286 AutoCaller autoCaller(this);
7287 HRESULT hrc = autoCaller.rc();
7288 if (SUCCEEDED(hrc))
7289 {
7290 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7291 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
7292 }
7293 return hrc;
7294}
7295
7296STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
7297{
7298 AutoCaller autoCaller(this);
7299 HRESULT hrc = autoCaller.rc();
7300 if (SUCCEEDED(hrc))
7301 {
7302 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7303 hrc = checkStateDependency(MutableStateDep);
7304 if (SUCCEEDED(hrc))
7305 {
7306 hrc = mHWData.backupEx();
7307 if (SUCCEEDED(hrc))
7308 {
7309 setModified(IsModified_MachineData);
7310 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
7311 }
7312 }
7313 }
7314 return hrc;
7315}
7316
7317STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
7318{
7319 CheckComArgOutPointerValid(pbstrConfig);
7320 AutoCaller autoCaller(this);
7321 HRESULT hrc = autoCaller.rc();
7322 if (SUCCEEDED(hrc))
7323 {
7324 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7325 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
7326 }
7327 return hrc;
7328}
7329
7330STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
7331{
7332 CheckComArgStr(bstrConfig);
7333 AutoCaller autoCaller(this);
7334 HRESULT hrc = autoCaller.rc();
7335 if (SUCCEEDED(hrc))
7336 {
7337 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7338 hrc = checkStateDependency(MutableStateDep);
7339 if (SUCCEEDED(hrc))
7340 {
7341 hrc = mHWData.backupEx();
7342 if (SUCCEEDED(hrc))
7343 {
7344 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
7345 if (SUCCEEDED(hrc))
7346 setModified(IsModified_MachineData);
7347 }
7348 }
7349 }
7350 return hrc;
7351
7352}
7353
7354STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
7355{
7356 CheckComArgOutPointerValid(pfAllow);
7357 AutoCaller autoCaller(this);
7358 HRESULT hrc = autoCaller.rc();
7359 if (SUCCEEDED(hrc))
7360 {
7361 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7362 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
7363 }
7364 return hrc;
7365}
7366
7367STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
7368{
7369 AutoCaller autoCaller(this);
7370 HRESULT hrc = autoCaller.rc();
7371 if (SUCCEEDED(hrc))
7372 {
7373 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7374 hrc = checkStateDependency(MutableStateDep);
7375 if (SUCCEEDED(hrc))
7376 {
7377 hrc = mHWData.backupEx();
7378 if (SUCCEEDED(hrc))
7379 {
7380 setModified(IsModified_MachineData);
7381 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
7382 }
7383 }
7384 }
7385 return hrc;
7386}
7387
7388STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
7389{
7390 CheckComArgOutPointerValid(pfEnabled);
7391 AutoCaller autoCaller(this);
7392 HRESULT hrc = autoCaller.rc();
7393 if (SUCCEEDED(hrc))
7394 {
7395 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7396 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
7397 }
7398 return hrc;
7399}
7400
7401STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
7402{
7403 AutoCaller autoCaller(this);
7404 HRESULT hrc = autoCaller.rc();
7405 if (SUCCEEDED(hrc))
7406 {
7407 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7408 hrc = checkStateDependency(MutableStateDep);
7409 if ( SUCCEEDED(hrc)
7410 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
7411 {
7412 AutostartDb *autostartDb = mParent->getAutostartDb();
7413 int vrc;
7414
7415 if (fEnabled)
7416 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7417 else
7418 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7419
7420 if (RT_SUCCESS(vrc))
7421 {
7422 hrc = mHWData.backupEx();
7423 if (SUCCEEDED(hrc))
7424 {
7425 setModified(IsModified_MachineData);
7426 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
7427 }
7428 }
7429 else if (vrc == VERR_NOT_SUPPORTED)
7430 hrc = setError(VBOX_E_NOT_SUPPORTED,
7431 tr("The VM autostart feature is not supported on this platform"));
7432 else if (vrc == VERR_PATH_NOT_FOUND)
7433 hrc = setError(E_FAIL,
7434 tr("The path to the autostart database is not set"));
7435 else
7436 hrc = setError(E_UNEXPECTED,
7437 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7438 fEnabled ? "Adding" : "Removing",
7439 mUserData->s.strName.c_str(), vrc);
7440 }
7441 }
7442 return hrc;
7443}
7444
7445STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
7446{
7447 CheckComArgOutPointerValid(puDelay);
7448 AutoCaller autoCaller(this);
7449 HRESULT hrc = autoCaller.rc();
7450 if (SUCCEEDED(hrc))
7451 {
7452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7453 *puDelay = mHWData->mAutostart.uAutostartDelay;
7454 }
7455 return hrc;
7456}
7457
7458STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
7459{
7460 AutoCaller autoCaller(this);
7461 HRESULT hrc = autoCaller.rc();
7462 if (SUCCEEDED(hrc))
7463 {
7464 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7465 hrc = checkStateDependency(MutableStateDep);
7466 if (SUCCEEDED(hrc))
7467 {
7468 hrc = mHWData.backupEx();
7469 if (SUCCEEDED(hrc))
7470 {
7471 setModified(IsModified_MachineData);
7472 mHWData->mAutostart.uAutostartDelay = uDelay;
7473 }
7474 }
7475 }
7476 return hrc;
7477}
7478
7479STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
7480{
7481 CheckComArgOutPointerValid(penmAutostopType);
7482 AutoCaller autoCaller(this);
7483 HRESULT hrc = autoCaller.rc();
7484 if (SUCCEEDED(hrc))
7485 {
7486 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7487 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
7488 }
7489 return hrc;
7490}
7491
7492STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
7493{
7494 AutoCaller autoCaller(this);
7495 HRESULT hrc = autoCaller.rc();
7496 if (SUCCEEDED(hrc))
7497 {
7498 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7499 hrc = checkStateDependency(MutableStateDep);
7500 if ( SUCCEEDED(hrc)
7501 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
7502 {
7503 AutostartDb *autostartDb = mParent->getAutostartDb();
7504 int vrc;
7505
7506 if (enmAutostopType != AutostopType_Disabled)
7507 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7508 else
7509 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7510
7511 if (RT_SUCCESS(vrc))
7512 {
7513 hrc = mHWData.backupEx();
7514 if (SUCCEEDED(hrc))
7515 {
7516 setModified(IsModified_MachineData);
7517 mHWData->mAutostart.enmAutostopType = enmAutostopType;
7518 }
7519 }
7520 else if (vrc == VERR_NOT_SUPPORTED)
7521 hrc = setError(VBOX_E_NOT_SUPPORTED,
7522 tr("The VM autostop feature is not supported on this platform"));
7523 else if (vrc == VERR_PATH_NOT_FOUND)
7524 hrc = setError(E_FAIL,
7525 tr("The path to the autostart database is not set"));
7526 else
7527 hrc = setError(E_UNEXPECTED,
7528 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7529 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7530 mUserData->s.strName.c_str(), vrc);
7531 }
7532 }
7533 return hrc;
7534}
7535
7536STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7537{
7538 CheckComArgOutPointerValid(aDefaultFrontend);
7539 AutoCaller autoCaller(this);
7540 HRESULT hrc = autoCaller.rc();
7541 if (SUCCEEDED(hrc))
7542 {
7543 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7544 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7545 }
7546 return hrc;
7547}
7548
7549STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7550{
7551 CheckComArgStr(aDefaultFrontend);
7552 AutoCaller autoCaller(this);
7553 HRESULT hrc = autoCaller.rc();
7554 if (SUCCEEDED(hrc))
7555 {
7556 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7557 hrc = checkStateDependency(MutableOrSavedStateDep);
7558 if (SUCCEEDED(hrc))
7559 {
7560 hrc = mHWData.backupEx();
7561 if (SUCCEEDED(hrc))
7562 {
7563 setModified(IsModified_MachineData);
7564 mHWData->mDefaultFrontend = aDefaultFrontend;
7565 }
7566 }
7567 }
7568 return hrc;
7569}
7570
7571STDMETHODIMP Machine::COMGETTER(Icon)(ComSafeArrayOut(BYTE, aIcon))
7572{
7573 CheckComArgSafeArrayNotNull(aIcon);
7574 CheckComArgOutSafeArrayPointerValid(aIcon);
7575 AutoCaller autoCaller(this);
7576 HRESULT hrc = autoCaller.rc();
7577 if (SUCCEEDED(hrc))
7578 {
7579 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7580 com::SafeArray<BYTE> icon(mUserData->mIcon.size());
7581 memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size());
7582 icon.detachTo(ComSafeArrayOutArg(aIcon));
7583 }
7584 return hrc;
7585}
7586
7587STDMETHODIMP Machine::COMSETTER(Icon)(ComSafeArrayIn(BYTE, aIcon))
7588{
7589 CheckComArgSafeArrayNotNull(aIcon);
7590 AutoCaller autoCaller(this);
7591 HRESULT hrc = autoCaller.rc();
7592 if (SUCCEEDED(hrc))
7593 {
7594 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7595 hrc = checkStateDependency(MutableOrSavedStateDep);
7596 if (SUCCEEDED(hrc))
7597 {
7598 setModified(IsModified_MachineData);
7599 mUserData.backup();
7600 com::SafeArray<BYTE> icon(ComSafeArrayInArg(aIcon));
7601 mUserData->mIcon.clear();
7602 memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size());
7603 }
7604 }
7605 return hrc;
7606}
7607
7608STDMETHODIMP Machine::COMGETTER(USBProxyAvailable)(BOOL *aAvailable)
7609{
7610 CheckComArgOutPointerValid(aAvailable);
7611
7612 AutoCaller autoCaller(this);
7613 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7614
7615#ifdef VBOX_WITH_USB
7616 *aAvailable = true;
7617#else
7618 *aAvailable = false;
7619#endif
7620 return S_OK;
7621}
7622
7623STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7624{
7625 LogFlowFuncEnter();
7626
7627 CheckComArgNotNull(pTarget);
7628 CheckComArgOutPointerValid(pProgress);
7629
7630 /* Convert the options. */
7631 RTCList<CloneOptions_T> optList;
7632 if (options != NULL)
7633 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7634
7635 if (optList.contains(CloneOptions_Link))
7636 {
7637 if (!isSnapshotMachine())
7638 return setError(E_INVALIDARG,
7639 tr("Linked clone can only be created from a snapshot"));
7640 if (mode != CloneMode_MachineState)
7641 return setError(E_INVALIDARG,
7642 tr("Linked clone can only be created for a single machine state"));
7643 }
7644 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7645
7646 AutoCaller autoCaller(this);
7647 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7648
7649
7650 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7651
7652 HRESULT rc = pWorker->start(pProgress);
7653
7654 LogFlowFuncLeave();
7655
7656 return rc;
7657}
7658
7659// public methods for internal purposes
7660/////////////////////////////////////////////////////////////////////////////
7661
7662/**
7663 * Adds the given IsModified_* flag to the dirty flags of the machine.
7664 * This must be called either during loadSettings or under the machine write lock.
7665 * @param fl
7666 */
7667void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7668{
7669 mData->flModifications |= fl;
7670 if (fAllowStateModification && isStateModificationAllowed())
7671 mData->mCurrentStateModified = true;
7672}
7673
7674/**
7675 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7676 * care of the write locking.
7677 *
7678 * @param fModifications The flag to add.
7679 */
7680void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7681{
7682 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7683 setModified(fModification, fAllowStateModification);
7684}
7685
7686/**
7687 * Saves the registry entry of this machine to the given configuration node.
7688 *
7689 * @param aEntryNode Node to save the registry entry to.
7690 *
7691 * @note locks this object for reading.
7692 */
7693HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7694{
7695 AutoLimitedCaller autoCaller(this);
7696 AssertComRCReturnRC(autoCaller.rc());
7697
7698 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7699
7700 data.uuid = mData->mUuid;
7701 data.strSettingsFile = mData->m_strConfigFile;
7702
7703 return S_OK;
7704}
7705
7706/**
7707 * Calculates the absolute path of the given path taking the directory of the
7708 * machine settings file as the current directory.
7709 *
7710 * @param aPath Path to calculate the absolute path for.
7711 * @param aResult Where to put the result (used only on success, can be the
7712 * same Utf8Str instance as passed in @a aPath).
7713 * @return IPRT result.
7714 *
7715 * @note Locks this object for reading.
7716 */
7717int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7718{
7719 AutoCaller autoCaller(this);
7720 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7721
7722 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7723
7724 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7725
7726 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7727
7728 strSettingsDir.stripFilename();
7729 char folder[RTPATH_MAX];
7730 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7731 if (RT_SUCCESS(vrc))
7732 aResult = folder;
7733
7734 return vrc;
7735}
7736
7737/**
7738 * Copies strSource to strTarget, making it relative to the machine folder
7739 * if it is a subdirectory thereof, or simply copying it otherwise.
7740 *
7741 * @param strSource Path to evaluate and copy.
7742 * @param strTarget Buffer to receive target path.
7743 *
7744 * @note Locks this object for reading.
7745 */
7746void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7747 Utf8Str &strTarget)
7748{
7749 AutoCaller autoCaller(this);
7750 AssertComRCReturn(autoCaller.rc(), (void)0);
7751
7752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7753
7754 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7755 // use strTarget as a temporary buffer to hold the machine settings dir
7756 strTarget = mData->m_strConfigFileFull;
7757 strTarget.stripFilename();
7758 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7759 {
7760 // is relative: then append what's left
7761 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7762 // for empty paths (only possible for subdirs) use "." to avoid
7763 // triggering default settings for not present config attributes.
7764 if (strTarget.isEmpty())
7765 strTarget = ".";
7766 }
7767 else
7768 // is not relative: then overwrite
7769 strTarget = strSource;
7770}
7771
7772/**
7773 * Returns the full path to the machine's log folder in the
7774 * \a aLogFolder argument.
7775 */
7776void Machine::getLogFolder(Utf8Str &aLogFolder)
7777{
7778 AutoCaller autoCaller(this);
7779 AssertComRCReturnVoid(autoCaller.rc());
7780
7781 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7782
7783 char szTmp[RTPATH_MAX];
7784 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7785 if (RT_SUCCESS(vrc))
7786 {
7787 if (szTmp[0] && !mUserData.isNull())
7788 {
7789 char szTmp2[RTPATH_MAX];
7790 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7791 if (RT_SUCCESS(vrc))
7792 aLogFolder = BstrFmt("%s%c%s",
7793 szTmp2,
7794 RTPATH_DELIMITER,
7795 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7796 }
7797 else
7798 vrc = VERR_PATH_IS_RELATIVE;
7799 }
7800
7801 if (RT_FAILURE(vrc))
7802 {
7803 // fallback if VBOX_USER_LOGHOME is not set or invalid
7804 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7805 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7806 aLogFolder.append(RTPATH_DELIMITER);
7807 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7808 }
7809}
7810
7811/**
7812 * Returns the full path to the machine's log file for an given index.
7813 */
7814Utf8Str Machine::queryLogFilename(ULONG idx)
7815{
7816 Utf8Str logFolder;
7817 getLogFolder(logFolder);
7818 Assert(logFolder.length());
7819 Utf8Str log;
7820 if (idx == 0)
7821 log = Utf8StrFmt("%s%cVBox.log",
7822 logFolder.c_str(), RTPATH_DELIMITER);
7823 else
7824 log = Utf8StrFmt("%s%cVBox.log.%d",
7825 logFolder.c_str(), RTPATH_DELIMITER, idx);
7826 return log;
7827}
7828
7829/**
7830 * Composes a unique saved state filename based on the current system time. The filename is
7831 * granular to the second so this will work so long as no more than one snapshot is taken on
7832 * a machine per second.
7833 *
7834 * Before version 4.1, we used this formula for saved state files:
7835 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7836 * which no longer works because saved state files can now be shared between the saved state of the
7837 * "saved" machine and an online snapshot, and the following would cause problems:
7838 * 1) save machine
7839 * 2) create online snapshot from that machine state --> reusing saved state file
7840 * 3) save machine again --> filename would be reused, breaking the online snapshot
7841 *
7842 * So instead we now use a timestamp.
7843 *
7844 * @param str
7845 */
7846void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7847{
7848 AutoCaller autoCaller(this);
7849 AssertComRCReturnVoid(autoCaller.rc());
7850
7851 {
7852 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7853 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7854 }
7855
7856 RTTIMESPEC ts;
7857 RTTimeNow(&ts);
7858 RTTIME time;
7859 RTTimeExplode(&time, &ts);
7860
7861 strStateFilePath += RTPATH_DELIMITER;
7862 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7863 time.i32Year, time.u8Month, time.u8MonthDay,
7864 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7865}
7866
7867/**
7868 * Returns the full path to the default video capture file.
7869 */
7870void Machine::getDefaultVideoCaptureFile(Utf8Str &strFile)
7871{
7872 AutoCaller autoCaller(this);
7873 AssertComRCReturnVoid(autoCaller.rc());
7874
7875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7876
7877 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7878 strFile.stripExt(); // path/to/machinesfolder/vmname/vmname
7879 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7880}
7881
7882/**
7883 * Returns whether at least one USB controller is present for the VM.
7884 */
7885bool Machine::isUSBControllerPresent()
7886{
7887 AutoCaller autoCaller(this);
7888 AssertComRCReturn(autoCaller.rc(), false);
7889
7890 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7891
7892 return (mUSBControllers->size() > 0);
7893}
7894
7895/**
7896 * @note Locks this object for writing, calls the client process
7897 * (inside the lock).
7898 */
7899HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7900 const Utf8Str &strFrontend,
7901 const Utf8Str &strEnvironment,
7902 ProgressProxy *aProgress)
7903{
7904 LogFlowThisFuncEnter();
7905
7906 AssertReturn(aControl, E_FAIL);
7907 AssertReturn(aProgress, E_FAIL);
7908 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7909
7910 AutoCaller autoCaller(this);
7911 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7912
7913 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7914
7915 if (!mData->mRegistered)
7916 return setError(E_UNEXPECTED,
7917 tr("The machine '%s' is not registered"),
7918 mUserData->s.strName.c_str());
7919
7920 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7921
7922 if ( mData->mSession.mState == SessionState_Locked
7923 || mData->mSession.mState == SessionState_Spawning
7924 || mData->mSession.mState == SessionState_Unlocking)
7925 return setError(VBOX_E_INVALID_OBJECT_STATE,
7926 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7927 mUserData->s.strName.c_str());
7928
7929 /* may not be busy */
7930 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7931
7932 /* get the path to the executable */
7933 char szPath[RTPATH_MAX];
7934 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7935 size_t sz = strlen(szPath);
7936 szPath[sz++] = RTPATH_DELIMITER;
7937 szPath[sz] = 0;
7938 char *cmd = szPath + sz;
7939 sz = RTPATH_MAX - sz;
7940
7941 int vrc = VINF_SUCCESS;
7942 RTPROCESS pid = NIL_RTPROCESS;
7943
7944 RTENV env = RTENV_DEFAULT;
7945
7946 if (!strEnvironment.isEmpty())
7947 {
7948 char *newEnvStr = NULL;
7949
7950 do
7951 {
7952 /* clone the current environment */
7953 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7954 AssertRCBreakStmt(vrc2, vrc = vrc2);
7955
7956 newEnvStr = RTStrDup(strEnvironment.c_str());
7957 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7958
7959 /* put new variables to the environment
7960 * (ignore empty variable names here since RTEnv API
7961 * intentionally doesn't do that) */
7962 char *var = newEnvStr;
7963 for (char *p = newEnvStr; *p; ++p)
7964 {
7965 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7966 {
7967 *p = '\0';
7968 if (*var)
7969 {
7970 char *val = strchr(var, '=');
7971 if (val)
7972 {
7973 *val++ = '\0';
7974 vrc2 = RTEnvSetEx(env, var, val);
7975 }
7976 else
7977 vrc2 = RTEnvUnsetEx(env, var);
7978 if (RT_FAILURE(vrc2))
7979 break;
7980 }
7981 var = p + 1;
7982 }
7983 }
7984 if (RT_SUCCESS(vrc2) && *var)
7985 vrc2 = RTEnvPutEx(env, var);
7986
7987 AssertRCBreakStmt(vrc2, vrc = vrc2);
7988 }
7989 while (0);
7990
7991 if (newEnvStr != NULL)
7992 RTStrFree(newEnvStr);
7993 }
7994
7995#ifdef VBOX_WITH_QTGUI
7996 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7997 {
7998# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7999 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
8000# else
8001 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
8002# endif
8003 Assert(sz >= sizeof(VirtualBox_exe));
8004 strcpy(cmd, VirtualBox_exe);
8005
8006 Utf8Str idStr = mData->mUuid.toString();
8007 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
8008 vrc = RTProcCreate(szPath, args, env, 0, &pid);
8009 }
8010#else /* !VBOX_WITH_QTGUI */
8011 if (0)
8012 ;
8013#endif /* VBOX_WITH_QTGUI */
8014
8015 else
8016
8017#ifdef VBOX_WITH_VBOXSDL
8018 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
8019 {
8020 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
8021 Assert(sz >= sizeof(VBoxSDL_exe));
8022 strcpy(cmd, VBoxSDL_exe);
8023
8024 Utf8Str idStr = mData->mUuid.toString();
8025 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
8026 vrc = RTProcCreate(szPath, args, env, 0, &pid);
8027 }
8028#else /* !VBOX_WITH_VBOXSDL */
8029 if (0)
8030 ;
8031#endif /* !VBOX_WITH_VBOXSDL */
8032
8033 else
8034
8035#ifdef VBOX_WITH_HEADLESS
8036 if ( strFrontend == "headless"
8037 || strFrontend == "capture"
8038 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
8039 )
8040 {
8041 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
8042 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
8043 * and a VM works even if the server has not been installed.
8044 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
8045 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
8046 * differently in 4.0 and 3.x.
8047 */
8048 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
8049 Assert(sz >= sizeof(VBoxHeadless_exe));
8050 strcpy(cmd, VBoxHeadless_exe);
8051
8052 Utf8Str idStr = mData->mUuid.toString();
8053 /* Leave space for "--capture" arg. */
8054 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
8055 "--startvm", idStr.c_str(),
8056 "--vrde", "config",
8057 0, /* For "--capture". */
8058 0 };
8059 if (strFrontend == "capture")
8060 {
8061 unsigned pos = RT_ELEMENTS(args) - 2;
8062 args[pos] = "--capture";
8063 }
8064 vrc = RTProcCreate(szPath, args, env,
8065#ifdef RT_OS_WINDOWS
8066 RTPROC_FLAGS_NO_WINDOW
8067#else
8068 0
8069#endif
8070 , &pid);
8071 }
8072#else /* !VBOX_WITH_HEADLESS */
8073 if (0)
8074 ;
8075#endif /* !VBOX_WITH_HEADLESS */
8076 else
8077 {
8078 RTEnvDestroy(env);
8079 return setError(E_INVALIDARG,
8080 tr("Invalid frontend name: '%s'"),
8081 strFrontend.c_str());
8082 }
8083
8084 RTEnvDestroy(env);
8085
8086 if (RT_FAILURE(vrc))
8087 return setError(VBOX_E_IPRT_ERROR,
8088 tr("Could not launch a process for the machine '%s' (%Rrc)"),
8089 mUserData->s.strName.c_str(), vrc);
8090
8091 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
8092
8093 /*
8094 * Note that we don't release the lock here before calling the client,
8095 * because it doesn't need to call us back if called with a NULL argument.
8096 * Releasing the lock here is dangerous because we didn't prepare the
8097 * launch data yet, but the client we've just started may happen to be
8098 * too fast and call openSession() that will fail (because of PID, etc.),
8099 * so that the Machine will never get out of the Spawning session state.
8100 */
8101
8102 /* inform the session that it will be a remote one */
8103 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
8104 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
8105 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
8106
8107 if (FAILED(rc))
8108 {
8109 /* restore the session state */
8110 mData->mSession.mState = SessionState_Unlocked;
8111 /* The failure may occur w/o any error info (from RPC), so provide one */
8112 return setError(VBOX_E_VM_ERROR,
8113 tr("Failed to assign the machine to the session (%Rrc)"), rc);
8114 }
8115
8116 /* attach launch data to the machine */
8117 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8118 mData->mSession.mRemoteControls.push_back(aControl);
8119 mData->mSession.mProgress = aProgress;
8120 mData->mSession.mPID = pid;
8121 mData->mSession.mState = SessionState_Spawning;
8122 mData->mSession.mType = strFrontend;
8123
8124 LogFlowThisFuncLeave();
8125 return S_OK;
8126}
8127
8128/**
8129 * Returns @c true if the given machine has an open direct session and returns
8130 * the session machine instance and additional session data (on some platforms)
8131 * if so.
8132 *
8133 * Note that when the method returns @c false, the arguments remain unchanged.
8134 *
8135 * @param aMachine Session machine object.
8136 * @param aControl Direct session control object (optional).
8137 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
8138 *
8139 * @note locks this object for reading.
8140 */
8141#if defined(RT_OS_WINDOWS)
8142bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8143 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8144 HANDLE *aIPCSem /*= NULL*/,
8145 bool aAllowClosing /*= false*/)
8146#elif defined(RT_OS_OS2)
8147bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8148 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8149 HMTX *aIPCSem /*= NULL*/,
8150 bool aAllowClosing /*= false*/)
8151#else
8152bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8153 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8154 bool aAllowClosing /*= false*/)
8155#endif
8156{
8157 AutoLimitedCaller autoCaller(this);
8158 AssertComRCReturn(autoCaller.rc(), false);
8159
8160 /* just return false for inaccessible machines */
8161 if (autoCaller.state() != Ready)
8162 return false;
8163
8164 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8165
8166 if ( mData->mSession.mState == SessionState_Locked
8167 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8168 )
8169 {
8170 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8171
8172 aMachine = mData->mSession.mMachine;
8173
8174 if (aControl != NULL)
8175 *aControl = mData->mSession.mDirectControl;
8176
8177#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
8178 /* Additional session data */
8179 if (aIPCSem != NULL)
8180 *aIPCSem = aMachine->mIPCSem;
8181#endif
8182 return true;
8183 }
8184
8185 return false;
8186}
8187
8188/**
8189 * Returns @c true if the given machine has an spawning direct session and
8190 * returns and additional session data (on some platforms) if so.
8191 *
8192 * Note that when the method returns @c false, the arguments remain unchanged.
8193 *
8194 * @param aPID PID of the spawned direct session process.
8195 *
8196 * @note locks this object for reading.
8197 */
8198#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
8199bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
8200#else
8201bool Machine::isSessionSpawning()
8202#endif
8203{
8204 AutoLimitedCaller autoCaller(this);
8205 AssertComRCReturn(autoCaller.rc(), false);
8206
8207 /* just return false for inaccessible machines */
8208 if (autoCaller.state() != Ready)
8209 return false;
8210
8211 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8212
8213 if (mData->mSession.mState == SessionState_Spawning)
8214 {
8215#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
8216 /* Additional session data */
8217 if (aPID != NULL)
8218 {
8219 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
8220 *aPID = mData->mSession.mPID;
8221 }
8222#endif
8223 return true;
8224 }
8225
8226 return false;
8227}
8228
8229/**
8230 * Called from the client watcher thread to check for unexpected client process
8231 * death during Session_Spawning state (e.g. before it successfully opened a
8232 * direct session).
8233 *
8234 * On Win32 and on OS/2, this method is called only when we've got the
8235 * direct client's process termination notification, so it always returns @c
8236 * true.
8237 *
8238 * On other platforms, this method returns @c true if the client process is
8239 * terminated and @c false if it's still alive.
8240 *
8241 * @note Locks this object for writing.
8242 */
8243bool Machine::checkForSpawnFailure()
8244{
8245 AutoCaller autoCaller(this);
8246 if (!autoCaller.isOk())
8247 {
8248 /* nothing to do */
8249 LogFlowThisFunc(("Already uninitialized!\n"));
8250 return true;
8251 }
8252
8253 /* VirtualBox::addProcessToReap() needs a write lock */
8254 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
8255
8256 if (mData->mSession.mState != SessionState_Spawning)
8257 {
8258 /* nothing to do */
8259 LogFlowThisFunc(("Not spawning any more!\n"));
8260 return true;
8261 }
8262
8263 HRESULT rc = S_OK;
8264
8265#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
8266
8267 /* the process was already unexpectedly terminated, we just need to set an
8268 * error and finalize session spawning */
8269 rc = setError(E_FAIL,
8270 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
8271 getName().c_str());
8272#else
8273
8274 /* PID not yet initialized, skip check. */
8275 if (mData->mSession.mPID == NIL_RTPROCESS)
8276 return false;
8277
8278 RTPROCSTATUS status;
8279 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
8280 &status);
8281
8282 if (vrc != VERR_PROCESS_RUNNING)
8283 {
8284 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8285 rc = setError(E_FAIL,
8286 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
8287 getName().c_str(), status.iStatus);
8288 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8289 rc = setError(E_FAIL,
8290 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
8291 getName().c_str(), status.iStatus);
8292 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8293 rc = setError(E_FAIL,
8294 tr("The virtual machine '%s' has terminated abnormally"),
8295 getName().c_str(), status.iStatus);
8296 else
8297 rc = setError(E_FAIL,
8298 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
8299 getName().c_str(), rc);
8300 }
8301
8302#endif
8303
8304 if (FAILED(rc))
8305 {
8306 /* Close the remote session, remove the remote control from the list
8307 * and reset session state to Closed (@note keep the code in sync with
8308 * the relevant part in checkForSpawnFailure()). */
8309
8310 Assert(mData->mSession.mRemoteControls.size() == 1);
8311 if (mData->mSession.mRemoteControls.size() == 1)
8312 {
8313 ErrorInfoKeeper eik;
8314 mData->mSession.mRemoteControls.front()->Uninitialize();
8315 }
8316
8317 mData->mSession.mRemoteControls.clear();
8318 mData->mSession.mState = SessionState_Unlocked;
8319
8320 /* finalize the progress after setting the state */
8321 if (!mData->mSession.mProgress.isNull())
8322 {
8323 mData->mSession.mProgress->notifyComplete(rc);
8324 mData->mSession.mProgress.setNull();
8325 }
8326
8327 mParent->addProcessToReap(mData->mSession.mPID);
8328 mData->mSession.mPID = NIL_RTPROCESS;
8329
8330 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8331 return true;
8332 }
8333
8334 return false;
8335}
8336
8337/**
8338 * Checks whether the machine can be registered. If so, commits and saves
8339 * all settings.
8340 *
8341 * @note Must be called from mParent's write lock. Locks this object and
8342 * children for writing.
8343 */
8344HRESULT Machine::prepareRegister()
8345{
8346 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8347
8348 AutoLimitedCaller autoCaller(this);
8349 AssertComRCReturnRC(autoCaller.rc());
8350
8351 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8352
8353 /* wait for state dependents to drop to zero */
8354 ensureNoStateDependencies();
8355
8356 if (!mData->mAccessible)
8357 return setError(VBOX_E_INVALID_OBJECT_STATE,
8358 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8359 mUserData->s.strName.c_str(),
8360 mData->mUuid.toString().c_str());
8361
8362 AssertReturn(autoCaller.state() == Ready, E_FAIL);
8363
8364 if (mData->mRegistered)
8365 return setError(VBOX_E_INVALID_OBJECT_STATE,
8366 tr("The machine '%s' with UUID {%s} is already registered"),
8367 mUserData->s.strName.c_str(),
8368 mData->mUuid.toString().c_str());
8369
8370 HRESULT rc = S_OK;
8371
8372 // Ensure the settings are saved. If we are going to be registered and
8373 // no config file exists yet, create it by calling saveSettings() too.
8374 if ( (mData->flModifications)
8375 || (!mData->pMachineConfigFile->fileExists())
8376 )
8377 {
8378 rc = saveSettings(NULL);
8379 // no need to check whether VirtualBox.xml needs saving too since
8380 // we can't have a machine XML file rename pending
8381 if (FAILED(rc)) return rc;
8382 }
8383
8384 /* more config checking goes here */
8385
8386 if (SUCCEEDED(rc))
8387 {
8388 /* we may have had implicit modifications we want to fix on success */
8389 commit();
8390
8391 mData->mRegistered = true;
8392 }
8393 else
8394 {
8395 /* we may have had implicit modifications we want to cancel on failure*/
8396 rollback(false /* aNotify */);
8397 }
8398
8399 return rc;
8400}
8401
8402/**
8403 * Increases the number of objects dependent on the machine state or on the
8404 * registered state. Guarantees that these two states will not change at least
8405 * until #releaseStateDependency() is called.
8406 *
8407 * Depending on the @a aDepType value, additional state checks may be made.
8408 * These checks will set extended error info on failure. See
8409 * #checkStateDependency() for more info.
8410 *
8411 * If this method returns a failure, the dependency is not added and the caller
8412 * is not allowed to rely on any particular machine state or registration state
8413 * value and may return the failed result code to the upper level.
8414 *
8415 * @param aDepType Dependency type to add.
8416 * @param aState Current machine state (NULL if not interested).
8417 * @param aRegistered Current registered state (NULL if not interested).
8418 *
8419 * @note Locks this object for writing.
8420 */
8421HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8422 MachineState_T *aState /* = NULL */,
8423 BOOL *aRegistered /* = NULL */)
8424{
8425 AutoCaller autoCaller(this);
8426 AssertComRCReturnRC(autoCaller.rc());
8427
8428 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8429
8430 HRESULT rc = checkStateDependency(aDepType);
8431 if (FAILED(rc)) return rc;
8432
8433 {
8434 if (mData->mMachineStateChangePending != 0)
8435 {
8436 /* ensureNoStateDependencies() is waiting for state dependencies to
8437 * drop to zero so don't add more. It may make sense to wait a bit
8438 * and retry before reporting an error (since the pending state
8439 * transition should be really quick) but let's just assert for
8440 * now to see if it ever happens on practice. */
8441
8442 AssertFailed();
8443
8444 return setError(E_ACCESSDENIED,
8445 tr("Machine state change is in progress. Please retry the operation later."));
8446 }
8447
8448 ++mData->mMachineStateDeps;
8449 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8450 }
8451
8452 if (aState)
8453 *aState = mData->mMachineState;
8454 if (aRegistered)
8455 *aRegistered = mData->mRegistered;
8456
8457 return S_OK;
8458}
8459
8460/**
8461 * Decreases the number of objects dependent on the machine state.
8462 * Must always complete the #addStateDependency() call after the state
8463 * dependency is no more necessary.
8464 */
8465void Machine::releaseStateDependency()
8466{
8467 AutoCaller autoCaller(this);
8468 AssertComRCReturnVoid(autoCaller.rc());
8469
8470 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8471
8472 /* releaseStateDependency() w/o addStateDependency()? */
8473 AssertReturnVoid(mData->mMachineStateDeps != 0);
8474 -- mData->mMachineStateDeps;
8475
8476 if (mData->mMachineStateDeps == 0)
8477 {
8478 /* inform ensureNoStateDependencies() that there are no more deps */
8479 if (mData->mMachineStateChangePending != 0)
8480 {
8481 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8482 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8483 }
8484 }
8485}
8486
8487Utf8Str Machine::getExtraData(const Utf8Str &strKey)
8488{
8489 /* start with nothing found */
8490 Utf8Str strResult("");
8491
8492 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8493
8494 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8495 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8496 // found:
8497 strResult = it->second; // source is a Utf8Str
8498
8499 return strResult;
8500}
8501
8502// protected methods
8503/////////////////////////////////////////////////////////////////////////////
8504
8505/**
8506 * Performs machine state checks based on the @a aDepType value. If a check
8507 * fails, this method will set extended error info, otherwise it will return
8508 * S_OK. It is supposed, that on failure, the caller will immediately return
8509 * the return value of this method to the upper level.
8510 *
8511 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8512 *
8513 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8514 * current state of this machine object allows to change settings of the
8515 * machine (i.e. the machine is not registered, or registered but not running
8516 * and not saved). It is useful to call this method from Machine setters
8517 * before performing any change.
8518 *
8519 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8520 * as for MutableStateDep except that if the machine is saved, S_OK is also
8521 * returned. This is useful in setters which allow changing machine
8522 * properties when it is in the saved state.
8523 *
8524 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
8525 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
8526 * Aborted).
8527 *
8528 * @param aDepType Dependency type to check.
8529 *
8530 * @note Non Machine based classes should use #addStateDependency() and
8531 * #releaseStateDependency() methods or the smart AutoStateDependency
8532 * template.
8533 *
8534 * @note This method must be called from under this object's read or write
8535 * lock.
8536 */
8537HRESULT Machine::checkStateDependency(StateDependency aDepType)
8538{
8539 switch (aDepType)
8540 {
8541 case AnyStateDep:
8542 {
8543 break;
8544 }
8545 case MutableStateDep:
8546 {
8547 if ( mData->mRegistered
8548 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8549 || ( mData->mMachineState != MachineState_Paused
8550 && mData->mMachineState != MachineState_Running
8551 && mData->mMachineState != MachineState_Aborted
8552 && mData->mMachineState != MachineState_Teleported
8553 && mData->mMachineState != MachineState_PoweredOff
8554 )
8555 )
8556 )
8557 return setError(VBOX_E_INVALID_VM_STATE,
8558 tr("The machine is not mutable (state is %s)"),
8559 Global::stringifyMachineState(mData->mMachineState));
8560 break;
8561 }
8562 case MutableOrSavedStateDep:
8563 {
8564 if ( mData->mRegistered
8565 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8566 || ( mData->mMachineState != MachineState_Paused
8567 && mData->mMachineState != MachineState_Running
8568 && mData->mMachineState != MachineState_Aborted
8569 && mData->mMachineState != MachineState_Teleported
8570 && mData->mMachineState != MachineState_Saved
8571 && mData->mMachineState != MachineState_PoweredOff
8572 )
8573 )
8574 )
8575 return setError(VBOX_E_INVALID_VM_STATE,
8576 tr("The machine is not mutable (state is %s)"),
8577 Global::stringifyMachineState(mData->mMachineState));
8578 break;
8579 }
8580 case OfflineStateDep:
8581 {
8582 if ( mData->mRegistered
8583 && ( !isSessionMachine()
8584 || ( mData->mMachineState != MachineState_PoweredOff
8585 && mData->mMachineState != MachineState_Saved
8586 && mData->mMachineState != MachineState_Aborted
8587 && mData->mMachineState != MachineState_Teleported
8588 )
8589 )
8590 )
8591 return setError(VBOX_E_INVALID_VM_STATE,
8592 tr("The machine is not offline (state is %s)"),
8593 Global::stringifyMachineState(mData->mMachineState));
8594 break;
8595 }
8596 }
8597
8598 return S_OK;
8599}
8600
8601/**
8602 * Helper to initialize all associated child objects and allocate data
8603 * structures.
8604 *
8605 * This method must be called as a part of the object's initialization procedure
8606 * (usually done in the #init() method).
8607 *
8608 * @note Must be called only from #init() or from #registeredInit().
8609 */
8610HRESULT Machine::initDataAndChildObjects()
8611{
8612 AutoCaller autoCaller(this);
8613 AssertComRCReturnRC(autoCaller.rc());
8614 AssertComRCReturn(autoCaller.state() == InInit ||
8615 autoCaller.state() == Limited, E_FAIL);
8616
8617 AssertReturn(!mData->mAccessible, E_FAIL);
8618
8619 /* allocate data structures */
8620 mSSData.allocate();
8621 mUserData.allocate();
8622 mHWData.allocate();
8623 mMediaData.allocate();
8624 mStorageControllers.allocate();
8625 mUSBControllers.allocate();
8626
8627 /* initialize mOSTypeId */
8628 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
8629
8630 /* create associated BIOS settings object */
8631 unconst(mBIOSSettings).createObject();
8632 mBIOSSettings->init(this);
8633
8634 /* create an associated VRDE object (default is disabled) */
8635 unconst(mVRDEServer).createObject();
8636 mVRDEServer->init(this);
8637
8638 /* create associated serial port objects */
8639 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8640 {
8641 unconst(mSerialPorts[slot]).createObject();
8642 mSerialPorts[slot]->init(this, slot);
8643 }
8644
8645 /* create associated parallel port objects */
8646 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8647 {
8648 unconst(mParallelPorts[slot]).createObject();
8649 mParallelPorts[slot]->init(this, slot);
8650 }
8651
8652 /* create the audio adapter object (always present, default is disabled) */
8653 unconst(mAudioAdapter).createObject();
8654 mAudioAdapter->init(this);
8655
8656 /* create the USB device filters object (always present) */
8657 unconst(mUSBDeviceFilters).createObject();
8658 mUSBDeviceFilters->init(this);
8659
8660 /* create associated network adapter objects */
8661 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8662 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8663 {
8664 unconst(mNetworkAdapters[slot]).createObject();
8665 mNetworkAdapters[slot]->init(this, slot);
8666 }
8667
8668 /* create the bandwidth control */
8669 unconst(mBandwidthControl).createObject();
8670 mBandwidthControl->init(this);
8671
8672 return S_OK;
8673}
8674
8675/**
8676 * Helper to uninitialize all associated child objects and to free all data
8677 * structures.
8678 *
8679 * This method must be called as a part of the object's uninitialization
8680 * procedure (usually done in the #uninit() method).
8681 *
8682 * @note Must be called only from #uninit() or from #registeredInit().
8683 */
8684void Machine::uninitDataAndChildObjects()
8685{
8686 AutoCaller autoCaller(this);
8687 AssertComRCReturnVoid(autoCaller.rc());
8688 AssertComRCReturnVoid( autoCaller.state() == InUninit
8689 || autoCaller.state() == Limited);
8690
8691 /* tell all our other child objects we've been uninitialized */
8692 if (mBandwidthControl)
8693 {
8694 mBandwidthControl->uninit();
8695 unconst(mBandwidthControl).setNull();
8696 }
8697
8698 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8699 {
8700 if (mNetworkAdapters[slot])
8701 {
8702 mNetworkAdapters[slot]->uninit();
8703 unconst(mNetworkAdapters[slot]).setNull();
8704 }
8705 }
8706
8707 if (mUSBDeviceFilters)
8708 {
8709 mUSBDeviceFilters->uninit();
8710 unconst(mUSBDeviceFilters).setNull();
8711 }
8712
8713 if (mAudioAdapter)
8714 {
8715 mAudioAdapter->uninit();
8716 unconst(mAudioAdapter).setNull();
8717 }
8718
8719 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8720 {
8721 if (mParallelPorts[slot])
8722 {
8723 mParallelPorts[slot]->uninit();
8724 unconst(mParallelPorts[slot]).setNull();
8725 }
8726 }
8727
8728 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8729 {
8730 if (mSerialPorts[slot])
8731 {
8732 mSerialPorts[slot]->uninit();
8733 unconst(mSerialPorts[slot]).setNull();
8734 }
8735 }
8736
8737 if (mVRDEServer)
8738 {
8739 mVRDEServer->uninit();
8740 unconst(mVRDEServer).setNull();
8741 }
8742
8743 if (mBIOSSettings)
8744 {
8745 mBIOSSettings->uninit();
8746 unconst(mBIOSSettings).setNull();
8747 }
8748
8749 /* Deassociate media (only when a real Machine or a SnapshotMachine
8750 * instance is uninitialized; SessionMachine instances refer to real
8751 * Machine media). This is necessary for a clean re-initialization of
8752 * the VM after successfully re-checking the accessibility state. Note
8753 * that in case of normal Machine or SnapshotMachine uninitialization (as
8754 * a result of unregistering or deleting the snapshot), outdated media
8755 * attachments will already be uninitialized and deleted, so this
8756 * code will not affect them. */
8757 if ( !!mMediaData
8758 && (!isSessionMachine())
8759 )
8760 {
8761 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8762 it != mMediaData->mAttachments.end();
8763 ++it)
8764 {
8765 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8766 if (pMedium.isNull())
8767 continue;
8768 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8769 AssertComRC(rc);
8770 }
8771 }
8772
8773 if (!isSessionMachine() && !isSnapshotMachine())
8774 {
8775 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8776 if (mData->mFirstSnapshot)
8777 {
8778 // snapshots tree is protected by machine write lock; strictly
8779 // this isn't necessary here since we're deleting the entire
8780 // machine, but otherwise we assert in Snapshot::uninit()
8781 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8782 mData->mFirstSnapshot->uninit();
8783 mData->mFirstSnapshot.setNull();
8784 }
8785
8786 mData->mCurrentSnapshot.setNull();
8787 }
8788
8789 /* free data structures (the essential mData structure is not freed here
8790 * since it may be still in use) */
8791 mMediaData.free();
8792 mStorageControllers.free();
8793 mUSBControllers.free();
8794 mHWData.free();
8795 mUserData.free();
8796 mSSData.free();
8797}
8798
8799/**
8800 * Returns a pointer to the Machine object for this machine that acts like a
8801 * parent for complex machine data objects such as shared folders, etc.
8802 *
8803 * For primary Machine objects and for SnapshotMachine objects, returns this
8804 * object's pointer itself. For SessionMachine objects, returns the peer
8805 * (primary) machine pointer.
8806 */
8807Machine* Machine::getMachine()
8808{
8809 if (isSessionMachine())
8810 return (Machine*)mPeer;
8811 return this;
8812}
8813
8814/**
8815 * Makes sure that there are no machine state dependents. If necessary, waits
8816 * for the number of dependents to drop to zero.
8817 *
8818 * Make sure this method is called from under this object's write lock to
8819 * guarantee that no new dependents may be added when this method returns
8820 * control to the caller.
8821 *
8822 * @note Locks this object for writing. The lock will be released while waiting
8823 * (if necessary).
8824 *
8825 * @warning To be used only in methods that change the machine state!
8826 */
8827void Machine::ensureNoStateDependencies()
8828{
8829 AssertReturnVoid(isWriteLockOnCurrentThread());
8830
8831 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8832
8833 /* Wait for all state dependents if necessary */
8834 if (mData->mMachineStateDeps != 0)
8835 {
8836 /* lazy semaphore creation */
8837 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8838 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8839
8840 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8841 mData->mMachineStateDeps));
8842
8843 ++mData->mMachineStateChangePending;
8844
8845 /* reset the semaphore before waiting, the last dependent will signal
8846 * it */
8847 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8848
8849 alock.release();
8850
8851 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8852
8853 alock.acquire();
8854
8855 -- mData->mMachineStateChangePending;
8856 }
8857}
8858
8859/**
8860 * Changes the machine state and informs callbacks.
8861 *
8862 * This method is not intended to fail so it either returns S_OK or asserts (and
8863 * returns a failure).
8864 *
8865 * @note Locks this object for writing.
8866 */
8867HRESULT Machine::setMachineState(MachineState_T aMachineState)
8868{
8869 LogFlowThisFuncEnter();
8870 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8871
8872 AutoCaller autoCaller(this);
8873 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8874
8875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8876
8877 /* wait for state dependents to drop to zero */
8878 ensureNoStateDependencies();
8879
8880 if (mData->mMachineState != aMachineState)
8881 {
8882 mData->mMachineState = aMachineState;
8883
8884 RTTimeNow(&mData->mLastStateChange);
8885
8886 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8887 }
8888
8889 LogFlowThisFuncLeave();
8890 return S_OK;
8891}
8892
8893/**
8894 * Searches for a shared folder with the given logical name
8895 * in the collection of shared folders.
8896 *
8897 * @param aName logical name of the shared folder
8898 * @param aSharedFolder where to return the found object
8899 * @param aSetError whether to set the error info if the folder is
8900 * not found
8901 * @return
8902 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8903 *
8904 * @note
8905 * must be called from under the object's lock!
8906 */
8907HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8908 ComObjPtr<SharedFolder> &aSharedFolder,
8909 bool aSetError /* = false */)
8910{
8911 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8912 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8913 it != mHWData->mSharedFolders.end();
8914 ++it)
8915 {
8916 SharedFolder *pSF = *it;
8917 AutoCaller autoCaller(pSF);
8918 if (pSF->getName() == aName)
8919 {
8920 aSharedFolder = pSF;
8921 rc = S_OK;
8922 break;
8923 }
8924 }
8925
8926 if (aSetError && FAILED(rc))
8927 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8928
8929 return rc;
8930}
8931
8932/**
8933 * Initializes all machine instance data from the given settings structures
8934 * from XML. The exception is the machine UUID which needs special handling
8935 * depending on the caller's use case, so the caller needs to set that herself.
8936 *
8937 * This gets called in several contexts during machine initialization:
8938 *
8939 * -- When machine XML exists on disk already and needs to be loaded into memory,
8940 * for example, from registeredInit() to load all registered machines on
8941 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8942 * attached to the machine should be part of some media registry already.
8943 *
8944 * -- During OVF import, when a machine config has been constructed from an
8945 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8946 * ensure that the media listed as attachments in the config (which have
8947 * been imported from the OVF) receive the correct registry ID.
8948 *
8949 * -- During VM cloning.
8950 *
8951 * @param config Machine settings from XML.
8952 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8953 * @return
8954 */
8955HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8956 const Guid *puuidRegistry)
8957{
8958 // copy name, description, OS type, teleporter, UTC etc.
8959 #define DECODE_STR_MAX _1M
8960 mUserData->s = config.machineUserData;
8961
8962 // Decode the Icon overide data from config userdata and set onto Machine.
8963 const char* pszStr = config.machineUserData.ovIcon.c_str();
8964 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8965 if (cbOut > DECODE_STR_MAX)
8966 return setError(E_FAIL,
8967 tr("Icon Data too long.'%d' > '%d'"),
8968 cbOut,
8969 DECODE_STR_MAX);
8970 com::SafeArray<BYTE> iconByte(cbOut);
8971 HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
8972 if (FAILED(rc))
8973 return setError(E_FAIL,
8974 tr("Failure to Decode Icon Data. '%s' (%d)"),
8975 pszStr,
8976 rc);
8977 COMSETTER(Icon)(ComSafeArrayAsInParam(iconByte));
8978
8979 // look up the object by Id to check it is valid
8980 ComPtr<IGuestOSType> guestOSType;
8981 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8982 guestOSType.asOutParam());
8983 if (FAILED(rc)) return rc;
8984
8985 // stateFile (optional)
8986 if (config.strStateFile.isEmpty())
8987 mSSData->strStateFilePath.setNull();
8988 else
8989 {
8990 Utf8Str stateFilePathFull(config.strStateFile);
8991 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8992 if (RT_FAILURE(vrc))
8993 return setError(E_FAIL,
8994 tr("Invalid saved state file path '%s' (%Rrc)"),
8995 config.strStateFile.c_str(),
8996 vrc);
8997 mSSData->strStateFilePath = stateFilePathFull;
8998 }
8999
9000 // snapshot folder needs special processing so set it again
9001 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
9002 if (FAILED(rc)) return rc;
9003
9004 /* Copy the extra data items (Not in any case config is already the same as
9005 * mData->pMachineConfigFile, like when the xml files are read from disk. So
9006 * make sure the extra data map is copied). */
9007 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
9008
9009 /* currentStateModified (optional, default is true) */
9010 mData->mCurrentStateModified = config.fCurrentStateModified;
9011
9012 mData->mLastStateChange = config.timeLastStateChange;
9013
9014 /*
9015 * note: all mUserData members must be assigned prior this point because
9016 * we need to commit changes in order to let mUserData be shared by all
9017 * snapshot machine instances.
9018 */
9019 mUserData.commitCopy();
9020
9021 // machine registry, if present (must be loaded before snapshots)
9022 if (config.canHaveOwnMediaRegistry())
9023 {
9024 // determine machine folder
9025 Utf8Str strMachineFolder = getSettingsFileFull();
9026 strMachineFolder.stripFilename();
9027 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
9028 config.mediaRegistry,
9029 strMachineFolder);
9030 if (FAILED(rc)) return rc;
9031 }
9032
9033 /* Snapshot node (optional) */
9034 size_t cRootSnapshots;
9035 if ((cRootSnapshots = config.llFirstSnapshot.size()))
9036 {
9037 // there must be only one root snapshot
9038 Assert(cRootSnapshots == 1);
9039
9040 const settings::Snapshot &snap = config.llFirstSnapshot.front();
9041
9042 rc = loadSnapshot(snap,
9043 config.uuidCurrentSnapshot,
9044 NULL); // no parent == first snapshot
9045 if (FAILED(rc)) return rc;
9046 }
9047
9048 // hardware data
9049 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9050 if (FAILED(rc)) return rc;
9051
9052 // load storage controllers
9053 rc = loadStorageControllers(config.storageMachine,
9054 puuidRegistry,
9055 NULL /* puuidSnapshot */);
9056 if (FAILED(rc)) return rc;
9057
9058 /*
9059 * NOTE: the assignment below must be the last thing to do,
9060 * otherwise it will be not possible to change the settings
9061 * somewhere in the code above because all setters will be
9062 * blocked by checkStateDependency(MutableStateDep).
9063 */
9064
9065 /* set the machine state to Aborted or Saved when appropriate */
9066 if (config.fAborted)
9067 {
9068 mSSData->strStateFilePath.setNull();
9069
9070 /* no need to use setMachineState() during init() */
9071 mData->mMachineState = MachineState_Aborted;
9072 }
9073 else if (!mSSData->strStateFilePath.isEmpty())
9074 {
9075 /* no need to use setMachineState() during init() */
9076 mData->mMachineState = MachineState_Saved;
9077 }
9078
9079 // after loading settings, we are no longer different from the XML on disk
9080 mData->flModifications = 0;
9081
9082 return S_OK;
9083}
9084
9085/**
9086 * Recursively loads all snapshots starting from the given.
9087 *
9088 * @param aNode <Snapshot> node.
9089 * @param aCurSnapshotId Current snapshot ID from the settings file.
9090 * @param aParentSnapshot Parent snapshot.
9091 */
9092HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
9093 const Guid &aCurSnapshotId,
9094 Snapshot *aParentSnapshot)
9095{
9096 AssertReturn(!isSnapshotMachine(), E_FAIL);
9097 AssertReturn(!isSessionMachine(), E_FAIL);
9098
9099 HRESULT rc = S_OK;
9100
9101 Utf8Str strStateFile;
9102 if (!data.strStateFile.isEmpty())
9103 {
9104 /* optional */
9105 strStateFile = data.strStateFile;
9106 int vrc = calculateFullPath(strStateFile, strStateFile);
9107 if (RT_FAILURE(vrc))
9108 return setError(E_FAIL,
9109 tr("Invalid saved state file path '%s' (%Rrc)"),
9110 strStateFile.c_str(),
9111 vrc);
9112 }
9113
9114 /* create a snapshot machine object */
9115 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9116 pSnapshotMachine.createObject();
9117 rc = pSnapshotMachine->initFromSettings(this,
9118 data.hardware,
9119 &data.debugging,
9120 &data.autostart,
9121 data.storage,
9122 data.uuid.ref(),
9123 strStateFile);
9124 if (FAILED(rc)) return rc;
9125
9126 /* create a snapshot object */
9127 ComObjPtr<Snapshot> pSnapshot;
9128 pSnapshot.createObject();
9129 /* initialize the snapshot */
9130 rc = pSnapshot->init(mParent, // VirtualBox object
9131 data.uuid,
9132 data.strName,
9133 data.strDescription,
9134 data.timestamp,
9135 pSnapshotMachine,
9136 aParentSnapshot);
9137 if (FAILED(rc)) return rc;
9138
9139 /* memorize the first snapshot if necessary */
9140 if (!mData->mFirstSnapshot)
9141 mData->mFirstSnapshot = pSnapshot;
9142
9143 /* memorize the current snapshot when appropriate */
9144 if ( !mData->mCurrentSnapshot
9145 && pSnapshot->getId() == aCurSnapshotId
9146 )
9147 mData->mCurrentSnapshot = pSnapshot;
9148
9149 // now create the children
9150 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
9151 it != data.llChildSnapshots.end();
9152 ++it)
9153 {
9154 const settings::Snapshot &childData = *it;
9155 // recurse
9156 rc = loadSnapshot(childData,
9157 aCurSnapshotId,
9158 pSnapshot); // parent = the one we created above
9159 if (FAILED(rc)) return rc;
9160 }
9161
9162 return rc;
9163}
9164
9165/**
9166 * Loads settings into mHWData.
9167 *
9168 * @param data Reference to the hardware settings.
9169 * @param pDbg Pointer to the debugging settings.
9170 * @param pAutostart Pointer to the autostart settings.
9171 */
9172HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
9173 const settings::Autostart *pAutostart)
9174{
9175 AssertReturn(!isSessionMachine(), E_FAIL);
9176
9177 HRESULT rc = S_OK;
9178
9179 try
9180 {
9181 /* The hardware version attribute (optional). */
9182 mHWData->mHWVersion = data.strVersion;
9183 mHWData->mHardwareUUID = data.uuid;
9184
9185 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9186 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
9187 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9188 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9189 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9190 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9191 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9192 mHWData->mPAEEnabled = data.fPAE;
9193 mHWData->mSyntheticCpu = data.fSyntheticCpu;
9194 mHWData->mLongMode = data.enmLongMode;
9195 mHWData->mCPUCount = data.cCPUs;
9196 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9197 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9198
9199 // cpu
9200 if (mHWData->mCPUHotPlugEnabled)
9201 {
9202 for (settings::CpuList::const_iterator it = data.llCpus.begin();
9203 it != data.llCpus.end();
9204 ++it)
9205 {
9206 const settings::Cpu &cpu = *it;
9207
9208 mHWData->mCPUAttached[cpu.ulId] = true;
9209 }
9210 }
9211
9212 // cpuid leafs
9213 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
9214 it != data.llCpuIdLeafs.end();
9215 ++it)
9216 {
9217 const settings::CpuIdLeaf &leaf = *it;
9218
9219 switch (leaf.ulId)
9220 {
9221 case 0x0:
9222 case 0x1:
9223 case 0x2:
9224 case 0x3:
9225 case 0x4:
9226 case 0x5:
9227 case 0x6:
9228 case 0x7:
9229 case 0x8:
9230 case 0x9:
9231 case 0xA:
9232 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
9233 break;
9234
9235 case 0x80000000:
9236 case 0x80000001:
9237 case 0x80000002:
9238 case 0x80000003:
9239 case 0x80000004:
9240 case 0x80000005:
9241 case 0x80000006:
9242 case 0x80000007:
9243 case 0x80000008:
9244 case 0x80000009:
9245 case 0x8000000A:
9246 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
9247 break;
9248
9249 default:
9250 /* just ignore */
9251 break;
9252 }
9253 }
9254
9255 mHWData->mMemorySize = data.ulMemorySizeMB;
9256 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9257
9258 // boot order
9259 for (size_t i = 0;
9260 i < RT_ELEMENTS(mHWData->mBootOrder);
9261 i++)
9262 {
9263 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9264 if (it == data.mapBootOrder.end())
9265 mHWData->mBootOrder[i] = DeviceType_Null;
9266 else
9267 mHWData->mBootOrder[i] = it->second;
9268 }
9269
9270 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9271 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9272 mHWData->mMonitorCount = data.cMonitors;
9273 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9274 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9275 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9276 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9277 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9278 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); i++)
9279 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9280 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9281 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9282 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9283 if (!data.strVideoCaptureFile.isEmpty())
9284 calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9285 else
9286 mHWData->mVideoCaptureFile.setNull();
9287 mHWData->mFirmwareType = data.firmwareType;
9288 mHWData->mPointingHIDType = data.pointingHIDType;
9289 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9290 mHWData->mChipsetType = data.chipsetType;
9291 mHWData->mEmulatedUSBWebcamEnabled = data.fEmulatedUSBWebcam;
9292 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9293 mHWData->mHPETEnabled = data.fHPETEnabled;
9294
9295 /* VRDEServer */
9296 rc = mVRDEServer->loadSettings(data.vrdeSettings);
9297 if (FAILED(rc)) return rc;
9298
9299 /* BIOS */
9300 rc = mBIOSSettings->loadSettings(data.biosSettings);
9301 if (FAILED(rc)) return rc;
9302
9303 // Bandwidth control (must come before network adapters)
9304 rc = mBandwidthControl->loadSettings(data.ioSettings);
9305 if (FAILED(rc)) return rc;
9306
9307 /* Shared folders */
9308 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
9309 it != data.usbSettings.llUSBControllers.end();
9310 ++it)
9311 {
9312 const settings::USBController &settingsCtrl = *it;
9313 ComObjPtr<USBController> newCtrl;
9314
9315 newCtrl.createObject();
9316 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9317 mUSBControllers->push_back(newCtrl);
9318 }
9319
9320 /* USB device filters */
9321 rc = mUSBDeviceFilters->loadSettings(data.usbSettings);
9322 if (FAILED(rc)) return rc;
9323
9324 // network adapters
9325 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9326 uint32_t oldCount = mNetworkAdapters.size();
9327 if (newCount > oldCount)
9328 {
9329 mNetworkAdapters.resize(newCount);
9330 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
9331 {
9332 unconst(mNetworkAdapters[slot]).createObject();
9333 mNetworkAdapters[slot]->init(this, slot);
9334 }
9335 }
9336 else if (newCount < oldCount)
9337 mNetworkAdapters.resize(newCount);
9338 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
9339 it != data.llNetworkAdapters.end();
9340 ++it)
9341 {
9342 const settings::NetworkAdapter &nic = *it;
9343
9344 /* slot unicity is guaranteed by XML Schema */
9345 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9346 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
9347 if (FAILED(rc)) return rc;
9348 }
9349
9350 // serial ports
9351 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
9352 it != data.llSerialPorts.end();
9353 ++it)
9354 {
9355 const settings::SerialPort &s = *it;
9356
9357 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9358 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
9359 if (FAILED(rc)) return rc;
9360 }
9361
9362 // parallel ports (optional)
9363 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
9364 it != data.llParallelPorts.end();
9365 ++it)
9366 {
9367 const settings::ParallelPort &p = *it;
9368
9369 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9370 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
9371 if (FAILED(rc)) return rc;
9372 }
9373
9374 /* AudioAdapter */
9375 rc = mAudioAdapter->loadSettings(data.audioAdapter);
9376 if (FAILED(rc)) return rc;
9377
9378 /* Shared folders */
9379 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9380 it != data.llSharedFolders.end();
9381 ++it)
9382 {
9383 const settings::SharedFolder &sf = *it;
9384
9385 ComObjPtr<SharedFolder> sharedFolder;
9386 /* Check for double entries. Not allowed! */
9387 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9388 if (SUCCEEDED(rc))
9389 return setError(VBOX_E_OBJECT_IN_USE,
9390 tr("Shared folder named '%s' already exists"),
9391 sf.strName.c_str());
9392
9393 /* Create the new shared folder. Don't break on error. This will be
9394 * reported when the machine starts. */
9395 sharedFolder.createObject();
9396 rc = sharedFolder->init(getMachine(),
9397 sf.strName,
9398 sf.strHostPath,
9399 RT_BOOL(sf.fWritable),
9400 RT_BOOL(sf.fAutoMount),
9401 false /* fFailOnError */);
9402 if (FAILED(rc)) return rc;
9403 mHWData->mSharedFolders.push_back(sharedFolder);
9404 }
9405
9406 // Clipboard
9407 mHWData->mClipboardMode = data.clipboardMode;
9408
9409 // drag'n'drop
9410 mHWData->mDragAndDropMode = data.dragAndDropMode;
9411
9412 // guest settings
9413 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9414
9415 // IO settings
9416 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9417 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9418
9419 // Host PCI devices
9420 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9421 it != data.pciAttachments.end();
9422 ++it)
9423 {
9424 const settings::HostPCIDeviceAttachment &hpda = *it;
9425 ComObjPtr<PCIDeviceAttachment> pda;
9426
9427 pda.createObject();
9428 pda->loadSettings(this, hpda);
9429 mHWData->mPCIDeviceAssignments.push_back(pda);
9430 }
9431
9432 /*
9433 * (The following isn't really real hardware, but it lives in HWData
9434 * for reasons of convenience.)
9435 */
9436
9437#ifdef VBOX_WITH_GUEST_PROPS
9438 /* Guest properties (optional) */
9439 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9440 it != data.llGuestProperties.end();
9441 ++it)
9442 {
9443 const settings::GuestProperty &prop = *it;
9444 uint32_t fFlags = guestProp::NILFLAG;
9445 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9446 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9447 mHWData->mGuestProperties[prop.strName] = property;
9448 }
9449
9450 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9451#endif /* VBOX_WITH_GUEST_PROPS defined */
9452
9453 rc = loadDebugging(pDbg);
9454 if (FAILED(rc))
9455 return rc;
9456
9457 mHWData->mAutostart = *pAutostart;
9458
9459 /* default frontend */
9460 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9461 }
9462 catch(std::bad_alloc &)
9463 {
9464 return E_OUTOFMEMORY;
9465 }
9466
9467 AssertComRC(rc);
9468 return rc;
9469}
9470
9471/**
9472 * Called from Machine::loadHardware() to load the debugging settings of the
9473 * machine.
9474 *
9475 * @param pDbg Pointer to the settings.
9476 */
9477HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
9478{
9479 mHWData->mDebugging = *pDbg;
9480 /* no more processing currently required, this will probably change. */
9481 return S_OK;
9482}
9483
9484/**
9485 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
9486 *
9487 * @param data
9488 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9489 * @param puuidSnapshot
9490 * @return
9491 */
9492HRESULT Machine::loadStorageControllers(const settings::Storage &data,
9493 const Guid *puuidRegistry,
9494 const Guid *puuidSnapshot)
9495{
9496 AssertReturn(!isSessionMachine(), E_FAIL);
9497
9498 HRESULT rc = S_OK;
9499
9500 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9501 it != data.llStorageControllers.end();
9502 ++it)
9503 {
9504 const settings::StorageController &ctlData = *it;
9505
9506 ComObjPtr<StorageController> pCtl;
9507 /* Try to find one with the name first. */
9508 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9509 if (SUCCEEDED(rc))
9510 return setError(VBOX_E_OBJECT_IN_USE,
9511 tr("Storage controller named '%s' already exists"),
9512 ctlData.strName.c_str());
9513
9514 pCtl.createObject();
9515 rc = pCtl->init(this,
9516 ctlData.strName,
9517 ctlData.storageBus,
9518 ctlData.ulInstance,
9519 ctlData.fBootable);
9520 if (FAILED(rc)) return rc;
9521
9522 mStorageControllers->push_back(pCtl);
9523
9524 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9525 if (FAILED(rc)) return rc;
9526
9527 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9528 if (FAILED(rc)) return rc;
9529
9530 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9531 if (FAILED(rc)) return rc;
9532
9533 /* Set IDE emulation settings (only for AHCI controller). */
9534 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9535 {
9536 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9537 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9538 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9539 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9540 )
9541 return rc;
9542 }
9543
9544 /* Load the attached devices now. */
9545 rc = loadStorageDevices(pCtl,
9546 ctlData,
9547 puuidRegistry,
9548 puuidSnapshot);
9549 if (FAILED(rc)) return rc;
9550 }
9551
9552 return S_OK;
9553}
9554
9555/**
9556 * Called from loadStorageControllers for a controller's devices.
9557 *
9558 * @param aStorageController
9559 * @param data
9560 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9561 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9562 * @return
9563 */
9564HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
9565 const settings::StorageController &data,
9566 const Guid *puuidRegistry,
9567 const Guid *puuidSnapshot)
9568{
9569 HRESULT rc = S_OK;
9570
9571 /* paranoia: detect duplicate attachments */
9572 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9573 it != data.llAttachedDevices.end();
9574 ++it)
9575 {
9576 const settings::AttachedDevice &ad = *it;
9577
9578 for (settings::AttachedDevicesList::const_iterator it2 = it;
9579 it2 != data.llAttachedDevices.end();
9580 ++it2)
9581 {
9582 if (it == it2)
9583 continue;
9584
9585 const settings::AttachedDevice &ad2 = *it2;
9586
9587 if ( ad.lPort == ad2.lPort
9588 && ad.lDevice == ad2.lDevice)
9589 {
9590 return setError(E_FAIL,
9591 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9592 aStorageController->getName().c_str(),
9593 ad.lPort,
9594 ad.lDevice,
9595 mUserData->s.strName.c_str());
9596 }
9597 }
9598 }
9599
9600 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9601 it != data.llAttachedDevices.end();
9602 ++it)
9603 {
9604 const settings::AttachedDevice &dev = *it;
9605 ComObjPtr<Medium> medium;
9606
9607 switch (dev.deviceType)
9608 {
9609 case DeviceType_Floppy:
9610 case DeviceType_DVD:
9611 if (dev.strHostDriveSrc.isNotEmpty())
9612 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9613 else
9614 rc = mParent->findRemoveableMedium(dev.deviceType,
9615 dev.uuid,
9616 false /* fRefresh */,
9617 false /* aSetError */,
9618 medium);
9619 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9620 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9621 rc = S_OK;
9622 break;
9623
9624 case DeviceType_HardDisk:
9625 {
9626 /* find a hard disk by UUID */
9627 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9628 if (FAILED(rc))
9629 {
9630 if (isSnapshotMachine())
9631 {
9632 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9633 // so the user knows that the bad disk is in a snapshot somewhere
9634 com::ErrorInfo info;
9635 return setError(E_FAIL,
9636 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9637 puuidSnapshot->raw(),
9638 info.getText().raw());
9639 }
9640 else
9641 return rc;
9642 }
9643
9644 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9645
9646 if (medium->getType() == MediumType_Immutable)
9647 {
9648 if (isSnapshotMachine())
9649 return setError(E_FAIL,
9650 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9651 "of the virtual machine '%s' ('%s')"),
9652 medium->getLocationFull().c_str(),
9653 dev.uuid.raw(),
9654 puuidSnapshot->raw(),
9655 mUserData->s.strName.c_str(),
9656 mData->m_strConfigFileFull.c_str());
9657
9658 return setError(E_FAIL,
9659 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9660 medium->getLocationFull().c_str(),
9661 dev.uuid.raw(),
9662 mUserData->s.strName.c_str(),
9663 mData->m_strConfigFileFull.c_str());
9664 }
9665
9666 if (medium->getType() == MediumType_MultiAttach)
9667 {
9668 if (isSnapshotMachine())
9669 return setError(E_FAIL,
9670 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9671 "of the virtual machine '%s' ('%s')"),
9672 medium->getLocationFull().c_str(),
9673 dev.uuid.raw(),
9674 puuidSnapshot->raw(),
9675 mUserData->s.strName.c_str(),
9676 mData->m_strConfigFileFull.c_str());
9677
9678 return setError(E_FAIL,
9679 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9680 medium->getLocationFull().c_str(),
9681 dev.uuid.raw(),
9682 mUserData->s.strName.c_str(),
9683 mData->m_strConfigFileFull.c_str());
9684 }
9685
9686 if ( !isSnapshotMachine()
9687 && medium->getChildren().size() != 0
9688 )
9689 return setError(E_FAIL,
9690 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9691 "because it has %d differencing child hard disks"),
9692 medium->getLocationFull().c_str(),
9693 dev.uuid.raw(),
9694 mUserData->s.strName.c_str(),
9695 mData->m_strConfigFileFull.c_str(),
9696 medium->getChildren().size());
9697
9698 if (findAttachment(mMediaData->mAttachments,
9699 medium))
9700 return setError(E_FAIL,
9701 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9702 medium->getLocationFull().c_str(),
9703 dev.uuid.raw(),
9704 mUserData->s.strName.c_str(),
9705 mData->m_strConfigFileFull.c_str());
9706
9707 break;
9708 }
9709
9710 default:
9711 return setError(E_FAIL,
9712 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9713 medium->getLocationFull().c_str(),
9714 mUserData->s.strName.c_str(),
9715 mData->m_strConfigFileFull.c_str());
9716 }
9717
9718 if (FAILED(rc))
9719 break;
9720
9721 /* Bandwidth groups are loaded at this point. */
9722 ComObjPtr<BandwidthGroup> pBwGroup;
9723
9724 if (!dev.strBwGroup.isEmpty())
9725 {
9726 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9727 if (FAILED(rc))
9728 return setError(E_FAIL,
9729 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9730 medium->getLocationFull().c_str(),
9731 dev.strBwGroup.c_str(),
9732 mUserData->s.strName.c_str(),
9733 mData->m_strConfigFileFull.c_str());
9734 pBwGroup->reference();
9735 }
9736
9737 const Bstr controllerName = aStorageController->getName();
9738 ComObjPtr<MediumAttachment> pAttachment;
9739 pAttachment.createObject();
9740 rc = pAttachment->init(this,
9741 medium,
9742 controllerName,
9743 dev.lPort,
9744 dev.lDevice,
9745 dev.deviceType,
9746 false,
9747 dev.fPassThrough,
9748 dev.fTempEject,
9749 dev.fNonRotational,
9750 dev.fDiscard,
9751 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
9752 if (FAILED(rc)) break;
9753
9754 /* associate the medium with this machine and snapshot */
9755 if (!medium.isNull())
9756 {
9757 AutoCaller medCaller(medium);
9758 if (FAILED(medCaller.rc())) return medCaller.rc();
9759 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9760
9761 if (isSnapshotMachine())
9762 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
9763 else
9764 rc = medium->addBackReference(mData->mUuid);
9765 /* If the medium->addBackReference fails it sets an appropriate
9766 * error message, so no need to do any guesswork here. */
9767
9768 if (puuidRegistry)
9769 // caller wants registry ID to be set on all attached media (OVF import case)
9770 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9771 }
9772
9773 if (FAILED(rc))
9774 break;
9775
9776 /* back up mMediaData to let registeredInit() properly rollback on failure
9777 * (= limited accessibility) */
9778 setModified(IsModified_Storage);
9779 mMediaData.backup();
9780 mMediaData->mAttachments.push_back(pAttachment);
9781 }
9782
9783 return rc;
9784}
9785
9786/**
9787 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9788 *
9789 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9790 * @param aSnapshot where to return the found snapshot
9791 * @param aSetError true to set extended error info on failure
9792 */
9793HRESULT Machine::findSnapshotById(const Guid &aId,
9794 ComObjPtr<Snapshot> &aSnapshot,
9795 bool aSetError /* = false */)
9796{
9797 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9798
9799 if (!mData->mFirstSnapshot)
9800 {
9801 if (aSetError)
9802 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9803 return E_FAIL;
9804 }
9805
9806 if (aId.isZero())
9807 aSnapshot = mData->mFirstSnapshot;
9808 else
9809 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9810
9811 if (!aSnapshot)
9812 {
9813 if (aSetError)
9814 return setError(E_FAIL,
9815 tr("Could not find a snapshot with UUID {%s}"),
9816 aId.toString().c_str());
9817 return E_FAIL;
9818 }
9819
9820 return S_OK;
9821}
9822
9823/**
9824 * Returns the snapshot with the given name or fails of no such snapshot.
9825 *
9826 * @param aName snapshot name to find
9827 * @param aSnapshot where to return the found snapshot
9828 * @param aSetError true to set extended error info on failure
9829 */
9830HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9831 ComObjPtr<Snapshot> &aSnapshot,
9832 bool aSetError /* = false */)
9833{
9834 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9835
9836 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9837
9838 if (!mData->mFirstSnapshot)
9839 {
9840 if (aSetError)
9841 return setError(VBOX_E_OBJECT_NOT_FOUND,
9842 tr("This machine does not have any snapshots"));
9843 return VBOX_E_OBJECT_NOT_FOUND;
9844 }
9845
9846 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9847
9848 if (!aSnapshot)
9849 {
9850 if (aSetError)
9851 return setError(VBOX_E_OBJECT_NOT_FOUND,
9852 tr("Could not find a snapshot named '%s'"), strName.c_str());
9853 return VBOX_E_OBJECT_NOT_FOUND;
9854 }
9855
9856 return S_OK;
9857}
9858
9859/**
9860 * Returns a storage controller object with the given name.
9861 *
9862 * @param aName storage controller name to find
9863 * @param aStorageController where to return the found storage controller
9864 * @param aSetError true to set extended error info on failure
9865 */
9866HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9867 ComObjPtr<StorageController> &aStorageController,
9868 bool aSetError /* = false */)
9869{
9870 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9871
9872 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9873 it != mStorageControllers->end();
9874 ++it)
9875 {
9876 if ((*it)->getName() == aName)
9877 {
9878 aStorageController = (*it);
9879 return S_OK;
9880 }
9881 }
9882
9883 if (aSetError)
9884 return setError(VBOX_E_OBJECT_NOT_FOUND,
9885 tr("Could not find a storage controller named '%s'"),
9886 aName.c_str());
9887 return VBOX_E_OBJECT_NOT_FOUND;
9888}
9889
9890/**
9891 * Returns a USB controller object with the given name.
9892 *
9893 * @param aName USB controller name to find
9894 * @param aUSBController where to return the found USB controller
9895 * @param aSetError true to set extended error info on failure
9896 */
9897HRESULT Machine::getUSBControllerByName(const Utf8Str &aName,
9898 ComObjPtr<USBController> &aUSBController,
9899 bool aSetError /* = false */)
9900{
9901 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9902
9903 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9904 it != mUSBControllers->end();
9905 ++it)
9906 {
9907 if ((*it)->getName() == aName)
9908 {
9909 aUSBController = (*it);
9910 return S_OK;
9911 }
9912 }
9913
9914 if (aSetError)
9915 return setError(VBOX_E_OBJECT_NOT_FOUND,
9916 tr("Could not find a storage controller named '%s'"),
9917 aName.c_str());
9918 return VBOX_E_OBJECT_NOT_FOUND;
9919}
9920
9921/**
9922 * Returns the number of USB controller instance of the given type.
9923 *
9924 * @param enmType USB controller type.
9925 */
9926ULONG Machine::getUSBControllerCountByType(USBControllerType_T enmType)
9927{
9928 ULONG cCtrls = 0;
9929
9930 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9931 it != mUSBControllers->end();
9932 ++it)
9933 {
9934 if ((*it)->getControllerType() == enmType)
9935 cCtrls++;
9936 }
9937
9938 return cCtrls;
9939}
9940
9941HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9942 MediaData::AttachmentList &atts)
9943{
9944 AutoCaller autoCaller(this);
9945 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9946
9947 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9948
9949 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9950 it != mMediaData->mAttachments.end();
9951 ++it)
9952 {
9953 const ComObjPtr<MediumAttachment> &pAtt = *it;
9954
9955 // should never happen, but deal with NULL pointers in the list.
9956 AssertStmt(!pAtt.isNull(), continue);
9957
9958 // getControllerName() needs caller+read lock
9959 AutoCaller autoAttCaller(pAtt);
9960 if (FAILED(autoAttCaller.rc()))
9961 {
9962 atts.clear();
9963 return autoAttCaller.rc();
9964 }
9965 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9966
9967 if (pAtt->getControllerName() == aName)
9968 atts.push_back(pAtt);
9969 }
9970
9971 return S_OK;
9972}
9973
9974/**
9975 * Helper for #saveSettings. Cares about renaming the settings directory and
9976 * file if the machine name was changed and about creating a new settings file
9977 * if this is a new machine.
9978 *
9979 * @note Must be never called directly but only from #saveSettings().
9980 */
9981HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9982{
9983 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9984
9985 HRESULT rc = S_OK;
9986
9987 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9988
9989 /// @todo need to handle primary group change, too
9990
9991 /* attempt to rename the settings file if machine name is changed */
9992 if ( mUserData->s.fNameSync
9993 && mUserData.isBackedUp()
9994 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9995 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9996 )
9997 {
9998 bool dirRenamed = false;
9999 bool fileRenamed = false;
10000
10001 Utf8Str configFile, newConfigFile;
10002 Utf8Str configFilePrev, newConfigFilePrev;
10003 Utf8Str configDir, newConfigDir;
10004
10005 do
10006 {
10007 int vrc = VINF_SUCCESS;
10008
10009 Utf8Str name = mUserData.backedUpData()->s.strName;
10010 Utf8Str newName = mUserData->s.strName;
10011 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
10012 if (group == "/")
10013 group.setNull();
10014 Utf8Str newGroup = mUserData->s.llGroups.front();
10015 if (newGroup == "/")
10016 newGroup.setNull();
10017
10018 configFile = mData->m_strConfigFileFull;
10019
10020 /* first, rename the directory if it matches the group and machine name */
10021 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
10022 group.c_str(), RTPATH_DELIMITER, name.c_str());
10023 /** @todo hack, make somehow use of ComposeMachineFilename */
10024 if (mUserData->s.fDirectoryIncludesUUID)
10025 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
10026 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
10027 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
10028 /** @todo hack, make somehow use of ComposeMachineFilename */
10029 if (mUserData->s.fDirectoryIncludesUUID)
10030 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
10031 configDir = configFile;
10032 configDir.stripFilename();
10033 newConfigDir = configDir;
10034 if ( configDir.length() >= groupPlusName.length()
10035 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
10036 {
10037 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
10038 Utf8Str newConfigBaseDir(newConfigDir);
10039 newConfigDir.append(newGroupPlusName);
10040 /* consistency: use \ if appropriate on the platform */
10041 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
10042 /* new dir and old dir cannot be equal here because of 'if'
10043 * above and because name != newName */
10044 Assert(configDir != newConfigDir);
10045 if (!fSettingsFileIsNew)
10046 {
10047 /* perform real rename only if the machine is not new */
10048 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10049 if ( vrc == VERR_FILE_NOT_FOUND
10050 || vrc == VERR_PATH_NOT_FOUND)
10051 {
10052 /* create the parent directory, then retry renaming */
10053 Utf8Str parent(newConfigDir);
10054 parent.stripFilename();
10055 (void)RTDirCreateFullPath(parent.c_str(), 0700);
10056 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10057 }
10058 if (RT_FAILURE(vrc))
10059 {
10060 rc = setError(E_FAIL,
10061 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
10062 configDir.c_str(),
10063 newConfigDir.c_str(),
10064 vrc);
10065 break;
10066 }
10067 /* delete subdirectories which are no longer needed */
10068 Utf8Str dir(configDir);
10069 dir.stripFilename();
10070 while (dir != newConfigBaseDir && dir != ".")
10071 {
10072 vrc = RTDirRemove(dir.c_str());
10073 if (RT_FAILURE(vrc))
10074 break;
10075 dir.stripFilename();
10076 }
10077 dirRenamed = true;
10078 }
10079 }
10080
10081 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
10082 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10083
10084 /* then try to rename the settings file itself */
10085 if (newConfigFile != configFile)
10086 {
10087 /* get the path to old settings file in renamed directory */
10088 configFile = Utf8StrFmt("%s%c%s",
10089 newConfigDir.c_str(),
10090 RTPATH_DELIMITER,
10091 RTPathFilename(configFile.c_str()));
10092 if (!fSettingsFileIsNew)
10093 {
10094 /* perform real rename only if the machine is not new */
10095 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10096 if (RT_FAILURE(vrc))
10097 {
10098 rc = setError(E_FAIL,
10099 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10100 configFile.c_str(),
10101 newConfigFile.c_str(),
10102 vrc);
10103 break;
10104 }
10105 fileRenamed = true;
10106 configFilePrev = configFile;
10107 configFilePrev += "-prev";
10108 newConfigFilePrev = newConfigFile;
10109 newConfigFilePrev += "-prev";
10110 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10111 }
10112 }
10113
10114 // update m_strConfigFileFull amd mConfigFile
10115 mData->m_strConfigFileFull = newConfigFile;
10116 // compute the relative path too
10117 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10118
10119 // store the old and new so that VirtualBox::saveSettings() can update
10120 // the media registry
10121 if ( mData->mRegistered
10122 && configDir != newConfigDir)
10123 {
10124 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
10125
10126 if (pfNeedsGlobalSaveSettings)
10127 *pfNeedsGlobalSaveSettings = true;
10128 }
10129
10130 // in the saved state file path, replace the old directory with the new directory
10131 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10132 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
10133
10134 // and do the same thing for the saved state file paths of all the online snapshots
10135 if (mData->mFirstSnapshot)
10136 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
10137 newConfigDir.c_str());
10138 }
10139 while (0);
10140
10141 if (FAILED(rc))
10142 {
10143 /* silently try to rename everything back */
10144 if (fileRenamed)
10145 {
10146 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10147 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10148 }
10149 if (dirRenamed)
10150 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10151 }
10152
10153 if (FAILED(rc)) return rc;
10154 }
10155
10156 if (fSettingsFileIsNew)
10157 {
10158 /* create a virgin config file */
10159 int vrc = VINF_SUCCESS;
10160
10161 /* ensure the settings directory exists */
10162 Utf8Str path(mData->m_strConfigFileFull);
10163 path.stripFilename();
10164 if (!RTDirExists(path.c_str()))
10165 {
10166 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10167 if (RT_FAILURE(vrc))
10168 {
10169 return setError(E_FAIL,
10170 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10171 path.c_str(),
10172 vrc);
10173 }
10174 }
10175
10176 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10177 path = Utf8Str(mData->m_strConfigFileFull);
10178 RTFILE f = NIL_RTFILE;
10179 vrc = RTFileOpen(&f, path.c_str(),
10180 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10181 if (RT_FAILURE(vrc))
10182 return setError(E_FAIL,
10183 tr("Could not create the settings file '%s' (%Rrc)"),
10184 path.c_str(),
10185 vrc);
10186 RTFileClose(f);
10187 }
10188
10189 return rc;
10190}
10191
10192/**
10193 * Saves and commits machine data, user data and hardware data.
10194 *
10195 * Note that on failure, the data remains uncommitted.
10196 *
10197 * @a aFlags may combine the following flags:
10198 *
10199 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10200 * Used when saving settings after an operation that makes them 100%
10201 * correspond to the settings from the current snapshot.
10202 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
10203 * #isReallyModified() returns false. This is necessary for cases when we
10204 * change machine data directly, not through the backup()/commit() mechanism.
10205 * - SaveS_Force: settings will be saved without doing a deep compare of the
10206 * settings structures. This is used when this is called because snapshots
10207 * have changed to avoid the overhead of the deep compare.
10208 *
10209 * @note Must be called from under this object's write lock. Locks children for
10210 * writing.
10211 *
10212 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10213 * initialized to false and that will be set to true by this function if
10214 * the caller must invoke VirtualBox::saveSettings() because the global
10215 * settings have changed. This will happen if a machine rename has been
10216 * saved and the global machine and media registries will therefore need
10217 * updating.
10218 */
10219HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
10220 int aFlags /*= 0*/)
10221{
10222 LogFlowThisFuncEnter();
10223
10224 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10225
10226 /* make sure child objects are unable to modify the settings while we are
10227 * saving them */
10228 ensureNoStateDependencies();
10229
10230 AssertReturn(!isSnapshotMachine(),
10231 E_FAIL);
10232
10233 HRESULT rc = S_OK;
10234 bool fNeedsWrite = false;
10235
10236 /* First, prepare to save settings. It will care about renaming the
10237 * settings directory and file if the machine name was changed and about
10238 * creating a new settings file if this is a new machine. */
10239 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
10240 if (FAILED(rc)) return rc;
10241
10242 // keep a pointer to the current settings structures
10243 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10244 settings::MachineConfigFile *pNewConfig = NULL;
10245
10246 try
10247 {
10248 // make a fresh one to have everyone write stuff into
10249 pNewConfig = new settings::MachineConfigFile(NULL);
10250 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10251
10252 // now go and copy all the settings data from COM to the settings structures
10253 // (this calles saveSettings() on all the COM objects in the machine)
10254 copyMachineDataToSettings(*pNewConfig);
10255
10256 if (aFlags & SaveS_ResetCurStateModified)
10257 {
10258 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10259 mData->mCurrentStateModified = FALSE;
10260 fNeedsWrite = true; // always, no need to compare
10261 }
10262 else if (aFlags & SaveS_Force)
10263 {
10264 fNeedsWrite = true; // always, no need to compare
10265 }
10266 else
10267 {
10268 if (!mData->mCurrentStateModified)
10269 {
10270 // do a deep compare of the settings that we just saved with the settings
10271 // previously stored in the config file; this invokes MachineConfigFile::operator==
10272 // which does a deep compare of all the settings, which is expensive but less expensive
10273 // than writing out XML in vain
10274 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10275
10276 // could still be modified if any settings changed
10277 mData->mCurrentStateModified = fAnySettingsChanged;
10278
10279 fNeedsWrite = fAnySettingsChanged;
10280 }
10281 else
10282 fNeedsWrite = true;
10283 }
10284
10285 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10286
10287 if (fNeedsWrite)
10288 // now spit it all out!
10289 pNewConfig->write(mData->m_strConfigFileFull);
10290
10291 mData->pMachineConfigFile = pNewConfig;
10292 delete pOldConfig;
10293 commit();
10294
10295 // after saving settings, we are no longer different from the XML on disk
10296 mData->flModifications = 0;
10297 }
10298 catch (HRESULT err)
10299 {
10300 // we assume that error info is set by the thrower
10301 rc = err;
10302
10303 // restore old config
10304 delete pNewConfig;
10305 mData->pMachineConfigFile = pOldConfig;
10306 }
10307 catch (...)
10308 {
10309 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10310 }
10311
10312 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
10313 {
10314 /* Fire the data change event, even on failure (since we've already
10315 * committed all data). This is done only for SessionMachines because
10316 * mutable Machine instances are always not registered (i.e. private
10317 * to the client process that creates them) and thus don't need to
10318 * inform callbacks. */
10319 if (isSessionMachine())
10320 mParent->onMachineDataChange(mData->mUuid);
10321 }
10322
10323 LogFlowThisFunc(("rc=%08X\n", rc));
10324 LogFlowThisFuncLeave();
10325 return rc;
10326}
10327
10328/**
10329 * Implementation for saving the machine settings into the given
10330 * settings::MachineConfigFile instance. This copies machine extradata
10331 * from the previous machine config file in the instance data, if any.
10332 *
10333 * This gets called from two locations:
10334 *
10335 * -- Machine::saveSettings(), during the regular XML writing;
10336 *
10337 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10338 * exported to OVF and we write the VirtualBox proprietary XML
10339 * into a <vbox:Machine> tag.
10340 *
10341 * This routine fills all the fields in there, including snapshots, *except*
10342 * for the following:
10343 *
10344 * -- fCurrentStateModified. There is some special logic associated with that.
10345 *
10346 * The caller can then call MachineConfigFile::write() or do something else
10347 * with it.
10348 *
10349 * Caller must hold the machine lock!
10350 *
10351 * This throws XML errors and HRESULT, so the caller must have a catch block!
10352 */
10353void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
10354{
10355 // deep copy extradata
10356 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10357
10358 config.uuid = mData->mUuid;
10359
10360 // copy name, description, OS type, teleport, UTC etc.
10361 config.machineUserData = mUserData->s;
10362
10363 // Encode the Icon Override data from Machine and store on config userdata.
10364 com::SafeArray<BYTE> iconByte;
10365 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
10366 ssize_t cbData = iconByte.size();
10367 if (cbData > 0)
10368 {
10369 ssize_t cchOut = RTBase64EncodedLength(cbData);
10370 Utf8Str strIconData;
10371 strIconData.reserve(cchOut+1);
10372 int vrc = RTBase64Encode(iconByte.raw(), cbData,
10373 strIconData.mutableRaw(), strIconData.capacity(),
10374 NULL);
10375 if (RT_FAILURE(vrc))
10376 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10377 strIconData.jolt();
10378 config.machineUserData.ovIcon = strIconData;
10379 }
10380 else
10381 config.machineUserData.ovIcon.setNull();
10382
10383 if ( mData->mMachineState == MachineState_Saved
10384 || mData->mMachineState == MachineState_Restoring
10385 // when deleting a snapshot we may or may not have a saved state in the current state,
10386 // so let's not assert here please
10387 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
10388 || mData->mMachineState == MachineState_DeletingSnapshotOnline
10389 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
10390 && (!mSSData->strStateFilePath.isEmpty())
10391 )
10392 )
10393 {
10394 Assert(!mSSData->strStateFilePath.isEmpty());
10395 /* try to make the file name relative to the settings file dir */
10396 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10397 }
10398 else
10399 {
10400 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10401 config.strStateFile.setNull();
10402 }
10403
10404 if (mData->mCurrentSnapshot)
10405 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
10406 else
10407 config.uuidCurrentSnapshot.clear();
10408
10409 config.timeLastStateChange = mData->mLastStateChange;
10410 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10411 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10412
10413 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10414 if (FAILED(rc)) throw rc;
10415
10416 rc = saveStorageControllers(config.storageMachine);
10417 if (FAILED(rc)) throw rc;
10418
10419 // save machine's media registry if this is VirtualBox 4.0 or later
10420 if (config.canHaveOwnMediaRegistry())
10421 {
10422 // determine machine folder
10423 Utf8Str strMachineFolder = getSettingsFileFull();
10424 strMachineFolder.stripFilename();
10425 mParent->saveMediaRegistry(config.mediaRegistry,
10426 getId(), // only media with registry ID == machine UUID
10427 strMachineFolder);
10428 // this throws HRESULT
10429 }
10430
10431 // save snapshots
10432 rc = saveAllSnapshots(config);
10433 if (FAILED(rc)) throw rc;
10434}
10435
10436/**
10437 * Saves all snapshots of the machine into the given machine config file. Called
10438 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10439 * @param config
10440 * @return
10441 */
10442HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
10443{
10444 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10445
10446 HRESULT rc = S_OK;
10447
10448 try
10449 {
10450 config.llFirstSnapshot.clear();
10451
10452 if (mData->mFirstSnapshot)
10453 {
10454 settings::Snapshot snapNew;
10455 config.llFirstSnapshot.push_back(snapNew);
10456
10457 // get reference to the fresh copy of the snapshot on the list and
10458 // work on that copy directly to avoid excessive copying later
10459 settings::Snapshot &snap = config.llFirstSnapshot.front();
10460
10461 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
10462 if (FAILED(rc)) throw rc;
10463 }
10464
10465// if (mType == IsSessionMachine)
10466// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10467
10468 }
10469 catch (HRESULT err)
10470 {
10471 /* we assume that error info is set by the thrower */
10472 rc = err;
10473 }
10474 catch (...)
10475 {
10476 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10477 }
10478
10479 return rc;
10480}
10481
10482/**
10483 * Saves the VM hardware configuration. It is assumed that the
10484 * given node is empty.
10485 *
10486 * @param data Reference to the settings object for the hardware config.
10487 * @param pDbg Pointer to the settings object for the debugging config
10488 * which happens to live in mHWData.
10489 * @param pAutostart Pointer to the settings object for the autostart config
10490 * which happens to live in mHWData.
10491 */
10492HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10493 settings::Autostart *pAutostart)
10494{
10495 HRESULT rc = S_OK;
10496
10497 try
10498 {
10499 /* The hardware version attribute (optional).
10500 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10501 if ( mHWData->mHWVersion == "1"
10502 && mSSData->strStateFilePath.isEmpty()
10503 )
10504 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. */
10505
10506 data.strVersion = mHWData->mHWVersion;
10507 data.uuid = mHWData->mHardwareUUID;
10508
10509 // CPU
10510 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10511 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
10512 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10513 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10514 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10515 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10516 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10517 data.fPAE = !!mHWData->mPAEEnabled;
10518 data.enmLongMode = mHWData->mLongMode;
10519 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10520
10521 /* Standard and Extended CPUID leafs. */
10522 data.llCpuIdLeafs.clear();
10523 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
10524 {
10525 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10526 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10527 }
10528 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
10529 {
10530 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10531 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10532 }
10533
10534 data.cCPUs = mHWData->mCPUCount;
10535 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10536 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10537
10538 data.llCpus.clear();
10539 if (data.fCpuHotPlug)
10540 {
10541 for (unsigned idx = 0; idx < data.cCPUs; idx++)
10542 {
10543 if (mHWData->mCPUAttached[idx])
10544 {
10545 settings::Cpu cpu;
10546 cpu.ulId = idx;
10547 data.llCpus.push_back(cpu);
10548 }
10549 }
10550 }
10551
10552 // memory
10553 data.ulMemorySizeMB = mHWData->mMemorySize;
10554 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10555
10556 // firmware
10557 data.firmwareType = mHWData->mFirmwareType;
10558
10559 // HID
10560 data.pointingHIDType = mHWData->mPointingHIDType;
10561 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10562
10563 // chipset
10564 data.chipsetType = mHWData->mChipsetType;
10565
10566 data.fEmulatedUSBWebcam = !!mHWData->mEmulatedUSBWebcamEnabled;
10567 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10568
10569 // HPET
10570 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10571
10572 // boot order
10573 data.mapBootOrder.clear();
10574 for (size_t i = 0;
10575 i < RT_ELEMENTS(mHWData->mBootOrder);
10576 ++i)
10577 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10578
10579 // display
10580 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10581 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10582 data.cMonitors = mHWData->mMonitorCount;
10583 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10584 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10585 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10586 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10587 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10588 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10589 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10590 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; i++)
10591 {
10592 if (mHWData->maVideoCaptureScreens[i])
10593 ASMBitSet(&data.u64VideoCaptureScreens, i);
10594 else
10595 ASMBitClear(&data.u64VideoCaptureScreens, i);
10596 }
10597 /* store relative video capture file if possible */
10598 copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10599
10600 /* VRDEServer settings (optional) */
10601 rc = mVRDEServer->saveSettings(data.vrdeSettings);
10602 if (FAILED(rc)) throw rc;
10603
10604 /* BIOS (required) */
10605 rc = mBIOSSettings->saveSettings(data.biosSettings);
10606 if (FAILED(rc)) throw rc;
10607
10608 /* USB Controller (required) */
10609 for (USBControllerList::const_iterator it = mUSBControllers->begin();
10610 it != mUSBControllers->end();
10611 ++it)
10612 {
10613 ComObjPtr<USBController> ctrl = *it;
10614 settings::USBController settingsCtrl;
10615
10616 settingsCtrl.strName = ctrl->getName();
10617 settingsCtrl.enmType = ctrl->getControllerType();
10618
10619 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10620 }
10621
10622 /* USB device filters (required) */
10623 rc = mUSBDeviceFilters->saveSettings(data.usbSettings);
10624 if (FAILED(rc)) throw rc;
10625
10626 /* Network adapters (required) */
10627 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10628 data.llNetworkAdapters.clear();
10629 /* Write out only the nominal number of network adapters for this
10630 * chipset type. Since Machine::commit() hasn't been called there
10631 * may be extra NIC settings in the vector. */
10632 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10633 {
10634 settings::NetworkAdapter nic;
10635 nic.ulSlot = slot;
10636 /* paranoia check... must not be NULL, but must not crash either. */
10637 if (mNetworkAdapters[slot])
10638 {
10639 rc = mNetworkAdapters[slot]->saveSettings(nic);
10640 if (FAILED(rc)) throw rc;
10641
10642 data.llNetworkAdapters.push_back(nic);
10643 }
10644 }
10645
10646 /* Serial ports */
10647 data.llSerialPorts.clear();
10648 for (ULONG slot = 0;
10649 slot < RT_ELEMENTS(mSerialPorts);
10650 ++slot)
10651 {
10652 settings::SerialPort s;
10653 s.ulSlot = slot;
10654 rc = mSerialPorts[slot]->saveSettings(s);
10655 if (FAILED(rc)) return rc;
10656
10657 data.llSerialPorts.push_back(s);
10658 }
10659
10660 /* Parallel ports */
10661 data.llParallelPorts.clear();
10662 for (ULONG slot = 0;
10663 slot < RT_ELEMENTS(mParallelPorts);
10664 ++slot)
10665 {
10666 settings::ParallelPort p;
10667 p.ulSlot = slot;
10668 rc = mParallelPorts[slot]->saveSettings(p);
10669 if (FAILED(rc)) return rc;
10670
10671 data.llParallelPorts.push_back(p);
10672 }
10673
10674 /* Audio adapter */
10675 rc = mAudioAdapter->saveSettings(data.audioAdapter);
10676 if (FAILED(rc)) return rc;
10677
10678 /* Shared folders */
10679 data.llSharedFolders.clear();
10680 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10681 it != mHWData->mSharedFolders.end();
10682 ++it)
10683 {
10684 SharedFolder *pSF = *it;
10685 AutoCaller sfCaller(pSF);
10686 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10687 settings::SharedFolder sf;
10688 sf.strName = pSF->getName();
10689 sf.strHostPath = pSF->getHostPath();
10690 sf.fWritable = !!pSF->isWritable();
10691 sf.fAutoMount = !!pSF->isAutoMounted();
10692
10693 data.llSharedFolders.push_back(sf);
10694 }
10695
10696 // clipboard
10697 data.clipboardMode = mHWData->mClipboardMode;
10698
10699 // drag'n'drop
10700 data.dragAndDropMode = mHWData->mDragAndDropMode;
10701
10702 /* Guest */
10703 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10704
10705 // IO settings
10706 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10707 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10708
10709 /* BandwidthControl (required) */
10710 rc = mBandwidthControl->saveSettings(data.ioSettings);
10711 if (FAILED(rc)) throw rc;
10712
10713 /* Host PCI devices */
10714 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10715 it != mHWData->mPCIDeviceAssignments.end();
10716 ++it)
10717 {
10718 ComObjPtr<PCIDeviceAttachment> pda = *it;
10719 settings::HostPCIDeviceAttachment hpda;
10720
10721 rc = pda->saveSettings(hpda);
10722 if (FAILED(rc)) throw rc;
10723
10724 data.pciAttachments.push_back(hpda);
10725 }
10726
10727
10728 // guest properties
10729 data.llGuestProperties.clear();
10730#ifdef VBOX_WITH_GUEST_PROPS
10731 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10732 it != mHWData->mGuestProperties.end();
10733 ++it)
10734 {
10735 HWData::GuestProperty property = it->second;
10736
10737 /* Remove transient guest properties at shutdown unless we
10738 * are saving state */
10739 if ( ( mData->mMachineState == MachineState_PoweredOff
10740 || mData->mMachineState == MachineState_Aborted
10741 || mData->mMachineState == MachineState_Teleported)
10742 && ( property.mFlags & guestProp::TRANSIENT
10743 || property.mFlags & guestProp::TRANSRESET))
10744 continue;
10745 settings::GuestProperty prop;
10746 prop.strName = it->first;
10747 prop.strValue = property.strValue;
10748 prop.timestamp = property.mTimestamp;
10749 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10750 guestProp::writeFlags(property.mFlags, szFlags);
10751 prop.strFlags = szFlags;
10752
10753 data.llGuestProperties.push_back(prop);
10754 }
10755
10756 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10757 /* I presume this doesn't require a backup(). */
10758 mData->mGuestPropertiesModified = FALSE;
10759#endif /* VBOX_WITH_GUEST_PROPS defined */
10760
10761 *pDbg = mHWData->mDebugging;
10762 *pAutostart = mHWData->mAutostart;
10763
10764 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10765 }
10766 catch(std::bad_alloc &)
10767 {
10768 return E_OUTOFMEMORY;
10769 }
10770
10771 AssertComRC(rc);
10772 return rc;
10773}
10774
10775/**
10776 * Saves the storage controller configuration.
10777 *
10778 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10779 */
10780HRESULT Machine::saveStorageControllers(settings::Storage &data)
10781{
10782 data.llStorageControllers.clear();
10783
10784 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10785 it != mStorageControllers->end();
10786 ++it)
10787 {
10788 HRESULT rc;
10789 ComObjPtr<StorageController> pCtl = *it;
10790
10791 settings::StorageController ctl;
10792 ctl.strName = pCtl->getName();
10793 ctl.controllerType = pCtl->getControllerType();
10794 ctl.storageBus = pCtl->getStorageBus();
10795 ctl.ulInstance = pCtl->getInstance();
10796 ctl.fBootable = pCtl->getBootable();
10797
10798 /* Save the port count. */
10799 ULONG portCount;
10800 rc = pCtl->COMGETTER(PortCount)(&portCount);
10801 ComAssertComRCRet(rc, rc);
10802 ctl.ulPortCount = portCount;
10803
10804 /* Save fUseHostIOCache */
10805 BOOL fUseHostIOCache;
10806 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10807 ComAssertComRCRet(rc, rc);
10808 ctl.fUseHostIOCache = !!fUseHostIOCache;
10809
10810 /* Save IDE emulation settings. */
10811 if (ctl.controllerType == StorageControllerType_IntelAhci)
10812 {
10813 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10814 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10815 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10816 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10817 )
10818 ComAssertComRCRet(rc, rc);
10819 }
10820
10821 /* save the devices now. */
10822 rc = saveStorageDevices(pCtl, ctl);
10823 ComAssertComRCRet(rc, rc);
10824
10825 data.llStorageControllers.push_back(ctl);
10826 }
10827
10828 return S_OK;
10829}
10830
10831/**
10832 * Saves the hard disk configuration.
10833 */
10834HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10835 settings::StorageController &data)
10836{
10837 MediaData::AttachmentList atts;
10838
10839 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
10840 if (FAILED(rc)) return rc;
10841
10842 data.llAttachedDevices.clear();
10843 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10844 it != atts.end();
10845 ++it)
10846 {
10847 settings::AttachedDevice dev;
10848
10849 MediumAttachment *pAttach = *it;
10850 Medium *pMedium = pAttach->getMedium();
10851
10852 dev.deviceType = pAttach->getType();
10853 dev.lPort = pAttach->getPort();
10854 dev.lDevice = pAttach->getDevice();
10855 if (pMedium)
10856 {
10857 if (pMedium->isHostDrive())
10858 dev.strHostDriveSrc = pMedium->getLocationFull();
10859 else
10860 dev.uuid = pMedium->getId();
10861 dev.fPassThrough = pAttach->getPassthrough();
10862 dev.fTempEject = pAttach->getTempEject();
10863 dev.fNonRotational = pAttach->getNonRotational();
10864 dev.fDiscard = pAttach->getDiscard();
10865 }
10866
10867 dev.strBwGroup = pAttach->getBandwidthGroup();
10868
10869 data.llAttachedDevices.push_back(dev);
10870 }
10871
10872 return S_OK;
10873}
10874
10875/**
10876 * Saves machine state settings as defined by aFlags
10877 * (SaveSTS_* values).
10878 *
10879 * @param aFlags Combination of SaveSTS_* flags.
10880 *
10881 * @note Locks objects for writing.
10882 */
10883HRESULT Machine::saveStateSettings(int aFlags)
10884{
10885 if (aFlags == 0)
10886 return S_OK;
10887
10888 AutoCaller autoCaller(this);
10889 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10890
10891 /* This object's write lock is also necessary to serialize file access
10892 * (prevent concurrent reads and writes) */
10893 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10894
10895 HRESULT rc = S_OK;
10896
10897 Assert(mData->pMachineConfigFile);
10898
10899 try
10900 {
10901 if (aFlags & SaveSTS_CurStateModified)
10902 mData->pMachineConfigFile->fCurrentStateModified = true;
10903
10904 if (aFlags & SaveSTS_StateFilePath)
10905 {
10906 if (!mSSData->strStateFilePath.isEmpty())
10907 /* try to make the file name relative to the settings file dir */
10908 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10909 else
10910 mData->pMachineConfigFile->strStateFile.setNull();
10911 }
10912
10913 if (aFlags & SaveSTS_StateTimeStamp)
10914 {
10915 Assert( mData->mMachineState != MachineState_Aborted
10916 || mSSData->strStateFilePath.isEmpty());
10917
10918 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10919
10920 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10921//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10922 }
10923
10924 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10925 }
10926 catch (...)
10927 {
10928 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10929 }
10930
10931 return rc;
10932}
10933
10934/**
10935 * Ensures that the given medium is added to a media registry. If this machine
10936 * was created with 4.0 or later, then the machine registry is used. Otherwise
10937 * the global VirtualBox media registry is used.
10938 *
10939 * Caller must NOT hold machine lock, media tree or any medium locks!
10940 *
10941 * @param pMedium
10942 */
10943void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10944{
10945 /* Paranoia checks: do not hold machine or media tree locks. */
10946 AssertReturnVoid(!isWriteLockOnCurrentThread());
10947 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10948
10949 ComObjPtr<Medium> pBase;
10950 {
10951 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10952 pBase = pMedium->getBase();
10953 }
10954
10955 /* Paranoia checks: do not hold medium locks. */
10956 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10957 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10958
10959 // decide which medium registry to use now that the medium is attached:
10960 Guid uuid;
10961 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10962 // machine XML is VirtualBox 4.0 or higher:
10963 uuid = getId(); // machine UUID
10964 else
10965 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10966
10967 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10968 mParent->markRegistryModified(uuid);
10969
10970 /* For more complex hard disk structures it can happen that the base
10971 * medium isn't yet associated with any medium registry. Do that now. */
10972 if (pMedium != pBase)
10973 {
10974 if (pBase->addRegistry(uuid, true /* fRecurse */))
10975 mParent->markRegistryModified(uuid);
10976 }
10977}
10978
10979/**
10980 * Creates differencing hard disks for all normal hard disks attached to this
10981 * machine and a new set of attachments to refer to created disks.
10982 *
10983 * Used when taking a snapshot or when deleting the current state. Gets called
10984 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10985 *
10986 * This method assumes that mMediaData contains the original hard disk attachments
10987 * it needs to create diffs for. On success, these attachments will be replaced
10988 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10989 * called to delete created diffs which will also rollback mMediaData and restore
10990 * whatever was backed up before calling this method.
10991 *
10992 * Attachments with non-normal hard disks are left as is.
10993 *
10994 * If @a aOnline is @c false then the original hard disks that require implicit
10995 * diffs will be locked for reading. Otherwise it is assumed that they are
10996 * already locked for writing (when the VM was started). Note that in the latter
10997 * case it is responsibility of the caller to lock the newly created diffs for
10998 * writing if this method succeeds.
10999 *
11000 * @param aProgress Progress object to run (must contain at least as
11001 * many operations left as the number of hard disks
11002 * attached).
11003 * @param aOnline Whether the VM was online prior to this operation.
11004 *
11005 * @note The progress object is not marked as completed, neither on success nor
11006 * on failure. This is a responsibility of the caller.
11007 *
11008 * @note Locks this object and the media tree for writing.
11009 */
11010HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
11011 ULONG aWeight,
11012 bool aOnline)
11013{
11014 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11015
11016 AutoCaller autoCaller(this);
11017 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11018
11019 AutoMultiWriteLock2 alock(this->lockHandle(),
11020 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11021
11022 /* must be in a protective state because we release the lock below */
11023 AssertReturn( mData->mMachineState == MachineState_Saving
11024 || mData->mMachineState == MachineState_LiveSnapshotting
11025 || mData->mMachineState == MachineState_RestoringSnapshot
11026 || mData->mMachineState == MachineState_DeletingSnapshot
11027 , E_FAIL);
11028
11029 HRESULT rc = S_OK;
11030
11031 // use appropriate locked media map (online or offline)
11032 MediumLockListMap lockedMediaOffline;
11033 MediumLockListMap *lockedMediaMap;
11034 if (aOnline)
11035 lockedMediaMap = &mData->mSession.mLockedMedia;
11036 else
11037 lockedMediaMap = &lockedMediaOffline;
11038
11039 try
11040 {
11041 if (!aOnline)
11042 {
11043 /* lock all attached hard disks early to detect "in use"
11044 * situations before creating actual diffs */
11045 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11046 it != mMediaData->mAttachments.end();
11047 ++it)
11048 {
11049 MediumAttachment* pAtt = *it;
11050 if (pAtt->getType() == DeviceType_HardDisk)
11051 {
11052 Medium* pMedium = pAtt->getMedium();
11053 Assert(pMedium);
11054
11055 MediumLockList *pMediumLockList(new MediumLockList());
11056 alock.release();
11057 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
11058 false /* fMediumLockWrite */,
11059 NULL,
11060 *pMediumLockList);
11061 alock.acquire();
11062 if (FAILED(rc))
11063 {
11064 delete pMediumLockList;
11065 throw rc;
11066 }
11067 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11068 if (FAILED(rc))
11069 {
11070 throw setError(rc,
11071 tr("Collecting locking information for all attached media failed"));
11072 }
11073 }
11074 }
11075
11076 /* Now lock all media. If this fails, nothing is locked. */
11077 alock.release();
11078 rc = lockedMediaMap->Lock();
11079 alock.acquire();
11080 if (FAILED(rc))
11081 {
11082 throw setError(rc,
11083 tr("Locking of attached media failed"));
11084 }
11085 }
11086
11087 /* remember the current list (note that we don't use backup() since
11088 * mMediaData may be already backed up) */
11089 MediaData::AttachmentList atts = mMediaData->mAttachments;
11090
11091 /* start from scratch */
11092 mMediaData->mAttachments.clear();
11093
11094 /* go through remembered attachments and create diffs for normal hard
11095 * disks and attach them */
11096 for (MediaData::AttachmentList::const_iterator it = atts.begin();
11097 it != atts.end();
11098 ++it)
11099 {
11100 MediumAttachment* pAtt = *it;
11101
11102 DeviceType_T devType = pAtt->getType();
11103 Medium* pMedium = pAtt->getMedium();
11104
11105 if ( devType != DeviceType_HardDisk
11106 || pMedium == NULL
11107 || pMedium->getType() != MediumType_Normal)
11108 {
11109 /* copy the attachment as is */
11110
11111 /** @todo the progress object created in Console::TakeSnaphot
11112 * only expects operations for hard disks. Later other
11113 * device types need to show up in the progress as well. */
11114 if (devType == DeviceType_HardDisk)
11115 {
11116 if (pMedium == NULL)
11117 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11118 aWeight); // weight
11119 else
11120 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11121 pMedium->getBase()->getName().c_str()).raw(),
11122 aWeight); // weight
11123 }
11124
11125 mMediaData->mAttachments.push_back(pAtt);
11126 continue;
11127 }
11128
11129 /* need a diff */
11130 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11131 pMedium->getBase()->getName().c_str()).raw(),
11132 aWeight); // weight
11133
11134 Utf8Str strFullSnapshotFolder;
11135 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11136
11137 ComObjPtr<Medium> diff;
11138 diff.createObject();
11139 // store the diff in the same registry as the parent
11140 // (this cannot fail here because we can't create implicit diffs for
11141 // unregistered images)
11142 Guid uuidRegistryParent;
11143 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
11144 Assert(fInRegistry); NOREF(fInRegistry);
11145 rc = diff->init(mParent,
11146 pMedium->getPreferredDiffFormat(),
11147 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11148 uuidRegistryParent);
11149 if (FAILED(rc)) throw rc;
11150
11151 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11152 * the push_back? Looks like we're going to release medium with the
11153 * wrong kind of lock (general issue with if we fail anywhere at all)
11154 * and an orphaned VDI in the snapshots folder. */
11155
11156 /* update the appropriate lock list */
11157 MediumLockList *pMediumLockList;
11158 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11159 AssertComRCThrowRC(rc);
11160 if (aOnline)
11161 {
11162 alock.release();
11163 /* The currently attached medium will be read-only, change
11164 * the lock type to read. */
11165 rc = pMediumLockList->Update(pMedium, false);
11166 alock.acquire();
11167 AssertComRCThrowRC(rc);
11168 }
11169
11170 /* release the locks before the potentially lengthy operation */
11171 alock.release();
11172 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
11173 pMediumLockList,
11174 NULL /* aProgress */,
11175 true /* aWait */);
11176 alock.acquire();
11177 if (FAILED(rc)) throw rc;
11178
11179 /* actual lock list update is done in Medium::commitMedia */
11180
11181 rc = diff->addBackReference(mData->mUuid);
11182 AssertComRCThrowRC(rc);
11183
11184 /* add a new attachment */
11185 ComObjPtr<MediumAttachment> attachment;
11186 attachment.createObject();
11187 rc = attachment->init(this,
11188 diff,
11189 pAtt->getControllerName(),
11190 pAtt->getPort(),
11191 pAtt->getDevice(),
11192 DeviceType_HardDisk,
11193 true /* aImplicit */,
11194 false /* aPassthrough */,
11195 false /* aTempEject */,
11196 pAtt->getNonRotational(),
11197 pAtt->getDiscard(),
11198 pAtt->getBandwidthGroup());
11199 if (FAILED(rc)) throw rc;
11200
11201 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11202 AssertComRCThrowRC(rc);
11203 mMediaData->mAttachments.push_back(attachment);
11204 }
11205 }
11206 catch (HRESULT aRC) { rc = aRC; }
11207
11208 /* unlock all hard disks we locked when there is no VM */
11209 if (!aOnline)
11210 {
11211 ErrorInfoKeeper eik;
11212
11213 HRESULT rc1 = lockedMediaMap->Clear();
11214 AssertComRC(rc1);
11215 }
11216
11217 return rc;
11218}
11219
11220/**
11221 * Deletes implicit differencing hard disks created either by
11222 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
11223 *
11224 * Note that to delete hard disks created by #AttachDevice() this method is
11225 * called from #fixupMedia() when the changes are rolled back.
11226 *
11227 * @note Locks this object and the media tree for writing.
11228 */
11229HRESULT Machine::deleteImplicitDiffs(bool aOnline)
11230{
11231 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11232
11233 AutoCaller autoCaller(this);
11234 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11235
11236 AutoMultiWriteLock2 alock(this->lockHandle(),
11237 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11238
11239 /* We absolutely must have backed up state. */
11240 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
11241
11242 /* Check if there are any implicitly created diff images. */
11243 bool fImplicitDiffs = false;
11244 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11245 it != mMediaData->mAttachments.end();
11246 ++it)
11247 {
11248 const ComObjPtr<MediumAttachment> &pAtt = *it;
11249 if (pAtt->isImplicit())
11250 {
11251 fImplicitDiffs = true;
11252 break;
11253 }
11254 }
11255 /* If there is nothing to do, leave early. This saves lots of image locking
11256 * effort. It also avoids a MachineStateChanged event without real reason.
11257 * This is important e.g. when loading a VM config, because there should be
11258 * no events. Otherwise API clients can become thoroughly confused for
11259 * inaccessible VMs (the code for loading VM configs uses this method for
11260 * cleanup if the config makes no sense), as they take such events as an
11261 * indication that the VM is alive, and they would force the VM config to
11262 * be reread, leading to an endless loop. */
11263 if (!fImplicitDiffs)
11264 return S_OK;
11265
11266 HRESULT rc = S_OK;
11267 MachineState_T oldState = mData->mMachineState;
11268
11269 /* will release the lock before the potentially lengthy operation,
11270 * so protect with the special state (unless already protected) */
11271 if ( oldState != MachineState_Saving
11272 && oldState != MachineState_LiveSnapshotting
11273 && oldState != MachineState_RestoringSnapshot
11274 && oldState != MachineState_DeletingSnapshot
11275 && oldState != MachineState_DeletingSnapshotOnline
11276 && oldState != MachineState_DeletingSnapshotPaused
11277 )
11278 setMachineState(MachineState_SettingUp);
11279
11280 // use appropriate locked media map (online or offline)
11281 MediumLockListMap lockedMediaOffline;
11282 MediumLockListMap *lockedMediaMap;
11283 if (aOnline)
11284 lockedMediaMap = &mData->mSession.mLockedMedia;
11285 else
11286 lockedMediaMap = &lockedMediaOffline;
11287
11288 try
11289 {
11290 if (!aOnline)
11291 {
11292 /* lock all attached hard disks early to detect "in use"
11293 * situations before deleting actual diffs */
11294 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11295 it != mMediaData->mAttachments.end();
11296 ++it)
11297 {
11298 MediumAttachment* pAtt = *it;
11299 if (pAtt->getType() == DeviceType_HardDisk)
11300 {
11301 Medium* pMedium = pAtt->getMedium();
11302 Assert(pMedium);
11303
11304 MediumLockList *pMediumLockList(new MediumLockList());
11305 alock.release();
11306 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
11307 false /* fMediumLockWrite */,
11308 NULL,
11309 *pMediumLockList);
11310 alock.acquire();
11311
11312 if (FAILED(rc))
11313 {
11314 delete pMediumLockList;
11315 throw rc;
11316 }
11317
11318 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11319 if (FAILED(rc))
11320 throw rc;
11321 }
11322 }
11323
11324 if (FAILED(rc))
11325 throw rc;
11326 } // end of offline
11327
11328 /* Lock lists are now up to date and include implicitly created media */
11329
11330 /* Go through remembered attachments and delete all implicitly created
11331 * diffs and fix up the attachment information */
11332 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11333 MediaData::AttachmentList implicitAtts;
11334 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11335 it != mMediaData->mAttachments.end();
11336 ++it)
11337 {
11338 ComObjPtr<MediumAttachment> pAtt = *it;
11339 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11340 if (pMedium.isNull())
11341 continue;
11342
11343 // Implicit attachments go on the list for deletion and back references are removed.
11344 if (pAtt->isImplicit())
11345 {
11346 /* Deassociate and mark for deletion */
11347 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
11348 rc = pMedium->removeBackReference(mData->mUuid);
11349 if (FAILED(rc))
11350 throw rc;
11351 implicitAtts.push_back(pAtt);
11352 continue;
11353 }
11354
11355 /* Was this medium attached before? */
11356 if (!findAttachment(oldAtts, pMedium))
11357 {
11358 /* no: de-associate */
11359 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
11360 rc = pMedium->removeBackReference(mData->mUuid);
11361 if (FAILED(rc))
11362 throw rc;
11363 continue;
11364 }
11365 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
11366 }
11367
11368 /* If there are implicit attachments to delete, throw away the lock
11369 * map contents (which will unlock all media) since the medium
11370 * attachments will be rolled back. Below we need to completely
11371 * recreate the lock map anyway since it is infinitely complex to
11372 * do this incrementally (would need reconstructing each attachment
11373 * change, which would be extremely hairy). */
11374 if (implicitAtts.size() != 0)
11375 {
11376 ErrorInfoKeeper eik;
11377
11378 HRESULT rc1 = lockedMediaMap->Clear();
11379 AssertComRC(rc1);
11380 }
11381
11382 /* rollback hard disk changes */
11383 mMediaData.rollback();
11384
11385 MultiResult mrc(S_OK);
11386
11387 // Delete unused implicit diffs.
11388 if (implicitAtts.size() != 0)
11389 {
11390 alock.release();
11391
11392 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
11393 it != implicitAtts.end();
11394 ++it)
11395 {
11396 // Remove medium associated with this attachment.
11397 ComObjPtr<MediumAttachment> pAtt = *it;
11398 Assert(pAtt);
11399 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
11400 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11401 Assert(pMedium);
11402
11403 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11404 // continue on delete failure, just collect error messages
11405 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
11406 mrc = rc;
11407 }
11408
11409 alock.acquire();
11410
11411 /* if there is a VM recreate media lock map as mentioned above,
11412 * otherwise it is a waste of time and we leave things unlocked */
11413 if (aOnline)
11414 {
11415 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11416 /* must never be NULL, but better safe than sorry */
11417 if (!pMachine.isNull())
11418 {
11419 alock.release();
11420 rc = mData->mSession.mMachine->lockMedia();
11421 alock.acquire();
11422 if (FAILED(rc))
11423 throw rc;
11424 }
11425 }
11426 }
11427 }
11428 catch (HRESULT aRC) {rc = aRC;}
11429
11430 if (mData->mMachineState == MachineState_SettingUp)
11431 setMachineState(oldState);
11432
11433 /* unlock all hard disks we locked when there is no VM */
11434 if (!aOnline)
11435 {
11436 ErrorInfoKeeper eik;
11437
11438 HRESULT rc1 = lockedMediaMap->Clear();
11439 AssertComRC(rc1);
11440 }
11441
11442 return rc;
11443}
11444
11445
11446/**
11447 * Looks through the given list of media attachments for one with the given parameters
11448 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11449 * can be searched as well if needed.
11450 *
11451 * @param list
11452 * @param aControllerName
11453 * @param aControllerPort
11454 * @param aDevice
11455 * @return
11456 */
11457MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11458 IN_BSTR aControllerName,
11459 LONG aControllerPort,
11460 LONG aDevice)
11461{
11462 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11463 it != ll.end();
11464 ++it)
11465 {
11466 MediumAttachment *pAttach = *it;
11467 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
11468 return pAttach;
11469 }
11470
11471 return NULL;
11472}
11473
11474/**
11475 * Looks through the given list of media attachments for one with the given parameters
11476 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11477 * can be searched as well if needed.
11478 *
11479 * @param list
11480 * @param aControllerName
11481 * @param aControllerPort
11482 * @param aDevice
11483 * @return
11484 */
11485MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11486 ComObjPtr<Medium> pMedium)
11487{
11488 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11489 it != ll.end();
11490 ++it)
11491 {
11492 MediumAttachment *pAttach = *it;
11493 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11494 if (pMediumThis == pMedium)
11495 return pAttach;
11496 }
11497
11498 return NULL;
11499}
11500
11501/**
11502 * Looks through the given list of media attachments for one with the given parameters
11503 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11504 * can be searched as well if needed.
11505 *
11506 * @param list
11507 * @param aControllerName
11508 * @param aControllerPort
11509 * @param aDevice
11510 * @return
11511 */
11512MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11513 Guid &id)
11514{
11515 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11516 it != ll.end();
11517 ++it)
11518 {
11519 MediumAttachment *pAttach = *it;
11520 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11521 if (pMediumThis->getId() == id)
11522 return pAttach;
11523 }
11524
11525 return NULL;
11526}
11527
11528/**
11529 * Main implementation for Machine::DetachDevice. This also gets called
11530 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11531 *
11532 * @param pAttach Medium attachment to detach.
11533 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11534 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
11535 * @return
11536 */
11537HRESULT Machine::detachDevice(MediumAttachment *pAttach,
11538 AutoWriteLock &writeLock,
11539 Snapshot *pSnapshot)
11540{
11541 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
11542 DeviceType_T mediumType = pAttach->getType();
11543
11544 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
11545
11546 if (pAttach->isImplicit())
11547 {
11548 /* attempt to implicitly delete the implicitly created diff */
11549
11550 /// @todo move the implicit flag from MediumAttachment to Medium
11551 /// and forbid any hard disk operation when it is implicit. Or maybe
11552 /// a special media state for it to make it even more simple.
11553
11554 Assert(mMediaData.isBackedUp());
11555
11556 /* will release the lock before the potentially lengthy operation, so
11557 * protect with the special state */
11558 MachineState_T oldState = mData->mMachineState;
11559 setMachineState(MachineState_SettingUp);
11560
11561 writeLock.release();
11562
11563 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
11564 true /*aWait*/);
11565
11566 writeLock.acquire();
11567
11568 setMachineState(oldState);
11569
11570 if (FAILED(rc)) return rc;
11571 }
11572
11573 setModified(IsModified_Storage);
11574 mMediaData.backup();
11575 mMediaData->mAttachments.remove(pAttach);
11576
11577 if (!oldmedium.isNull())
11578 {
11579 // if this is from a snapshot, do not defer detachment to commitMedia()
11580 if (pSnapshot)
11581 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
11582 // else if non-hard disk media, do not defer detachment to commitMedia() either
11583 else if (mediumType != DeviceType_HardDisk)
11584 oldmedium->removeBackReference(mData->mUuid);
11585 }
11586
11587 return S_OK;
11588}
11589
11590/**
11591 * Goes thru all media of the given list and
11592 *
11593 * 1) calls detachDevice() on each of them for this machine and
11594 * 2) adds all Medium objects found in the process to the given list,
11595 * depending on cleanupMode.
11596 *
11597 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11598 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11599 * media to the list.
11600 *
11601 * This gets called from Machine::Unregister, both for the actual Machine and
11602 * the SnapshotMachine objects that might be found in the snapshots.
11603 *
11604 * Requires caller and locking. The machine lock must be passed in because it
11605 * will be passed on to detachDevice which needs it for temporary unlocking.
11606 *
11607 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
11608 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11609 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
11610 * otherwise no media get added.
11611 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11612 * @return
11613 */
11614HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
11615 Snapshot *pSnapshot,
11616 CleanupMode_T cleanupMode,
11617 MediaList &llMedia)
11618{
11619 Assert(isWriteLockOnCurrentThread());
11620
11621 HRESULT rc;
11622
11623 // make a temporary list because detachDevice invalidates iterators into
11624 // mMediaData->mAttachments
11625 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11626
11627 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
11628 it != llAttachments2.end();
11629 ++it)
11630 {
11631 ComObjPtr<MediumAttachment> &pAttach = *it;
11632 ComObjPtr<Medium> pMedium = pAttach->getMedium();
11633
11634 if (!pMedium.isNull())
11635 {
11636 AutoCaller mac(pMedium);
11637 if (FAILED(mac.rc())) return mac.rc();
11638 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11639 DeviceType_T devType = pMedium->getDeviceType();
11640 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11641 && devType == DeviceType_HardDisk)
11642 || (cleanupMode == CleanupMode_Full)
11643 )
11644 {
11645 llMedia.push_back(pMedium);
11646 ComObjPtr<Medium> pParent = pMedium->getParent();
11647 /*
11648 * Search for medias which are not attached to any machine, but
11649 * in the chain to an attached disk. Mediums are only consided
11650 * if they are:
11651 * - have only one child
11652 * - no references to any machines
11653 * - are of normal medium type
11654 */
11655 while (!pParent.isNull())
11656 {
11657 AutoCaller mac1(pParent);
11658 if (FAILED(mac1.rc())) return mac1.rc();
11659 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11660 if (pParent->getChildren().size() == 1)
11661 {
11662 if ( pParent->getMachineBackRefCount() == 0
11663 && pParent->getType() == MediumType_Normal
11664 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11665 llMedia.push_back(pParent);
11666 }
11667 else
11668 break;
11669 pParent = pParent->getParent();
11670 }
11671 }
11672 }
11673
11674 // real machine: then we need to use the proper method
11675 rc = detachDevice(pAttach, writeLock, pSnapshot);
11676
11677 if (FAILED(rc))
11678 return rc;
11679 }
11680
11681 return S_OK;
11682}
11683
11684/**
11685 * Perform deferred hard disk detachments.
11686 *
11687 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11688 * backed up).
11689 *
11690 * If @a aOnline is @c true then this method will also unlock the old hard disks
11691 * for which the new implicit diffs were created and will lock these new diffs for
11692 * writing.
11693 *
11694 * @param aOnline Whether the VM was online prior to this operation.
11695 *
11696 * @note Locks this object for writing!
11697 */
11698void Machine::commitMedia(bool aOnline /*= false*/)
11699{
11700 AutoCaller autoCaller(this);
11701 AssertComRCReturnVoid(autoCaller.rc());
11702
11703 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11704
11705 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11706
11707 HRESULT rc = S_OK;
11708
11709 /* no attach/detach operations -- nothing to do */
11710 if (!mMediaData.isBackedUp())
11711 return;
11712
11713 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11714 bool fMediaNeedsLocking = false;
11715
11716 /* enumerate new attachments */
11717 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11718 it != mMediaData->mAttachments.end();
11719 ++it)
11720 {
11721 MediumAttachment *pAttach = *it;
11722
11723 pAttach->commit();
11724
11725 Medium* pMedium = pAttach->getMedium();
11726 bool fImplicit = pAttach->isImplicit();
11727
11728 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11729 (pMedium) ? pMedium->getName().c_str() : "NULL",
11730 fImplicit));
11731
11732 /** @todo convert all this Machine-based voodoo to MediumAttachment
11733 * based commit logic. */
11734 if (fImplicit)
11735 {
11736 /* convert implicit attachment to normal */
11737 pAttach->setImplicit(false);
11738
11739 if ( aOnline
11740 && pMedium
11741 && pAttach->getType() == DeviceType_HardDisk
11742 )
11743 {
11744 ComObjPtr<Medium> parent = pMedium->getParent();
11745 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11746
11747 /* update the appropriate lock list */
11748 MediumLockList *pMediumLockList;
11749 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11750 AssertComRC(rc);
11751 if (pMediumLockList)
11752 {
11753 /* unlock if there's a need to change the locking */
11754 if (!fMediaNeedsLocking)
11755 {
11756 rc = mData->mSession.mLockedMedia.Unlock();
11757 AssertComRC(rc);
11758 fMediaNeedsLocking = true;
11759 }
11760 rc = pMediumLockList->Update(parent, false);
11761 AssertComRC(rc);
11762 rc = pMediumLockList->Append(pMedium, true);
11763 AssertComRC(rc);
11764 }
11765 }
11766
11767 continue;
11768 }
11769
11770 if (pMedium)
11771 {
11772 /* was this medium attached before? */
11773 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11774 oldIt != oldAtts.end();
11775 ++oldIt)
11776 {
11777 MediumAttachment *pOldAttach = *oldIt;
11778 if (pOldAttach->getMedium() == pMedium)
11779 {
11780 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
11781
11782 /* yes: remove from old to avoid de-association */
11783 oldAtts.erase(oldIt);
11784 break;
11785 }
11786 }
11787 }
11788 }
11789
11790 /* enumerate remaining old attachments and de-associate from the
11791 * current machine state */
11792 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11793 it != oldAtts.end();
11794 ++it)
11795 {
11796 MediumAttachment *pAttach = *it;
11797 Medium* pMedium = pAttach->getMedium();
11798
11799 /* Detach only hard disks, since DVD/floppy media is detached
11800 * instantly in MountMedium. */
11801 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
11802 {
11803 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
11804
11805 /* now de-associate from the current machine state */
11806 rc = pMedium->removeBackReference(mData->mUuid);
11807 AssertComRC(rc);
11808
11809 if (aOnline)
11810 {
11811 /* unlock since medium is not used anymore */
11812 MediumLockList *pMediumLockList;
11813 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11814 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11815 {
11816 /* this happens for online snapshots, there the attachment
11817 * is changing, but only to a diff image created under
11818 * the old one, so there is no separate lock list */
11819 Assert(!pMediumLockList);
11820 }
11821 else
11822 {
11823 AssertComRC(rc);
11824 if (pMediumLockList)
11825 {
11826 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11827 AssertComRC(rc);
11828 }
11829 }
11830 }
11831 }
11832 }
11833
11834 /* take media locks again so that the locking state is consistent */
11835 if (fMediaNeedsLocking)
11836 {
11837 Assert(aOnline);
11838 rc = mData->mSession.mLockedMedia.Lock();
11839 AssertComRC(rc);
11840 }
11841
11842 /* commit the hard disk changes */
11843 mMediaData.commit();
11844
11845 if (isSessionMachine())
11846 {
11847 /*
11848 * Update the parent machine to point to the new owner.
11849 * This is necessary because the stored parent will point to the
11850 * session machine otherwise and cause crashes or errors later
11851 * when the session machine gets invalid.
11852 */
11853 /** @todo Change the MediumAttachment class to behave like any other
11854 * class in this regard by creating peer MediumAttachment
11855 * objects for session machines and share the data with the peer
11856 * machine.
11857 */
11858 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11859 it != mMediaData->mAttachments.end();
11860 ++it)
11861 {
11862 (*it)->updateParentMachine(mPeer);
11863 }
11864
11865 /* attach new data to the primary machine and reshare it */
11866 mPeer->mMediaData.attach(mMediaData);
11867 }
11868
11869 return;
11870}
11871
11872/**
11873 * Perform deferred deletion of implicitly created diffs.
11874 *
11875 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11876 * backed up).
11877 *
11878 * @note Locks this object for writing!
11879 */
11880void Machine::rollbackMedia()
11881{
11882 AutoCaller autoCaller(this);
11883 AssertComRCReturnVoid(autoCaller.rc());
11884
11885 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11886 LogFlowThisFunc(("Entering rollbackMedia\n"));
11887
11888 HRESULT rc = S_OK;
11889
11890 /* no attach/detach operations -- nothing to do */
11891 if (!mMediaData.isBackedUp())
11892 return;
11893
11894 /* enumerate new attachments */
11895 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11896 it != mMediaData->mAttachments.end();
11897 ++it)
11898 {
11899 MediumAttachment *pAttach = *it;
11900 /* Fix up the backrefs for DVD/floppy media. */
11901 if (pAttach->getType() != DeviceType_HardDisk)
11902 {
11903 Medium* pMedium = pAttach->getMedium();
11904 if (pMedium)
11905 {
11906 rc = pMedium->removeBackReference(mData->mUuid);
11907 AssertComRC(rc);
11908 }
11909 }
11910
11911 (*it)->rollback();
11912
11913 pAttach = *it;
11914 /* Fix up the backrefs for DVD/floppy media. */
11915 if (pAttach->getType() != DeviceType_HardDisk)
11916 {
11917 Medium* pMedium = pAttach->getMedium();
11918 if (pMedium)
11919 {
11920 rc = pMedium->addBackReference(mData->mUuid);
11921 AssertComRC(rc);
11922 }
11923 }
11924 }
11925
11926 /** @todo convert all this Machine-based voodoo to MediumAttachment
11927 * based rollback logic. */
11928 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11929
11930 return;
11931}
11932
11933/**
11934 * Returns true if the settings file is located in the directory named exactly
11935 * as the machine; this means, among other things, that the machine directory
11936 * should be auto-renamed.
11937 *
11938 * @param aSettingsDir if not NULL, the full machine settings file directory
11939 * name will be assigned there.
11940 *
11941 * @note Doesn't lock anything.
11942 * @note Not thread safe (must be called from this object's lock).
11943 */
11944bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11945{
11946 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11947 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11948 if (aSettingsDir)
11949 *aSettingsDir = strMachineDirName;
11950 strMachineDirName.stripPath(); // vmname
11951 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11952 strConfigFileOnly.stripPath() // vmname.vbox
11953 .stripExt(); // vmname
11954 /** @todo hack, make somehow use of ComposeMachineFilename */
11955 if (mUserData->s.fDirectoryIncludesUUID)
11956 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11957
11958 AssertReturn(!strMachineDirName.isEmpty(), false);
11959 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11960
11961 return strMachineDirName == strConfigFileOnly;
11962}
11963
11964/**
11965 * Discards all changes to machine settings.
11966 *
11967 * @param aNotify Whether to notify the direct session about changes or not.
11968 *
11969 * @note Locks objects for writing!
11970 */
11971void Machine::rollback(bool aNotify)
11972{
11973 AutoCaller autoCaller(this);
11974 AssertComRCReturn(autoCaller.rc(), (void)0);
11975
11976 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11977
11978 if (!mStorageControllers.isNull())
11979 {
11980 if (mStorageControllers.isBackedUp())
11981 {
11982 /* unitialize all new devices (absent in the backed up list). */
11983 StorageControllerList::const_iterator it = mStorageControllers->begin();
11984 StorageControllerList *backedList = mStorageControllers.backedUpData();
11985 while (it != mStorageControllers->end())
11986 {
11987 if ( std::find(backedList->begin(), backedList->end(), *it)
11988 == backedList->end()
11989 )
11990 {
11991 (*it)->uninit();
11992 }
11993 ++it;
11994 }
11995
11996 /* restore the list */
11997 mStorageControllers.rollback();
11998 }
11999
12000 /* rollback any changes to devices after restoring the list */
12001 if (mData->flModifications & IsModified_Storage)
12002 {
12003 StorageControllerList::const_iterator it = mStorageControllers->begin();
12004 while (it != mStorageControllers->end())
12005 {
12006 (*it)->rollback();
12007 ++it;
12008 }
12009 }
12010 }
12011
12012 if (!mUSBControllers.isNull())
12013 {
12014 if (mUSBControllers.isBackedUp())
12015 {
12016 /* unitialize all new devices (absent in the backed up list). */
12017 USBControllerList::const_iterator it = mUSBControllers->begin();
12018 USBControllerList *backedList = mUSBControllers.backedUpData();
12019 while (it != mUSBControllers->end())
12020 {
12021 if ( std::find(backedList->begin(), backedList->end(), *it)
12022 == backedList->end()
12023 )
12024 {
12025 (*it)->uninit();
12026 }
12027 ++it;
12028 }
12029
12030 /* restore the list */
12031 mUSBControllers.rollback();
12032 }
12033
12034 /* rollback any changes to devices after restoring the list */
12035 if (mData->flModifications & IsModified_USB)
12036 {
12037 USBControllerList::const_iterator it = mUSBControllers->begin();
12038 while (it != mUSBControllers->end())
12039 {
12040 (*it)->rollback();
12041 ++it;
12042 }
12043 }
12044 }
12045
12046 mUserData.rollback();
12047
12048 mHWData.rollback();
12049
12050 if (mData->flModifications & IsModified_Storage)
12051 rollbackMedia();
12052
12053 if (mBIOSSettings)
12054 mBIOSSettings->rollback();
12055
12056 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12057 mVRDEServer->rollback();
12058
12059 if (mAudioAdapter)
12060 mAudioAdapter->rollback();
12061
12062 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12063 mUSBDeviceFilters->rollback();
12064
12065 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12066 mBandwidthControl->rollback();
12067
12068 if (!mHWData.isNull())
12069 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12070 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12071 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12072 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12073
12074 if (mData->flModifications & IsModified_NetworkAdapters)
12075 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12076 if ( mNetworkAdapters[slot]
12077 && mNetworkAdapters[slot]->isModified())
12078 {
12079 mNetworkAdapters[slot]->rollback();
12080 networkAdapters[slot] = mNetworkAdapters[slot];
12081 }
12082
12083 if (mData->flModifications & IsModified_SerialPorts)
12084 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12085 if ( mSerialPorts[slot]
12086 && mSerialPorts[slot]->isModified())
12087 {
12088 mSerialPorts[slot]->rollback();
12089 serialPorts[slot] = mSerialPorts[slot];
12090 }
12091
12092 if (mData->flModifications & IsModified_ParallelPorts)
12093 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12094 if ( mParallelPorts[slot]
12095 && mParallelPorts[slot]->isModified())
12096 {
12097 mParallelPorts[slot]->rollback();
12098 parallelPorts[slot] = mParallelPorts[slot];
12099 }
12100
12101 if (aNotify)
12102 {
12103 /* inform the direct session about changes */
12104
12105 ComObjPtr<Machine> that = this;
12106 uint32_t flModifications = mData->flModifications;
12107 alock.release();
12108
12109 if (flModifications & IsModified_SharedFolders)
12110 that->onSharedFolderChange();
12111
12112 if (flModifications & IsModified_VRDEServer)
12113 that->onVRDEServerChange(/* aRestart */ TRUE);
12114 if (flModifications & IsModified_USB)
12115 that->onUSBControllerChange();
12116
12117 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
12118 if (networkAdapters[slot])
12119 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
12120 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
12121 if (serialPorts[slot])
12122 that->onSerialPortChange(serialPorts[slot]);
12123 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
12124 if (parallelPorts[slot])
12125 that->onParallelPortChange(parallelPorts[slot]);
12126
12127 if (flModifications & IsModified_Storage)
12128 that->onStorageControllerChange();
12129
12130#if 0
12131 if (flModifications & IsModified_BandwidthControl)
12132 that->onBandwidthControlChange();
12133#endif
12134 }
12135}
12136
12137/**
12138 * Commits all the changes to machine settings.
12139 *
12140 * Note that this operation is supposed to never fail.
12141 *
12142 * @note Locks this object and children for writing.
12143 */
12144void Machine::commit()
12145{
12146 AutoCaller autoCaller(this);
12147 AssertComRCReturnVoid(autoCaller.rc());
12148
12149 AutoCaller peerCaller(mPeer);
12150 AssertComRCReturnVoid(peerCaller.rc());
12151
12152 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12153
12154 /*
12155 * use safe commit to ensure Snapshot machines (that share mUserData)
12156 * will still refer to a valid memory location
12157 */
12158 mUserData.commitCopy();
12159
12160 mHWData.commit();
12161
12162 if (mMediaData.isBackedUp())
12163 commitMedia(Global::IsOnline(mData->mMachineState));
12164
12165 mBIOSSettings->commit();
12166 mVRDEServer->commit();
12167 mAudioAdapter->commit();
12168 mUSBDeviceFilters->commit();
12169 mBandwidthControl->commit();
12170
12171 /* Since mNetworkAdapters is a list which might have been changed (resized)
12172 * without using the Backupable<> template we need to handle the copying
12173 * of the list entries manually, including the creation of peers for the
12174 * new objects. */
12175 bool commitNetworkAdapters = false;
12176 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12177 if (mPeer)
12178 {
12179 /* commit everything, even the ones which will go away */
12180 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12181 mNetworkAdapters[slot]->commit();
12182 /* copy over the new entries, creating a peer and uninit the original */
12183 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12184 for (size_t slot = 0; slot < newSize; slot++)
12185 {
12186 /* look if this adapter has a peer device */
12187 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
12188 if (!peer)
12189 {
12190 /* no peer means the adapter is a newly created one;
12191 * create a peer owning data this data share it with */
12192 peer.createObject();
12193 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12194 }
12195 mPeer->mNetworkAdapters[slot] = peer;
12196 }
12197 /* uninit any no longer needed network adapters */
12198 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
12199 mNetworkAdapters[slot]->uninit();
12200 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
12201 {
12202 if (mPeer->mNetworkAdapters[slot])
12203 mPeer->mNetworkAdapters[slot]->uninit();
12204 }
12205 /* Keep the original network adapter count until this point, so that
12206 * discarding a chipset type change will not lose settings. */
12207 mNetworkAdapters.resize(newSize);
12208 mPeer->mNetworkAdapters.resize(newSize);
12209 }
12210 else
12211 {
12212 /* we have no peer (our parent is the newly created machine);
12213 * just commit changes to the network adapters */
12214 commitNetworkAdapters = true;
12215 }
12216 if (commitNetworkAdapters)
12217 {
12218 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12219 mNetworkAdapters[slot]->commit();
12220 }
12221
12222 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12223 mSerialPorts[slot]->commit();
12224 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12225 mParallelPorts[slot]->commit();
12226
12227 bool commitStorageControllers = false;
12228
12229 if (mStorageControllers.isBackedUp())
12230 {
12231 mStorageControllers.commit();
12232
12233 if (mPeer)
12234 {
12235 /* Commit all changes to new controllers (this will reshare data with
12236 * peers for those who have peers) */
12237 StorageControllerList *newList = new StorageControllerList();
12238 StorageControllerList::const_iterator it = mStorageControllers->begin();
12239 while (it != mStorageControllers->end())
12240 {
12241 (*it)->commit();
12242
12243 /* look if this controller has a peer device */
12244 ComObjPtr<StorageController> peer = (*it)->getPeer();
12245 if (!peer)
12246 {
12247 /* no peer means the device is a newly created one;
12248 * create a peer owning data this device share it with */
12249 peer.createObject();
12250 peer->init(mPeer, *it, true /* aReshare */);
12251 }
12252 else
12253 {
12254 /* remove peer from the old list */
12255 mPeer->mStorageControllers->remove(peer);
12256 }
12257 /* and add it to the new list */
12258 newList->push_back(peer);
12259
12260 ++it;
12261 }
12262
12263 /* uninit old peer's controllers that are left */
12264 it = mPeer->mStorageControllers->begin();
12265 while (it != mPeer->mStorageControllers->end())
12266 {
12267 (*it)->uninit();
12268 ++it;
12269 }
12270
12271 /* attach new list of controllers to our peer */
12272 mPeer->mStorageControllers.attach(newList);
12273 }
12274 else
12275 {
12276 /* we have no peer (our parent is the newly created machine);
12277 * just commit changes to devices */
12278 commitStorageControllers = true;
12279 }
12280 }
12281 else
12282 {
12283 /* the list of controllers itself is not changed,
12284 * just commit changes to controllers themselves */
12285 commitStorageControllers = true;
12286 }
12287
12288 if (commitStorageControllers)
12289 {
12290 StorageControllerList::const_iterator it = mStorageControllers->begin();
12291 while (it != mStorageControllers->end())
12292 {
12293 (*it)->commit();
12294 ++it;
12295 }
12296 }
12297
12298 bool commitUSBControllers = false;
12299
12300 if (mUSBControllers.isBackedUp())
12301 {
12302 mUSBControllers.commit();
12303
12304 if (mPeer)
12305 {
12306 /* Commit all changes to new controllers (this will reshare data with
12307 * peers for those who have peers) */
12308 USBControllerList *newList = new USBControllerList();
12309 USBControllerList::const_iterator it = mUSBControllers->begin();
12310 while (it != mUSBControllers->end())
12311 {
12312 (*it)->commit();
12313
12314 /* look if this controller has a peer device */
12315 ComObjPtr<USBController> peer = (*it)->getPeer();
12316 if (!peer)
12317 {
12318 /* no peer means the device is a newly created one;
12319 * create a peer owning data this device share it with */
12320 peer.createObject();
12321 peer->init(mPeer, *it, true /* aReshare */);
12322 }
12323 else
12324 {
12325 /* remove peer from the old list */
12326 mPeer->mUSBControllers->remove(peer);
12327 }
12328 /* and add it to the new list */
12329 newList->push_back(peer);
12330
12331 ++it;
12332 }
12333
12334 /* uninit old peer's controllers that are left */
12335 it = mPeer->mUSBControllers->begin();
12336 while (it != mPeer->mUSBControllers->end())
12337 {
12338 (*it)->uninit();
12339 ++it;
12340 }
12341
12342 /* attach new list of controllers to our peer */
12343 mPeer->mUSBControllers.attach(newList);
12344 }
12345 else
12346 {
12347 /* we have no peer (our parent is the newly created machine);
12348 * just commit changes to devices */
12349 commitUSBControllers = true;
12350 }
12351 }
12352 else
12353 {
12354 /* the list of controllers itself is not changed,
12355 * just commit changes to controllers themselves */
12356 commitUSBControllers = true;
12357 }
12358
12359 if (commitUSBControllers)
12360 {
12361 USBControllerList::const_iterator it = mUSBControllers->begin();
12362 while (it != mUSBControllers->end())
12363 {
12364 (*it)->commit();
12365 ++it;
12366 }
12367 }
12368
12369 if (isSessionMachine())
12370 {
12371 /* attach new data to the primary machine and reshare it */
12372 mPeer->mUserData.attach(mUserData);
12373 mPeer->mHWData.attach(mHWData);
12374 /* mMediaData is reshared by fixupMedia */
12375 // mPeer->mMediaData.attach(mMediaData);
12376 Assert(mPeer->mMediaData.data() == mMediaData.data());
12377 }
12378}
12379
12380/**
12381 * Copies all the hardware data from the given machine.
12382 *
12383 * Currently, only called when the VM is being restored from a snapshot. In
12384 * particular, this implies that the VM is not running during this method's
12385 * call.
12386 *
12387 * @note This method must be called from under this object's lock.
12388 *
12389 * @note This method doesn't call #commit(), so all data remains backed up and
12390 * unsaved.
12391 */
12392void Machine::copyFrom(Machine *aThat)
12393{
12394 AssertReturnVoid(!isSnapshotMachine());
12395 AssertReturnVoid(aThat->isSnapshotMachine());
12396
12397 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12398
12399 mHWData.assignCopy(aThat->mHWData);
12400
12401 // create copies of all shared folders (mHWData after attaching a copy
12402 // contains just references to original objects)
12403 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12404 it != mHWData->mSharedFolders.end();
12405 ++it)
12406 {
12407 ComObjPtr<SharedFolder> folder;
12408 folder.createObject();
12409 HRESULT rc = folder->initCopy(getMachine(), *it);
12410 AssertComRC(rc);
12411 *it = folder;
12412 }
12413
12414 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
12415 mVRDEServer->copyFrom(aThat->mVRDEServer);
12416 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
12417 mUSBDeviceFilters->copyFrom(aThat->mUSBDeviceFilters);
12418 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
12419
12420 /* create private copies of all controllers */
12421 mStorageControllers.backup();
12422 mStorageControllers->clear();
12423 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12424 it != aThat->mStorageControllers->end();
12425 ++it)
12426 {
12427 ComObjPtr<StorageController> ctrl;
12428 ctrl.createObject();
12429 ctrl->initCopy(this, *it);
12430 mStorageControllers->push_back(ctrl);
12431 }
12432
12433 /* create private copies of all USB controllers */
12434 mUSBControllers.backup();
12435 mUSBControllers->clear();
12436 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12437 it != aThat->mUSBControllers->end();
12438 ++it)
12439 {
12440 ComObjPtr<USBController> ctrl;
12441 ctrl.createObject();
12442 ctrl->initCopy(this, *it);
12443 mUSBControllers->push_back(ctrl);
12444 }
12445
12446 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12447 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12448 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
12449 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12450 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
12451 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12452 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
12453}
12454
12455/**
12456 * Returns whether the given storage controller is hotplug capable.
12457 *
12458 * @returns true if the controller supports hotplugging
12459 * false otherwise.
12460 * @param enmCtrlType The controller type to check for.
12461 */
12462bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12463{
12464 switch (enmCtrlType)
12465 {
12466 case StorageControllerType_IntelAhci:
12467 return true;
12468 case StorageControllerType_LsiLogic:
12469 case StorageControllerType_LsiLogicSas:
12470 case StorageControllerType_BusLogic:
12471 case StorageControllerType_PIIX3:
12472 case StorageControllerType_PIIX4:
12473 case StorageControllerType_ICH6:
12474 case StorageControllerType_I82078:
12475 default:
12476 return false;
12477 }
12478}
12479
12480#ifdef VBOX_WITH_RESOURCE_USAGE_API
12481
12482void Machine::getDiskList(MediaList &list)
12483{
12484 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12485 it != mMediaData->mAttachments.end();
12486 ++it)
12487 {
12488 MediumAttachment* pAttach = *it;
12489 /* just in case */
12490 AssertStmt(pAttach, continue);
12491
12492 AutoCaller localAutoCallerA(pAttach);
12493 if (FAILED(localAutoCallerA.rc())) continue;
12494
12495 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12496
12497 if (pAttach->getType() == DeviceType_HardDisk)
12498 list.push_back(pAttach->getMedium());
12499 }
12500}
12501
12502void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12503{
12504 AssertReturnVoid(isWriteLockOnCurrentThread());
12505 AssertPtrReturnVoid(aCollector);
12506
12507 pm::CollectorHAL *hal = aCollector->getHAL();
12508 /* Create sub metrics */
12509 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12510 "Percentage of processor time spent in user mode by the VM process.");
12511 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12512 "Percentage of processor time spent in kernel mode by the VM process.");
12513 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12514 "Size of resident portion of VM process in memory.");
12515 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12516 "Actual size of all VM disks combined.");
12517 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12518 "Network receive rate.");
12519 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12520 "Network transmit rate.");
12521 /* Create and register base metrics */
12522 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12523 cpuLoadUser, cpuLoadKernel);
12524 aCollector->registerBaseMetric(cpuLoad);
12525 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12526 ramUsageUsed);
12527 aCollector->registerBaseMetric(ramUsage);
12528 MediaList disks;
12529 getDiskList(disks);
12530 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12531 diskUsageUsed);
12532 aCollector->registerBaseMetric(diskUsage);
12533
12534 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12535 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12536 new pm::AggregateAvg()));
12537 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12538 new pm::AggregateMin()));
12539 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12540 new pm::AggregateMax()));
12541 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12542 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12543 new pm::AggregateAvg()));
12544 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12545 new pm::AggregateMin()));
12546 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12547 new pm::AggregateMax()));
12548
12549 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12550 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12551 new pm::AggregateAvg()));
12552 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12553 new pm::AggregateMin()));
12554 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12555 new pm::AggregateMax()));
12556
12557 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12558 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12559 new pm::AggregateAvg()));
12560 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12561 new pm::AggregateMin()));
12562 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12563 new pm::AggregateMax()));
12564
12565
12566 /* Guest metrics collector */
12567 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12568 aCollector->registerGuest(mCollectorGuest);
12569 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12570 this, __PRETTY_FUNCTION__, mCollectorGuest));
12571
12572 /* Create sub metrics */
12573 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12574 "Percentage of processor time spent in user mode as seen by the guest.");
12575 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12576 "Percentage of processor time spent in kernel mode as seen by the guest.");
12577 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12578 "Percentage of processor time spent idling as seen by the guest.");
12579
12580 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12581 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12582 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12583 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12584 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12585 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12586
12587 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12588
12589 /* Create and register base metrics */
12590 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12591 machineNetRx, machineNetTx);
12592 aCollector->registerBaseMetric(machineNetRate);
12593
12594 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12595 guestLoadUser, guestLoadKernel, guestLoadIdle);
12596 aCollector->registerBaseMetric(guestCpuLoad);
12597
12598 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12599 guestMemTotal, guestMemFree,
12600 guestMemBalloon, guestMemShared,
12601 guestMemCache, guestPagedTotal);
12602 aCollector->registerBaseMetric(guestCpuMem);
12603
12604 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12605 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12606 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12607 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12608
12609 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12610 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12611 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12612 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12613
12614 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12615 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12616 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12617 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12618
12619 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12620 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12621 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12622 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12623
12624 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12625 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12626 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12627 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12628
12629 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12630 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12631 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12632 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12633
12634 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12635 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12636 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12637 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12638
12639 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12640 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12641 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12642 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12643
12644 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12645 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12646 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12647 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12648
12649 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12650 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12651 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12652 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12653
12654 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12655 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12656 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12657 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12658}
12659
12660void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12661{
12662 AssertReturnVoid(isWriteLockOnCurrentThread());
12663
12664 if (aCollector)
12665 {
12666 aCollector->unregisterMetricsFor(aMachine);
12667 aCollector->unregisterBaseMetricsFor(aMachine);
12668 }
12669}
12670
12671#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12672
12673
12674////////////////////////////////////////////////////////////////////////////////
12675
12676DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12677
12678HRESULT SessionMachine::FinalConstruct()
12679{
12680 LogFlowThisFunc(("\n"));
12681
12682#if defined(RT_OS_WINDOWS)
12683 mIPCSem = NULL;
12684#elif defined(RT_OS_OS2)
12685 mIPCSem = NULLHANDLE;
12686#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12687 mIPCSem = -1;
12688#else
12689# error "Port me!"
12690#endif
12691
12692 return BaseFinalConstruct();
12693}
12694
12695void SessionMachine::FinalRelease()
12696{
12697 LogFlowThisFunc(("\n"));
12698
12699 uninit(Uninit::Unexpected);
12700
12701 BaseFinalRelease();
12702}
12703
12704/**
12705 * @note Must be called only by Machine::openSession() from its own write lock.
12706 */
12707HRESULT SessionMachine::init(Machine *aMachine)
12708{
12709 LogFlowThisFuncEnter();
12710 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12711
12712 AssertReturn(aMachine, E_INVALIDARG);
12713
12714 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12715
12716 /* Enclose the state transition NotReady->InInit->Ready */
12717 AutoInitSpan autoInitSpan(this);
12718 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12719
12720 /* create the interprocess semaphore */
12721#if defined(RT_OS_WINDOWS)
12722 mIPCSemName = aMachine->mData->m_strConfigFileFull;
12723 for (size_t i = 0; i < mIPCSemName.length(); i++)
12724 if (mIPCSemName.raw()[i] == '\\')
12725 mIPCSemName.raw()[i] = '/';
12726 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
12727 ComAssertMsgRet(mIPCSem,
12728 ("Cannot create IPC mutex '%ls', err=%d",
12729 mIPCSemName.raw(), ::GetLastError()),
12730 E_FAIL);
12731#elif defined(RT_OS_OS2)
12732 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
12733 aMachine->mData->mUuid.raw());
12734 mIPCSemName = ipcSem;
12735 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
12736 ComAssertMsgRet(arc == NO_ERROR,
12737 ("Cannot create IPC mutex '%s', arc=%ld",
12738 ipcSem.c_str(), arc),
12739 E_FAIL);
12740#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12741# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12742# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
12743 /** @todo Check that this still works correctly. */
12744 AssertCompileSize(key_t, 8);
12745# else
12746 AssertCompileSize(key_t, 4);
12747# endif
12748 key_t key;
12749 mIPCSem = -1;
12750 mIPCKey = "0";
12751 for (uint32_t i = 0; i < 1 << 24; i++)
12752 {
12753 key = ((uint32_t)'V' << 24) | i;
12754 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
12755 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
12756 {
12757 mIPCSem = sem;
12758 if (sem >= 0)
12759 mIPCKey = BstrFmt("%u", key);
12760 break;
12761 }
12762 }
12763# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12764 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
12765 char *pszSemName = NULL;
12766 RTStrUtf8ToCurrentCP(&pszSemName, semName);
12767 key_t key = ::ftok(pszSemName, 'V');
12768 RTStrFree(pszSemName);
12769
12770 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
12771# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12772
12773 int errnoSave = errno;
12774 if (mIPCSem < 0 && errnoSave == ENOSYS)
12775 {
12776 setError(E_FAIL,
12777 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
12778 "support for SysV IPC. Check the host kernel configuration for "
12779 "CONFIG_SYSVIPC=y"));
12780 return E_FAIL;
12781 }
12782 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
12783 * the IPC semaphores */
12784 if (mIPCSem < 0 && errnoSave == ENOSPC)
12785 {
12786#ifdef RT_OS_LINUX
12787 setError(E_FAIL,
12788 tr("Cannot create IPC semaphore because the system limit for the "
12789 "maximum number of semaphore sets (SEMMNI), or the system wide "
12790 "maximum number of semaphores (SEMMNS) would be exceeded. The "
12791 "current set of SysV IPC semaphores can be determined from "
12792 "the file /proc/sysvipc/sem"));
12793#else
12794 setError(E_FAIL,
12795 tr("Cannot create IPC semaphore because the system-imposed limit "
12796 "on the maximum number of allowed semaphores or semaphore "
12797 "identifiers system-wide would be exceeded"));
12798#endif
12799 return E_FAIL;
12800 }
12801 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
12802 E_FAIL);
12803 /* set the initial value to 1 */
12804 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
12805 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
12806 E_FAIL);
12807#else
12808# error "Port me!"
12809#endif
12810
12811 /* memorize the peer Machine */
12812 unconst(mPeer) = aMachine;
12813 /* share the parent pointer */
12814 unconst(mParent) = aMachine->mParent;
12815
12816 /* take the pointers to data to share */
12817 mData.share(aMachine->mData);
12818 mSSData.share(aMachine->mSSData);
12819
12820 mUserData.share(aMachine->mUserData);
12821 mHWData.share(aMachine->mHWData);
12822 mMediaData.share(aMachine->mMediaData);
12823
12824 mStorageControllers.allocate();
12825 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12826 it != aMachine->mStorageControllers->end();
12827 ++it)
12828 {
12829 ComObjPtr<StorageController> ctl;
12830 ctl.createObject();
12831 ctl->init(this, *it);
12832 mStorageControllers->push_back(ctl);
12833 }
12834
12835 mUSBControllers.allocate();
12836 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12837 it != aMachine->mUSBControllers->end();
12838 ++it)
12839 {
12840 ComObjPtr<USBController> ctl;
12841 ctl.createObject();
12842 ctl->init(this, *it);
12843 mUSBControllers->push_back(ctl);
12844 }
12845
12846 unconst(mBIOSSettings).createObject();
12847 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12848 /* create another VRDEServer object that will be mutable */
12849 unconst(mVRDEServer).createObject();
12850 mVRDEServer->init(this, aMachine->mVRDEServer);
12851 /* create another audio adapter object that will be mutable */
12852 unconst(mAudioAdapter).createObject();
12853 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12854 /* create a list of serial ports that will be mutable */
12855 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12856 {
12857 unconst(mSerialPorts[slot]).createObject();
12858 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12859 }
12860 /* create a list of parallel ports that will be mutable */
12861 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12862 {
12863 unconst(mParallelPorts[slot]).createObject();
12864 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12865 }
12866
12867 /* create another USB device filters object that will be mutable */
12868 unconst(mUSBDeviceFilters).createObject();
12869 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12870
12871 /* create a list of network adapters that will be mutable */
12872 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12873 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12874 {
12875 unconst(mNetworkAdapters[slot]).createObject();
12876 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12877 }
12878
12879 /* create another bandwidth control object that will be mutable */
12880 unconst(mBandwidthControl).createObject();
12881 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12882
12883 /* default is to delete saved state on Saved -> PoweredOff transition */
12884 mRemoveSavedState = true;
12885
12886 /* Confirm a successful initialization when it's the case */
12887 autoInitSpan.setSucceeded();
12888
12889 LogFlowThisFuncLeave();
12890 return S_OK;
12891}
12892
12893/**
12894 * Uninitializes this session object. If the reason is other than
12895 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
12896 *
12897 * @param aReason uninitialization reason
12898 *
12899 * @note Locks mParent + this object for writing.
12900 */
12901void SessionMachine::uninit(Uninit::Reason aReason)
12902{
12903 LogFlowThisFuncEnter();
12904 LogFlowThisFunc(("reason=%d\n", aReason));
12905
12906 /*
12907 * Strongly reference ourselves to prevent this object deletion after
12908 * mData->mSession.mMachine.setNull() below (which can release the last
12909 * reference and call the destructor). Important: this must be done before
12910 * accessing any members (and before AutoUninitSpan that does it as well).
12911 * This self reference will be released as the very last step on return.
12912 */
12913 ComObjPtr<SessionMachine> selfRef = this;
12914
12915 /* Enclose the state transition Ready->InUninit->NotReady */
12916 AutoUninitSpan autoUninitSpan(this);
12917 if (autoUninitSpan.uninitDone())
12918 {
12919 LogFlowThisFunc(("Already uninitialized\n"));
12920 LogFlowThisFuncLeave();
12921 return;
12922 }
12923
12924 if (autoUninitSpan.initFailed())
12925 {
12926 /* We've been called by init() because it's failed. It's not really
12927 * necessary (nor it's safe) to perform the regular uninit sequence
12928 * below, the following is enough.
12929 */
12930 LogFlowThisFunc(("Initialization failed.\n"));
12931#if defined(RT_OS_WINDOWS)
12932 if (mIPCSem)
12933 ::CloseHandle(mIPCSem);
12934 mIPCSem = NULL;
12935#elif defined(RT_OS_OS2)
12936 if (mIPCSem != NULLHANDLE)
12937 ::DosCloseMutexSem(mIPCSem);
12938 mIPCSem = NULLHANDLE;
12939#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12940 if (mIPCSem >= 0)
12941 ::semctl(mIPCSem, 0, IPC_RMID);
12942 mIPCSem = -1;
12943# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12944 mIPCKey = "0";
12945# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12946#else
12947# error "Port me!"
12948#endif
12949 uninitDataAndChildObjects();
12950 mData.free();
12951 unconst(mParent) = NULL;
12952 unconst(mPeer) = NULL;
12953 LogFlowThisFuncLeave();
12954 return;
12955 }
12956
12957 MachineState_T lastState;
12958 {
12959 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12960 lastState = mData->mMachineState;
12961 }
12962 NOREF(lastState);
12963
12964#ifdef VBOX_WITH_USB
12965 // release all captured USB devices, but do this before requesting the locks below
12966 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12967 {
12968 /* Console::captureUSBDevices() is called in the VM process only after
12969 * setting the machine state to Starting or Restoring.
12970 * Console::detachAllUSBDevices() will be called upon successful
12971 * termination. So, we need to release USB devices only if there was
12972 * an abnormal termination of a running VM.
12973 *
12974 * This is identical to SessionMachine::DetachAllUSBDevices except
12975 * for the aAbnormal argument. */
12976 HRESULT rc = mUSBDeviceFilters->notifyProxy(false /* aInsertFilters */);
12977 AssertComRC(rc);
12978 NOREF(rc);
12979
12980 USBProxyService *service = mParent->host()->usbProxyService();
12981 if (service)
12982 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12983 }
12984#endif /* VBOX_WITH_USB */
12985
12986 // we need to lock this object in uninit() because the lock is shared
12987 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12988 // and others need mParent lock, and USB needs host lock.
12989 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12990
12991#ifdef VBOX_WITH_RESOURCE_USAGE_API
12992 /*
12993 * It is safe to call Machine::unregisterMetrics() here because
12994 * PerformanceCollector::samplerCallback no longer accesses guest methods
12995 * holding the lock.
12996 */
12997 unregisterMetrics(mParent->performanceCollector(), mPeer);
12998 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12999 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
13000 this, __PRETTY_FUNCTION__, mCollectorGuest));
13001 if (mCollectorGuest)
13002 {
13003 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
13004 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
13005 mCollectorGuest = NULL;
13006 }
13007#endif
13008
13009 if (aReason == Uninit::Abnormal)
13010 {
13011 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
13012 Global::IsOnlineOrTransient(lastState)));
13013
13014 /* reset the state to Aborted */
13015 if (mData->mMachineState != MachineState_Aborted)
13016 setMachineState(MachineState_Aborted);
13017 }
13018
13019 // any machine settings modified?
13020 if (mData->flModifications)
13021 {
13022 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
13023 rollback(false /* aNotify */);
13024 }
13025
13026 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
13027 || !mConsoleTaskData.mSnapshot);
13028 if (!mConsoleTaskData.strStateFilePath.isEmpty())
13029 {
13030 LogWarningThisFunc(("canceling failed save state request!\n"));
13031 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
13032 }
13033 else if (!mConsoleTaskData.mSnapshot.isNull())
13034 {
13035 LogWarningThisFunc(("canceling untaken snapshot!\n"));
13036
13037 /* delete all differencing hard disks created (this will also attach
13038 * their parents back by rolling back mMediaData) */
13039 rollbackMedia();
13040
13041 // delete the saved state file (it might have been already created)
13042 // AFTER killing the snapshot so that releaseSavedStateFile() won't
13043 // think it's still in use
13044 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
13045 mConsoleTaskData.mSnapshot->uninit();
13046 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
13047 }
13048
13049 if (!mData->mSession.mType.isEmpty())
13050 {
13051 /* mType is not null when this machine's process has been started by
13052 * Machine::LaunchVMProcess(), therefore it is our child. We
13053 * need to queue the PID to reap the process (and avoid zombies on
13054 * Linux). */
13055 Assert(mData->mSession.mPID != NIL_RTPROCESS);
13056 mParent->addProcessToReap(mData->mSession.mPID);
13057 }
13058
13059 mData->mSession.mPID = NIL_RTPROCESS;
13060
13061 if (aReason == Uninit::Unexpected)
13062 {
13063 /* Uninitialization didn't come from #checkForDeath(), so tell the
13064 * client watcher thread to update the set of machines that have open
13065 * sessions. */
13066 mParent->updateClientWatcher();
13067 }
13068
13069 /* uninitialize all remote controls */
13070 if (mData->mSession.mRemoteControls.size())
13071 {
13072 LogFlowThisFunc(("Closing remote sessions (%d):\n",
13073 mData->mSession.mRemoteControls.size()));
13074
13075 Data::Session::RemoteControlList::iterator it =
13076 mData->mSession.mRemoteControls.begin();
13077 while (it != mData->mSession.mRemoteControls.end())
13078 {
13079 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
13080 HRESULT rc = (*it)->Uninitialize();
13081 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
13082 if (FAILED(rc))
13083 LogWarningThisFunc(("Forgot to close the remote session?\n"));
13084 ++it;
13085 }
13086 mData->mSession.mRemoteControls.clear();
13087 }
13088
13089 /*
13090 * An expected uninitialization can come only from #checkForDeath().
13091 * Otherwise it means that something's gone really wrong (for example,
13092 * the Session implementation has released the VirtualBox reference
13093 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
13094 * etc). However, it's also possible, that the client releases the IPC
13095 * semaphore correctly (i.e. before it releases the VirtualBox reference),
13096 * but the VirtualBox release event comes first to the server process.
13097 * This case is practically possible, so we should not assert on an
13098 * unexpected uninit, just log a warning.
13099 */
13100
13101 if ((aReason == Uninit::Unexpected))
13102 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
13103
13104 if (aReason != Uninit::Normal)
13105 {
13106 mData->mSession.mDirectControl.setNull();
13107 }
13108 else
13109 {
13110 /* this must be null here (see #OnSessionEnd()) */
13111 Assert(mData->mSession.mDirectControl.isNull());
13112 Assert(mData->mSession.mState == SessionState_Unlocking);
13113 Assert(!mData->mSession.mProgress.isNull());
13114 }
13115 if (mData->mSession.mProgress)
13116 {
13117 if (aReason == Uninit::Normal)
13118 mData->mSession.mProgress->notifyComplete(S_OK);
13119 else
13120 mData->mSession.mProgress->notifyComplete(E_FAIL,
13121 COM_IIDOF(ISession),
13122 getComponentName(),
13123 tr("The VM session was aborted"));
13124 mData->mSession.mProgress.setNull();
13125 }
13126
13127 /* remove the association between the peer machine and this session machine */
13128 Assert( (SessionMachine*)mData->mSession.mMachine == this
13129 || aReason == Uninit::Unexpected);
13130
13131 /* reset the rest of session data */
13132 mData->mSession.mMachine.setNull();
13133 mData->mSession.mState = SessionState_Unlocked;
13134 mData->mSession.mType.setNull();
13135
13136 /* close the interprocess semaphore before leaving the exclusive lock */
13137#if defined(RT_OS_WINDOWS)
13138 if (mIPCSem)
13139 ::CloseHandle(mIPCSem);
13140 mIPCSem = NULL;
13141#elif defined(RT_OS_OS2)
13142 if (mIPCSem != NULLHANDLE)
13143 ::DosCloseMutexSem(mIPCSem);
13144 mIPCSem = NULLHANDLE;
13145#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
13146 if (mIPCSem >= 0)
13147 ::semctl(mIPCSem, 0, IPC_RMID);
13148 mIPCSem = -1;
13149# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
13150 mIPCKey = "0";
13151# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
13152#else
13153# error "Port me!"
13154#endif
13155
13156 /* fire an event */
13157 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
13158
13159 uninitDataAndChildObjects();
13160
13161 /* free the essential data structure last */
13162 mData.free();
13163
13164 /* release the exclusive lock before setting the below two to NULL */
13165 multilock.release();
13166
13167 unconst(mParent) = NULL;
13168 unconst(mPeer) = NULL;
13169
13170 LogFlowThisFuncLeave();
13171}
13172
13173// util::Lockable interface
13174////////////////////////////////////////////////////////////////////////////////
13175
13176/**
13177 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13178 * with the primary Machine instance (mPeer).
13179 */
13180RWLockHandle *SessionMachine::lockHandle() const
13181{
13182 AssertReturn(mPeer != NULL, NULL);
13183 return mPeer->lockHandle();
13184}
13185
13186// IInternalMachineControl methods
13187////////////////////////////////////////////////////////////////////////////////
13188
13189/**
13190 * Passes collected guest statistics to performance collector object
13191 */
13192STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13193 ULONG aCpuKernel, ULONG aCpuIdle,
13194 ULONG aMemTotal, ULONG aMemFree,
13195 ULONG aMemBalloon, ULONG aMemShared,
13196 ULONG aMemCache, ULONG aPageTotal,
13197 ULONG aAllocVMM, ULONG aFreeVMM,
13198 ULONG aBalloonedVMM, ULONG aSharedVMM,
13199 ULONG aVmNetRx, ULONG aVmNetTx)
13200{
13201#ifdef VBOX_WITH_RESOURCE_USAGE_API
13202 if (mCollectorGuest)
13203 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13204 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13205 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13206 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13207
13208 return S_OK;
13209#else
13210 NOREF(aValidStats);
13211 NOREF(aCpuUser);
13212 NOREF(aCpuKernel);
13213 NOREF(aCpuIdle);
13214 NOREF(aMemTotal);
13215 NOREF(aMemFree);
13216 NOREF(aMemBalloon);
13217 NOREF(aMemShared);
13218 NOREF(aMemCache);
13219 NOREF(aPageTotal);
13220 NOREF(aAllocVMM);
13221 NOREF(aFreeVMM);
13222 NOREF(aBalloonedVMM);
13223 NOREF(aSharedVMM);
13224 NOREF(aVmNetRx);
13225 NOREF(aVmNetTx);
13226 return E_NOTIMPL;
13227#endif
13228}
13229
13230/**
13231 * @note Locks this object for writing.
13232 */
13233STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
13234{
13235 AutoCaller autoCaller(this);
13236 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13237
13238 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13239
13240 mRemoveSavedState = aRemove;
13241
13242 return S_OK;
13243}
13244
13245/**
13246 * @note Locks the same as #setMachineState() does.
13247 */
13248STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
13249{
13250 return setMachineState(aMachineState);
13251}
13252
13253/**
13254 * @note Locks this object for reading.
13255 */
13256STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
13257{
13258 AutoCaller autoCaller(this);
13259 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13260
13261 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13262
13263#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
13264 mIPCSemName.cloneTo(aId);
13265 return S_OK;
13266#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
13267# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
13268 mIPCKey.cloneTo(aId);
13269# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
13270 mData->m_strConfigFileFull.cloneTo(aId);
13271# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
13272 return S_OK;
13273#else
13274# error "Port me!"
13275#endif
13276}
13277
13278/**
13279 * @note Locks this object for writing.
13280 */
13281STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
13282{
13283 LogFlowThisFunc(("aProgress=%p\n", aProgress));
13284 AutoCaller autoCaller(this);
13285 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13286
13287 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13288
13289 if (mData->mSession.mState != SessionState_Locked)
13290 return VBOX_E_INVALID_OBJECT_STATE;
13291
13292 if (!mData->mSession.mProgress.isNull())
13293 mData->mSession.mProgress->setOtherProgressObject(aProgress);
13294
13295 LogFlowThisFunc(("returns S_OK.\n"));
13296 return S_OK;
13297}
13298
13299/**
13300 * @note Locks this object for writing.
13301 */
13302STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
13303{
13304 AutoCaller autoCaller(this);
13305 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13306
13307 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13308
13309 if (mData->mSession.mState != SessionState_Locked)
13310 return VBOX_E_INVALID_OBJECT_STATE;
13311
13312 /* Finalize the LaunchVMProcess progress object. */
13313 if (mData->mSession.mProgress)
13314 {
13315 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
13316 mData->mSession.mProgress.setNull();
13317 }
13318
13319 if (SUCCEEDED((HRESULT)iResult))
13320 {
13321#ifdef VBOX_WITH_RESOURCE_USAGE_API
13322 /* The VM has been powered up successfully, so it makes sense
13323 * now to offer the performance metrics for a running machine
13324 * object. Doing it earlier wouldn't be safe. */
13325 registerMetrics(mParent->performanceCollector(), mPeer,
13326 mData->mSession.mPID);
13327#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13328 }
13329
13330 return S_OK;
13331}
13332
13333/**
13334 * @note Locks this object for writing.
13335 */
13336STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
13337{
13338 LogFlowThisFuncEnter();
13339
13340 CheckComArgOutPointerValid(aProgress);
13341
13342 AutoCaller autoCaller(this);
13343 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13344
13345 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13346
13347 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13348 E_FAIL);
13349
13350 /* create a progress object to track operation completion */
13351 ComObjPtr<Progress> pProgress;
13352 pProgress.createObject();
13353 pProgress->init(getVirtualBox(),
13354 static_cast<IMachine *>(this) /* aInitiator */,
13355 Bstr(tr("Stopping the virtual machine")).raw(),
13356 FALSE /* aCancelable */);
13357
13358 /* fill in the console task data */
13359 mConsoleTaskData.mLastState = mData->mMachineState;
13360 mConsoleTaskData.mProgress = pProgress;
13361
13362 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13363 setMachineState(MachineState_Stopping);
13364
13365 pProgress.queryInterfaceTo(aProgress);
13366
13367 return S_OK;
13368}
13369
13370/**
13371 * @note Locks this object for writing.
13372 */
13373STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
13374{
13375 LogFlowThisFuncEnter();
13376
13377 AutoCaller autoCaller(this);
13378 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13379
13380 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13381
13382 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
13383 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
13384 && mConsoleTaskData.mLastState != MachineState_Null,
13385 E_FAIL);
13386
13387 /*
13388 * On failure, set the state to the state we had when BeginPoweringDown()
13389 * was called (this is expected by Console::PowerDown() and the associated
13390 * task). On success the VM process already changed the state to
13391 * MachineState_PoweredOff, so no need to do anything.
13392 */
13393 if (FAILED(iResult))
13394 setMachineState(mConsoleTaskData.mLastState);
13395
13396 /* notify the progress object about operation completion */
13397 Assert(mConsoleTaskData.mProgress);
13398 if (SUCCEEDED(iResult))
13399 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13400 else
13401 {
13402 Utf8Str strErrMsg(aErrMsg);
13403 if (strErrMsg.length())
13404 mConsoleTaskData.mProgress->notifyComplete(iResult,
13405 COM_IIDOF(ISession),
13406 getComponentName(),
13407 strErrMsg.c_str());
13408 else
13409 mConsoleTaskData.mProgress->notifyComplete(iResult);
13410 }
13411
13412 /* clear out the temporary saved state data */
13413 mConsoleTaskData.mLastState = MachineState_Null;
13414 mConsoleTaskData.mProgress.setNull();
13415
13416 LogFlowThisFuncLeave();
13417 return S_OK;
13418}
13419
13420
13421/**
13422 * Goes through the USB filters of the given machine to see if the given
13423 * device matches any filter or not.
13424 *
13425 * @note Locks the same as USBController::hasMatchingFilter() does.
13426 */
13427STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
13428 BOOL *aMatched,
13429 ULONG *aMaskedIfs)
13430{
13431 LogFlowThisFunc(("\n"));
13432
13433 CheckComArgNotNull(aUSBDevice);
13434 CheckComArgOutPointerValid(aMatched);
13435
13436 AutoCaller autoCaller(this);
13437 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13438
13439#ifdef VBOX_WITH_USB
13440 *aMatched = mUSBDeviceFilters->hasMatchingFilter(aUSBDevice, aMaskedIfs);
13441#else
13442 NOREF(aUSBDevice);
13443 NOREF(aMaskedIfs);
13444 *aMatched = FALSE;
13445#endif
13446
13447 return S_OK;
13448}
13449
13450/**
13451 * @note Locks the same as Host::captureUSBDevice() does.
13452 */
13453STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
13454{
13455 LogFlowThisFunc(("\n"));
13456
13457 AutoCaller autoCaller(this);
13458 AssertComRCReturnRC(autoCaller.rc());
13459
13460#ifdef VBOX_WITH_USB
13461 /* if captureDeviceForVM() fails, it must have set extended error info */
13462 clearError();
13463 MultiResult rc = mParent->host()->checkUSBProxyService();
13464 if (FAILED(rc)) return rc;
13465
13466 USBProxyService *service = mParent->host()->usbProxyService();
13467 AssertReturn(service, E_FAIL);
13468 return service->captureDeviceForVM(this, Guid(aId).ref());
13469#else
13470 NOREF(aId);
13471 return E_NOTIMPL;
13472#endif
13473}
13474
13475/**
13476 * @note Locks the same as Host::detachUSBDevice() does.
13477 */
13478STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
13479{
13480 LogFlowThisFunc(("\n"));
13481
13482 AutoCaller autoCaller(this);
13483 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13484
13485#ifdef VBOX_WITH_USB
13486 USBProxyService *service = mParent->host()->usbProxyService();
13487 AssertReturn(service, E_FAIL);
13488 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
13489#else
13490 NOREF(aId);
13491 NOREF(aDone);
13492 return E_NOTIMPL;
13493#endif
13494}
13495
13496/**
13497 * Inserts all machine filters to the USB proxy service and then calls
13498 * Host::autoCaptureUSBDevices().
13499 *
13500 * Called by Console from the VM process upon VM startup.
13501 *
13502 * @note Locks what called methods lock.
13503 */
13504STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
13505{
13506 LogFlowThisFunc(("\n"));
13507
13508 AutoCaller autoCaller(this);
13509 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13510
13511#ifdef VBOX_WITH_USB
13512 HRESULT rc = mUSBDeviceFilters->notifyProxy(true /* aInsertFilters */);
13513 AssertComRC(rc);
13514 NOREF(rc);
13515
13516 USBProxyService *service = mParent->host()->usbProxyService();
13517 AssertReturn(service, E_FAIL);
13518 return service->autoCaptureDevicesForVM(this);
13519#else
13520 return S_OK;
13521#endif
13522}
13523
13524/**
13525 * Removes all machine filters from the USB proxy service and then calls
13526 * Host::detachAllUSBDevices().
13527 *
13528 * Called by Console from the VM process upon normal VM termination or by
13529 * SessionMachine::uninit() upon abnormal VM termination (from under the
13530 * Machine/SessionMachine lock).
13531 *
13532 * @note Locks what called methods lock.
13533 */
13534STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
13535{
13536 LogFlowThisFunc(("\n"));
13537
13538 AutoCaller autoCaller(this);
13539 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13540
13541#ifdef VBOX_WITH_USB
13542 HRESULT rc = mUSBDeviceFilters->notifyProxy(false /* aInsertFilters */);
13543 AssertComRC(rc);
13544 NOREF(rc);
13545
13546 USBProxyService *service = mParent->host()->usbProxyService();
13547 AssertReturn(service, E_FAIL);
13548 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13549#else
13550 NOREF(aDone);
13551 return S_OK;
13552#endif
13553}
13554
13555/**
13556 * @note Locks this object for writing.
13557 */
13558STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
13559 IProgress **aProgress)
13560{
13561 LogFlowThisFuncEnter();
13562
13563 AssertReturn(aSession, E_INVALIDARG);
13564 AssertReturn(aProgress, E_INVALIDARG);
13565
13566 AutoCaller autoCaller(this);
13567
13568 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
13569 /*
13570 * We don't assert below because it might happen that a non-direct session
13571 * informs us it is closed right after we've been uninitialized -- it's ok.
13572 */
13573 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13574
13575 /* get IInternalSessionControl interface */
13576 ComPtr<IInternalSessionControl> control(aSession);
13577
13578 ComAssertRet(!control.isNull(), E_INVALIDARG);
13579
13580 /* Creating a Progress object requires the VirtualBox lock, and
13581 * thus locking it here is required by the lock order rules. */
13582 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13583
13584 if (control == mData->mSession.mDirectControl)
13585 {
13586 ComAssertRet(aProgress, E_POINTER);
13587
13588 /* The direct session is being normally closed by the client process
13589 * ----------------------------------------------------------------- */
13590
13591 /* go to the closing state (essential for all open*Session() calls and
13592 * for #checkForDeath()) */
13593 Assert(mData->mSession.mState == SessionState_Locked);
13594 mData->mSession.mState = SessionState_Unlocking;
13595
13596 /* set direct control to NULL to release the remote instance */
13597 mData->mSession.mDirectControl.setNull();
13598 LogFlowThisFunc(("Direct control is set to NULL\n"));
13599
13600 if (mData->mSession.mProgress)
13601 {
13602 /* finalize the progress, someone might wait if a frontend
13603 * closes the session before powering on the VM. */
13604 mData->mSession.mProgress->notifyComplete(E_FAIL,
13605 COM_IIDOF(ISession),
13606 getComponentName(),
13607 tr("The VM session was closed before any attempt to power it on"));
13608 mData->mSession.mProgress.setNull();
13609 }
13610
13611 /* Create the progress object the client will use to wait until
13612 * #checkForDeath() is called to uninitialize this session object after
13613 * it releases the IPC semaphore.
13614 * Note! Because we're "reusing" mProgress here, this must be a proxy
13615 * object just like for LaunchVMProcess. */
13616 Assert(mData->mSession.mProgress.isNull());
13617 ComObjPtr<ProgressProxy> progress;
13618 progress.createObject();
13619 ComPtr<IUnknown> pPeer(mPeer);
13620 progress->init(mParent, pPeer,
13621 Bstr(tr("Closing session")).raw(),
13622 FALSE /* aCancelable */);
13623 progress.queryInterfaceTo(aProgress);
13624 mData->mSession.mProgress = progress;
13625 }
13626 else
13627 {
13628 /* the remote session is being normally closed */
13629 Data::Session::RemoteControlList::iterator it =
13630 mData->mSession.mRemoteControls.begin();
13631 while (it != mData->mSession.mRemoteControls.end())
13632 {
13633 if (control == *it)
13634 break;
13635 ++it;
13636 }
13637 BOOL found = it != mData->mSession.mRemoteControls.end();
13638 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13639 E_INVALIDARG);
13640 // This MUST be erase(it), not remove(*it) as the latter triggers a
13641 // very nasty use after free due to the place where the value "lives".
13642 mData->mSession.mRemoteControls.erase(it);
13643 }
13644
13645 /* signal the client watcher thread, because the client is going away */
13646 mParent->updateClientWatcher();
13647
13648 LogFlowThisFuncLeave();
13649 return S_OK;
13650}
13651
13652/**
13653 * @note Locks this object for writing.
13654 */
13655STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
13656{
13657 LogFlowThisFuncEnter();
13658
13659 CheckComArgOutPointerValid(aProgress);
13660 CheckComArgOutPointerValid(aStateFilePath);
13661
13662 AutoCaller autoCaller(this);
13663 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13664
13665 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13666
13667 AssertReturn( mData->mMachineState == MachineState_Paused
13668 && mConsoleTaskData.mLastState == MachineState_Null
13669 && mConsoleTaskData.strStateFilePath.isEmpty(),
13670 E_FAIL);
13671
13672 /* create a progress object to track operation completion */
13673 ComObjPtr<Progress> pProgress;
13674 pProgress.createObject();
13675 pProgress->init(getVirtualBox(),
13676 static_cast<IMachine *>(this) /* aInitiator */,
13677 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13678 FALSE /* aCancelable */);
13679
13680 Utf8Str strStateFilePath;
13681 /* stateFilePath is null when the machine is not running */
13682 if (mData->mMachineState == MachineState_Paused)
13683 composeSavedStateFilename(strStateFilePath);
13684
13685 /* fill in the console task data */
13686 mConsoleTaskData.mLastState = mData->mMachineState;
13687 mConsoleTaskData.strStateFilePath = strStateFilePath;
13688 mConsoleTaskData.mProgress = pProgress;
13689
13690 /* set the state to Saving (this is expected by Console::SaveState()) */
13691 setMachineState(MachineState_Saving);
13692
13693 strStateFilePath.cloneTo(aStateFilePath);
13694 pProgress.queryInterfaceTo(aProgress);
13695
13696 return S_OK;
13697}
13698
13699/**
13700 * @note Locks mParent + this object for writing.
13701 */
13702STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
13703{
13704 LogFlowThisFunc(("\n"));
13705
13706 AutoCaller autoCaller(this);
13707 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13708
13709 /* endSavingState() need mParent lock */
13710 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13711
13712 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
13713 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
13714 && mConsoleTaskData.mLastState != MachineState_Null
13715 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13716 E_FAIL);
13717
13718 /*
13719 * On failure, set the state to the state we had when BeginSavingState()
13720 * was called (this is expected by Console::SaveState() and the associated
13721 * task). On success the VM process already changed the state to
13722 * MachineState_Saved, so no need to do anything.
13723 */
13724 if (FAILED(iResult))
13725 setMachineState(mConsoleTaskData.mLastState);
13726
13727 return endSavingState(iResult, aErrMsg);
13728}
13729
13730/**
13731 * @note Locks this object for writing.
13732 */
13733STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
13734{
13735 LogFlowThisFunc(("\n"));
13736
13737 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
13738
13739 AutoCaller autoCaller(this);
13740 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13741
13742 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13743
13744 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13745 || mData->mMachineState == MachineState_Teleported
13746 || mData->mMachineState == MachineState_Aborted
13747 , E_FAIL); /** @todo setError. */
13748
13749 Utf8Str stateFilePathFull = aSavedStateFile;
13750 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
13751 if (RT_FAILURE(vrc))
13752 return setError(VBOX_E_FILE_ERROR,
13753 tr("Invalid saved state file path '%ls' (%Rrc)"),
13754 aSavedStateFile,
13755 vrc);
13756
13757 mSSData->strStateFilePath = stateFilePathFull;
13758
13759 /* The below setMachineState() will detect the state transition and will
13760 * update the settings file */
13761
13762 return setMachineState(MachineState_Saved);
13763}
13764
13765STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13766 ComSafeArrayOut(BSTR, aValues),
13767 ComSafeArrayOut(LONG64, aTimestamps),
13768 ComSafeArrayOut(BSTR, aFlags))
13769{
13770 LogFlowThisFunc(("\n"));
13771
13772#ifdef VBOX_WITH_GUEST_PROPS
13773 using namespace guestProp;
13774
13775 AutoCaller autoCaller(this);
13776 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13777
13778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13779
13780 CheckComArgOutSafeArrayPointerValid(aNames);
13781 CheckComArgOutSafeArrayPointerValid(aValues);
13782 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13783 CheckComArgOutSafeArrayPointerValid(aFlags);
13784
13785 size_t cEntries = mHWData->mGuestProperties.size();
13786 com::SafeArray<BSTR> names(cEntries);
13787 com::SafeArray<BSTR> values(cEntries);
13788 com::SafeArray<LONG64> timestamps(cEntries);
13789 com::SafeArray<BSTR> flags(cEntries);
13790 unsigned i = 0;
13791 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13792 it != mHWData->mGuestProperties.end();
13793 ++it)
13794 {
13795 char szFlags[MAX_FLAGS_LEN + 1];
13796 it->first.cloneTo(&names[i]);
13797 it->second.strValue.cloneTo(&values[i]);
13798 timestamps[i] = it->second.mTimestamp;
13799 /* If it is NULL, keep it NULL. */
13800 if (it->second.mFlags)
13801 {
13802 writeFlags(it->second.mFlags, szFlags);
13803 Bstr(szFlags).cloneTo(&flags[i]);
13804 }
13805 else
13806 flags[i] = NULL;
13807 ++i;
13808 }
13809 names.detachTo(ComSafeArrayOutArg(aNames));
13810 values.detachTo(ComSafeArrayOutArg(aValues));
13811 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13812 flags.detachTo(ComSafeArrayOutArg(aFlags));
13813 return S_OK;
13814#else
13815 ReturnComNotImplemented();
13816#endif
13817}
13818
13819STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13820 IN_BSTR aValue,
13821 LONG64 aTimestamp,
13822 IN_BSTR aFlags)
13823{
13824 LogFlowThisFunc(("\n"));
13825
13826#ifdef VBOX_WITH_GUEST_PROPS
13827 using namespace guestProp;
13828
13829 CheckComArgStrNotEmptyOrNull(aName);
13830 CheckComArgNotNull(aValue);
13831 CheckComArgNotNull(aFlags);
13832
13833 try
13834 {
13835 /*
13836 * Convert input up front.
13837 */
13838 Utf8Str utf8Name(aName);
13839 uint32_t fFlags = NILFLAG;
13840 if (aFlags)
13841 {
13842 Utf8Str utf8Flags(aFlags);
13843 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13844 AssertRCReturn(vrc, E_INVALIDARG);
13845 }
13846
13847 /*
13848 * Now grab the object lock, validate the state and do the update.
13849 */
13850 AutoCaller autoCaller(this);
13851 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13852
13853 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13854
13855 switch (mData->mMachineState)
13856 {
13857 case MachineState_Paused:
13858 case MachineState_Running:
13859 case MachineState_Teleporting:
13860 case MachineState_TeleportingPausedVM:
13861 case MachineState_LiveSnapshotting:
13862 case MachineState_DeletingSnapshotOnline:
13863 case MachineState_DeletingSnapshotPaused:
13864 case MachineState_Saving:
13865 case MachineState_Stopping:
13866 break;
13867
13868 default:
13869 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13870 VBOX_E_INVALID_VM_STATE);
13871 }
13872
13873 setModified(IsModified_MachineData);
13874 mHWData.backup();
13875
13876 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13877 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13878 if (it != mHWData->mGuestProperties.end())
13879 {
13880 if (!fDelete)
13881 {
13882 it->second.strValue = aValue;
13883 it->second.mTimestamp = aTimestamp;
13884 it->second.mFlags = fFlags;
13885 }
13886 else
13887 mHWData->mGuestProperties.erase(it);
13888
13889 mData->mGuestPropertiesModified = TRUE;
13890 }
13891 else if (!fDelete)
13892 {
13893 HWData::GuestProperty prop;
13894 prop.strValue = aValue;
13895 prop.mTimestamp = aTimestamp;
13896 prop.mFlags = fFlags;
13897
13898 mHWData->mGuestProperties[utf8Name] = prop;
13899 mData->mGuestPropertiesModified = TRUE;
13900 }
13901
13902 /*
13903 * Send a callback notification if appropriate
13904 */
13905 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13906 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13907 RTSTR_MAX,
13908 utf8Name.c_str(),
13909 RTSTR_MAX, NULL)
13910 )
13911 {
13912 alock.release();
13913
13914 mParent->onGuestPropertyChange(mData->mUuid,
13915 aName,
13916 aValue,
13917 aFlags);
13918 }
13919 }
13920 catch (...)
13921 {
13922 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13923 }
13924 return S_OK;
13925#else
13926 ReturnComNotImplemented();
13927#endif
13928}
13929
13930STDMETHODIMP SessionMachine::LockMedia()
13931{
13932 AutoCaller autoCaller(this);
13933 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13934
13935 AutoMultiWriteLock2 alock(this->lockHandle(),
13936 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13937
13938 AssertReturn( mData->mMachineState == MachineState_Starting
13939 || mData->mMachineState == MachineState_Restoring
13940 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13941
13942 clearError();
13943 alock.release();
13944 return lockMedia();
13945}
13946
13947STDMETHODIMP SessionMachine::UnlockMedia()
13948{
13949 unlockMedia();
13950 return S_OK;
13951}
13952
13953STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13954 IMediumAttachment **aNewAttachment)
13955{
13956 CheckComArgNotNull(aAttachment);
13957 CheckComArgOutPointerValid(aNewAttachment);
13958
13959 AutoCaller autoCaller(this);
13960 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13961
13962 // request the host lock first, since might be calling Host methods for getting host drives;
13963 // next, protect the media tree all the while we're in here, as well as our member variables
13964 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13965 this->lockHandle(),
13966 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13967
13968 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13969
13970 Bstr ctrlName;
13971 LONG lPort;
13972 LONG lDevice;
13973 bool fTempEject;
13974 {
13975 AutoCaller autoAttachCaller(this);
13976 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13977
13978 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13979
13980 /* Need to query the details first, as the IMediumAttachment reference
13981 * might be to the original settings, which we are going to change. */
13982 ctrlName = pAttach->getControllerName();
13983 lPort = pAttach->getPort();
13984 lDevice = pAttach->getDevice();
13985 fTempEject = pAttach->getTempEject();
13986 }
13987
13988 if (!fTempEject)
13989 {
13990 /* Remember previously mounted medium. The medium before taking the
13991 * backup is not necessarily the same thing. */
13992 ComObjPtr<Medium> oldmedium;
13993 oldmedium = pAttach->getMedium();
13994
13995 setModified(IsModified_Storage);
13996 mMediaData.backup();
13997
13998 // The backup operation makes the pAttach reference point to the
13999 // old settings. Re-get the correct reference.
14000 pAttach = findAttachment(mMediaData->mAttachments,
14001 ctrlName.raw(),
14002 lPort,
14003 lDevice);
14004
14005 {
14006 AutoCaller autoAttachCaller(this);
14007 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
14008
14009 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14010 if (!oldmedium.isNull())
14011 oldmedium->removeBackReference(mData->mUuid);
14012
14013 pAttach->updateMedium(NULL);
14014 pAttach->updateEjected();
14015 }
14016
14017 setModified(IsModified_Storage);
14018 }
14019 else
14020 {
14021 {
14022 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14023 pAttach->updateEjected();
14024 }
14025 }
14026
14027 pAttach.queryInterfaceTo(aNewAttachment);
14028
14029 return S_OK;
14030}
14031
14032// public methods only for internal purposes
14033/////////////////////////////////////////////////////////////////////////////
14034
14035/**
14036 * Called from the client watcher thread to check for expected or unexpected
14037 * death of the client process that has a direct session to this machine.
14038 *
14039 * On Win32 and on OS/2, this method is called only when we've got the
14040 * mutex (i.e. the client has either died or terminated normally) so it always
14041 * returns @c true (the client is terminated, the session machine is
14042 * uninitialized).
14043 *
14044 * On other platforms, the method returns @c true if the client process has
14045 * terminated normally or abnormally and the session machine was uninitialized,
14046 * and @c false if the client process is still alive.
14047 *
14048 * @note Locks this object for writing.
14049 */
14050bool SessionMachine::checkForDeath()
14051{
14052 Uninit::Reason reason;
14053 bool terminated = false;
14054
14055 /* Enclose autoCaller with a block because calling uninit() from under it
14056 * will deadlock. */
14057 {
14058 AutoCaller autoCaller(this);
14059 if (!autoCaller.isOk())
14060 {
14061 /* return true if not ready, to cause the client watcher to exclude
14062 * the corresponding session from watching */
14063 LogFlowThisFunc(("Already uninitialized!\n"));
14064 return true;
14065 }
14066
14067 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14068
14069 /* Determine the reason of death: if the session state is Closing here,
14070 * everything is fine. Otherwise it means that the client did not call
14071 * OnSessionEnd() before it released the IPC semaphore. This may happen
14072 * either because the client process has abnormally terminated, or
14073 * because it simply forgot to call ISession::Close() before exiting. We
14074 * threat the latter also as an abnormal termination (see
14075 * Session::uninit() for details). */
14076 reason = mData->mSession.mState == SessionState_Unlocking ?
14077 Uninit::Normal :
14078 Uninit::Abnormal;
14079
14080#if defined(RT_OS_WINDOWS)
14081
14082 AssertMsg(mIPCSem, ("semaphore must be created"));
14083
14084 /* release the IPC mutex */
14085 ::ReleaseMutex(mIPCSem);
14086
14087 terminated = true;
14088
14089#elif defined(RT_OS_OS2)
14090
14091 AssertMsg(mIPCSem, ("semaphore must be created"));
14092
14093 /* release the IPC mutex */
14094 ::DosReleaseMutexSem(mIPCSem);
14095
14096 terminated = true;
14097
14098#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
14099
14100 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
14101
14102 int val = ::semctl(mIPCSem, 0, GETVAL);
14103 if (val > 0)
14104 {
14105 /* the semaphore is signaled, meaning the session is terminated */
14106 terminated = true;
14107 }
14108
14109#else
14110# error "Port me!"
14111#endif
14112
14113 } /* AutoCaller block */
14114
14115 if (terminated)
14116 uninit(reason);
14117
14118 return terminated;
14119}
14120
14121/**
14122 * @note Locks this object for reading.
14123 */
14124HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14125{
14126 LogFlowThisFunc(("\n"));
14127
14128 AutoCaller autoCaller(this);
14129 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14130
14131 ComPtr<IInternalSessionControl> directControl;
14132 {
14133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14134 directControl = mData->mSession.mDirectControl;
14135 }
14136
14137 /* ignore notifications sent after #OnSessionEnd() is called */
14138 if (!directControl)
14139 return S_OK;
14140
14141 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14142}
14143
14144/**
14145 * @note Locks this object for reading.
14146 */
14147HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
14148 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
14149{
14150 LogFlowThisFunc(("\n"));
14151
14152 AutoCaller autoCaller(this);
14153 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14154
14155 ComPtr<IInternalSessionControl> directControl;
14156 {
14157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14158 directControl = mData->mSession.mDirectControl;
14159 }
14160
14161 /* ignore notifications sent after #OnSessionEnd() is called */
14162 if (!directControl)
14163 return S_OK;
14164 /*
14165 * instead acting like callback we ask IVirtualBox deliver corresponding event
14166 */
14167
14168 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14169 return S_OK;
14170}
14171
14172/**
14173 * @note Locks this object for reading.
14174 */
14175HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
14176{
14177 LogFlowThisFunc(("\n"));
14178
14179 AutoCaller autoCaller(this);
14180 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14181
14182 ComPtr<IInternalSessionControl> directControl;
14183 {
14184 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14185 directControl = mData->mSession.mDirectControl;
14186 }
14187
14188 /* ignore notifications sent after #OnSessionEnd() is called */
14189 if (!directControl)
14190 return S_OK;
14191
14192 return directControl->OnSerialPortChange(serialPort);
14193}
14194
14195/**
14196 * @note Locks this object for reading.
14197 */
14198HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
14199{
14200 LogFlowThisFunc(("\n"));
14201
14202 AutoCaller autoCaller(this);
14203 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14204
14205 ComPtr<IInternalSessionControl> directControl;
14206 {
14207 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14208 directControl = mData->mSession.mDirectControl;
14209 }
14210
14211 /* ignore notifications sent after #OnSessionEnd() is called */
14212 if (!directControl)
14213 return S_OK;
14214
14215 return directControl->OnParallelPortChange(parallelPort);
14216}
14217
14218/**
14219 * @note Locks this object for reading.
14220 */
14221HRESULT SessionMachine::onStorageControllerChange()
14222{
14223 LogFlowThisFunc(("\n"));
14224
14225 AutoCaller autoCaller(this);
14226 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14227
14228 ComPtr<IInternalSessionControl> directControl;
14229 {
14230 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14231 directControl = mData->mSession.mDirectControl;
14232 }
14233
14234 /* ignore notifications sent after #OnSessionEnd() is called */
14235 if (!directControl)
14236 return S_OK;
14237
14238 return directControl->OnStorageControllerChange();
14239}
14240
14241/**
14242 * @note Locks this object for reading.
14243 */
14244HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14245{
14246 LogFlowThisFunc(("\n"));
14247
14248 AutoCaller autoCaller(this);
14249 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14250
14251 ComPtr<IInternalSessionControl> directControl;
14252 {
14253 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14254 directControl = mData->mSession.mDirectControl;
14255 }
14256
14257 /* ignore notifications sent after #OnSessionEnd() is called */
14258 if (!directControl)
14259 return S_OK;
14260
14261 return directControl->OnMediumChange(aAttachment, aForce);
14262}
14263
14264/**
14265 * @note Locks this object for reading.
14266 */
14267HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
14268{
14269 LogFlowThisFunc(("\n"));
14270
14271 AutoCaller autoCaller(this);
14272 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14273
14274 ComPtr<IInternalSessionControl> directControl;
14275 {
14276 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14277 directControl = mData->mSession.mDirectControl;
14278 }
14279
14280 /* ignore notifications sent after #OnSessionEnd() is called */
14281 if (!directControl)
14282 return S_OK;
14283
14284 return directControl->OnCPUChange(aCPU, aRemove);
14285}
14286
14287HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
14288{
14289 LogFlowThisFunc(("\n"));
14290
14291 AutoCaller autoCaller(this);
14292 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14293
14294 ComPtr<IInternalSessionControl> directControl;
14295 {
14296 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14297 directControl = mData->mSession.mDirectControl;
14298 }
14299
14300 /* ignore notifications sent after #OnSessionEnd() is called */
14301 if (!directControl)
14302 return S_OK;
14303
14304 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14305}
14306
14307/**
14308 * @note Locks this object for reading.
14309 */
14310HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
14311{
14312 LogFlowThisFunc(("\n"));
14313
14314 AutoCaller autoCaller(this);
14315 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14316
14317 ComPtr<IInternalSessionControl> directControl;
14318 {
14319 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14320 directControl = mData->mSession.mDirectControl;
14321 }
14322
14323 /* ignore notifications sent after #OnSessionEnd() is called */
14324 if (!directControl)
14325 return S_OK;
14326
14327 return directControl->OnVRDEServerChange(aRestart);
14328}
14329
14330/**
14331 * @note Locks this object for reading.
14332 */
14333HRESULT SessionMachine::onVideoCaptureChange()
14334{
14335 LogFlowThisFunc(("\n"));
14336
14337 AutoCaller autoCaller(this);
14338 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14339
14340 ComPtr<IInternalSessionControl> directControl;
14341 {
14342 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14343 directControl = mData->mSession.mDirectControl;
14344 }
14345
14346 /* ignore notifications sent after #OnSessionEnd() is called */
14347 if (!directControl)
14348 return S_OK;
14349
14350 return directControl->OnVideoCaptureChange();
14351}
14352
14353/**
14354 * @note Locks this object for reading.
14355 */
14356HRESULT SessionMachine::onUSBControllerChange()
14357{
14358 LogFlowThisFunc(("\n"));
14359
14360 AutoCaller autoCaller(this);
14361 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14362
14363 ComPtr<IInternalSessionControl> directControl;
14364 {
14365 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14366 directControl = mData->mSession.mDirectControl;
14367 }
14368
14369 /* ignore notifications sent after #OnSessionEnd() is called */
14370 if (!directControl)
14371 return S_OK;
14372
14373 return directControl->OnUSBControllerChange();
14374}
14375
14376/**
14377 * @note Locks this object for reading.
14378 */
14379HRESULT SessionMachine::onSharedFolderChange()
14380{
14381 LogFlowThisFunc(("\n"));
14382
14383 AutoCaller autoCaller(this);
14384 AssertComRCReturnRC(autoCaller.rc());
14385
14386 ComPtr<IInternalSessionControl> directControl;
14387 {
14388 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14389 directControl = mData->mSession.mDirectControl;
14390 }
14391
14392 /* ignore notifications sent after #OnSessionEnd() is called */
14393 if (!directControl)
14394 return S_OK;
14395
14396 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14397}
14398
14399/**
14400 * @note Locks this object for reading.
14401 */
14402HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
14403{
14404 LogFlowThisFunc(("\n"));
14405
14406 AutoCaller autoCaller(this);
14407 AssertComRCReturnRC(autoCaller.rc());
14408
14409 ComPtr<IInternalSessionControl> directControl;
14410 {
14411 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14412 directControl = mData->mSession.mDirectControl;
14413 }
14414
14415 /* ignore notifications sent after #OnSessionEnd() is called */
14416 if (!directControl)
14417 return S_OK;
14418
14419 return directControl->OnClipboardModeChange(aClipboardMode);
14420}
14421
14422/**
14423 * @note Locks this object for reading.
14424 */
14425HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
14426{
14427 LogFlowThisFunc(("\n"));
14428
14429 AutoCaller autoCaller(this);
14430 AssertComRCReturnRC(autoCaller.rc());
14431
14432 ComPtr<IInternalSessionControl> directControl;
14433 {
14434 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14435 directControl = mData->mSession.mDirectControl;
14436 }
14437
14438 /* ignore notifications sent after #OnSessionEnd() is called */
14439 if (!directControl)
14440 return S_OK;
14441
14442 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
14443}
14444
14445/**
14446 * @note Locks this object for reading.
14447 */
14448HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14449{
14450 LogFlowThisFunc(("\n"));
14451
14452 AutoCaller autoCaller(this);
14453 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14454
14455 ComPtr<IInternalSessionControl> directControl;
14456 {
14457 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14458 directControl = mData->mSession.mDirectControl;
14459 }
14460
14461 /* ignore notifications sent after #OnSessionEnd() is called */
14462 if (!directControl)
14463 return S_OK;
14464
14465 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14466}
14467
14468/**
14469 * @note Locks this object for reading.
14470 */
14471HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14472{
14473 LogFlowThisFunc(("\n"));
14474
14475 AutoCaller autoCaller(this);
14476 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14477
14478 ComPtr<IInternalSessionControl> directControl;
14479 {
14480 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14481 directControl = mData->mSession.mDirectControl;
14482 }
14483
14484 /* ignore notifications sent after #OnSessionEnd() is called */
14485 if (!directControl)
14486 return S_OK;
14487
14488 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14489}
14490
14491/**
14492 * Returns @c true if this machine's USB controller reports it has a matching
14493 * filter for the given USB device and @c false otherwise.
14494 *
14495 * @note locks this object for reading.
14496 */
14497bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14498{
14499 AutoCaller autoCaller(this);
14500 /* silently return if not ready -- this method may be called after the
14501 * direct machine session has been called */
14502 if (!autoCaller.isOk())
14503 return false;
14504
14505#ifdef VBOX_WITH_USB
14506 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14507
14508 switch (mData->mMachineState)
14509 {
14510 case MachineState_Starting:
14511 case MachineState_Restoring:
14512 case MachineState_TeleportingIn:
14513 case MachineState_Paused:
14514 case MachineState_Running:
14515 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14516 * elsewhere... */
14517 alock.release();
14518 return mUSBDeviceFilters->hasMatchingFilter(aDevice, aMaskedIfs);
14519 default: break;
14520 }
14521#else
14522 NOREF(aDevice);
14523 NOREF(aMaskedIfs);
14524#endif
14525 return false;
14526}
14527
14528/**
14529 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14530 */
14531HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
14532 IVirtualBoxErrorInfo *aError,
14533 ULONG aMaskedIfs)
14534{
14535 LogFlowThisFunc(("\n"));
14536
14537 AutoCaller autoCaller(this);
14538
14539 /* This notification may happen after the machine object has been
14540 * uninitialized (the session was closed), so don't assert. */
14541 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14542
14543 ComPtr<IInternalSessionControl> directControl;
14544 {
14545 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14546 directControl = mData->mSession.mDirectControl;
14547 }
14548
14549 /* fail on notifications sent after #OnSessionEnd() is called, it is
14550 * expected by the caller */
14551 if (!directControl)
14552 return E_FAIL;
14553
14554 /* No locks should be held at this point. */
14555 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14556 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14557
14558 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
14559}
14560
14561/**
14562 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14563 */
14564HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
14565 IVirtualBoxErrorInfo *aError)
14566{
14567 LogFlowThisFunc(("\n"));
14568
14569 AutoCaller autoCaller(this);
14570
14571 /* This notification may happen after the machine object has been
14572 * uninitialized (the session was closed), so don't assert. */
14573 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14574
14575 ComPtr<IInternalSessionControl> directControl;
14576 {
14577 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14578 directControl = mData->mSession.mDirectControl;
14579 }
14580
14581 /* fail on notifications sent after #OnSessionEnd() is called, it is
14582 * expected by the caller */
14583 if (!directControl)
14584 return E_FAIL;
14585
14586 /* No locks should be held at this point. */
14587 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14588 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14589
14590 return directControl->OnUSBDeviceDetach(aId, aError);
14591}
14592
14593// protected methods
14594/////////////////////////////////////////////////////////////////////////////
14595
14596/**
14597 * Helper method to finalize saving the state.
14598 *
14599 * @note Must be called from under this object's lock.
14600 *
14601 * @param aRc S_OK if the snapshot has been taken successfully
14602 * @param aErrMsg human readable error message for failure
14603 *
14604 * @note Locks mParent + this objects for writing.
14605 */
14606HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
14607{
14608 LogFlowThisFuncEnter();
14609
14610 AutoCaller autoCaller(this);
14611 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14612
14613 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14614
14615 HRESULT rc = S_OK;
14616
14617 if (SUCCEEDED(aRc))
14618 {
14619 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
14620
14621 /* save all VM settings */
14622 rc = saveSettings(NULL);
14623 // no need to check whether VirtualBox.xml needs saving also since
14624 // we can't have a name change pending at this point
14625 }
14626 else
14627 {
14628 // delete the saved state file (it might have been already created);
14629 // we need not check whether this is shared with a snapshot here because
14630 // we certainly created this saved state file here anew
14631 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
14632 }
14633
14634 /* notify the progress object about operation completion */
14635 Assert(mConsoleTaskData.mProgress);
14636 if (SUCCEEDED(aRc))
14637 mConsoleTaskData.mProgress->notifyComplete(S_OK);
14638 else
14639 {
14640 if (aErrMsg.length())
14641 mConsoleTaskData.mProgress->notifyComplete(aRc,
14642 COM_IIDOF(ISession),
14643 getComponentName(),
14644 aErrMsg.c_str());
14645 else
14646 mConsoleTaskData.mProgress->notifyComplete(aRc);
14647 }
14648
14649 /* clear out the temporary saved state data */
14650 mConsoleTaskData.mLastState = MachineState_Null;
14651 mConsoleTaskData.strStateFilePath.setNull();
14652 mConsoleTaskData.mProgress.setNull();
14653
14654 LogFlowThisFuncLeave();
14655 return rc;
14656}
14657
14658/**
14659 * Deletes the given file if it is no longer in use by either the current machine state
14660 * (if the machine is "saved") or any of the machine's snapshots.
14661 *
14662 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14663 * but is different for each SnapshotMachine. When calling this, the order of calling this
14664 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14665 * is therefore critical. I know, it's all rather messy.
14666 *
14667 * @param strStateFile
14668 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
14669 */
14670void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
14671 Snapshot *pSnapshotToIgnore)
14672{
14673 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14674 if ( (strStateFile.isNotEmpty())
14675 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14676 )
14677 // ... and it must also not be shared with other snapshots
14678 if ( !mData->mFirstSnapshot
14679 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14680 // this checks the SnapshotMachine's state file paths
14681 )
14682 RTFileDelete(strStateFile.c_str());
14683}
14684
14685/**
14686 * Locks the attached media.
14687 *
14688 * All attached hard disks are locked for writing and DVD/floppy are locked for
14689 * reading. Parents of attached hard disks (if any) are locked for reading.
14690 *
14691 * This method also performs accessibility check of all media it locks: if some
14692 * media is inaccessible, the method will return a failure and a bunch of
14693 * extended error info objects per each inaccessible medium.
14694 *
14695 * Note that this method is atomic: if it returns a success, all media are
14696 * locked as described above; on failure no media is locked at all (all
14697 * succeeded individual locks will be undone).
14698 *
14699 * The caller is responsible for doing the necessary state sanity checks.
14700 *
14701 * The locks made by this method must be undone by calling #unlockMedia() when
14702 * no more needed.
14703 */
14704HRESULT SessionMachine::lockMedia()
14705{
14706 AutoCaller autoCaller(this);
14707 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14708
14709 AutoMultiWriteLock2 alock(this->lockHandle(),
14710 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14711
14712 /* bail out if trying to lock things with already set up locking */
14713 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14714
14715 MultiResult mrc(S_OK);
14716
14717 /* Collect locking information for all medium objects attached to the VM. */
14718 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14719 it != mMediaData->mAttachments.end();
14720 ++it)
14721 {
14722 MediumAttachment* pAtt = *it;
14723 DeviceType_T devType = pAtt->getType();
14724 Medium *pMedium = pAtt->getMedium();
14725
14726 MediumLockList *pMediumLockList(new MediumLockList());
14727 // There can be attachments without a medium (floppy/dvd), and thus
14728 // it's impossible to create a medium lock list. It still makes sense
14729 // to have the empty medium lock list in the map in case a medium is
14730 // attached later.
14731 if (pMedium != NULL)
14732 {
14733 MediumType_T mediumType = pMedium->getType();
14734 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14735 || mediumType == MediumType_Shareable;
14736 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14737
14738 alock.release();
14739 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14740 !fIsReadOnlyLock /* fMediumLockWrite */,
14741 NULL,
14742 *pMediumLockList);
14743 alock.acquire();
14744 if (FAILED(mrc))
14745 {
14746 delete pMediumLockList;
14747 mData->mSession.mLockedMedia.Clear();
14748 break;
14749 }
14750 }
14751
14752 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14753 if (FAILED(rc))
14754 {
14755 mData->mSession.mLockedMedia.Clear();
14756 mrc = setError(rc,
14757 tr("Collecting locking information for all attached media failed"));
14758 break;
14759 }
14760 }
14761
14762 if (SUCCEEDED(mrc))
14763 {
14764 /* Now lock all media. If this fails, nothing is locked. */
14765 alock.release();
14766 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14767 alock.acquire();
14768 if (FAILED(rc))
14769 {
14770 mrc = setError(rc,
14771 tr("Locking of attached media failed"));
14772 }
14773 }
14774
14775 return mrc;
14776}
14777
14778/**
14779 * Undoes the locks made by by #lockMedia().
14780 */
14781void SessionMachine::unlockMedia()
14782{
14783 AutoCaller autoCaller(this);
14784 AssertComRCReturnVoid(autoCaller.rc());
14785
14786 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14787
14788 /* we may be holding important error info on the current thread;
14789 * preserve it */
14790 ErrorInfoKeeper eik;
14791
14792 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14793 AssertComRC(rc);
14794}
14795
14796/**
14797 * Helper to change the machine state (reimplementation).
14798 *
14799 * @note Locks this object for writing.
14800 * @note This method must not call saveSettings or SaveSettings, otherwise
14801 * it can cause crashes in random places due to unexpectedly committing
14802 * the current settings. The caller is responsible for that. The call
14803 * to saveStateSettings is fine, because this method does not commit.
14804 */
14805HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
14806{
14807 LogFlowThisFuncEnter();
14808 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14809
14810 AutoCaller autoCaller(this);
14811 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14812
14813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14814
14815 MachineState_T oldMachineState = mData->mMachineState;
14816
14817 AssertMsgReturn(oldMachineState != aMachineState,
14818 ("oldMachineState=%s, aMachineState=%s\n",
14819 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14820 E_FAIL);
14821
14822 HRESULT rc = S_OK;
14823
14824 int stsFlags = 0;
14825 bool deleteSavedState = false;
14826
14827 /* detect some state transitions */
14828
14829 if ( ( oldMachineState == MachineState_Saved
14830 && aMachineState == MachineState_Restoring)
14831 || ( ( oldMachineState == MachineState_PoweredOff
14832 || oldMachineState == MachineState_Teleported
14833 || oldMachineState == MachineState_Aborted
14834 )
14835 && ( aMachineState == MachineState_TeleportingIn
14836 || aMachineState == MachineState_Starting
14837 )
14838 )
14839 )
14840 {
14841 /* The EMT thread is about to start */
14842
14843 /* Nothing to do here for now... */
14844
14845 /// @todo NEWMEDIA don't let mDVDDrive and other children
14846 /// change anything when in the Starting/Restoring state
14847 }
14848 else if ( ( oldMachineState == MachineState_Running
14849 || oldMachineState == MachineState_Paused
14850 || oldMachineState == MachineState_Teleporting
14851 || oldMachineState == MachineState_LiveSnapshotting
14852 || oldMachineState == MachineState_Stuck
14853 || oldMachineState == MachineState_Starting
14854 || oldMachineState == MachineState_Stopping
14855 || oldMachineState == MachineState_Saving
14856 || oldMachineState == MachineState_Restoring
14857 || oldMachineState == MachineState_TeleportingPausedVM
14858 || oldMachineState == MachineState_TeleportingIn
14859 )
14860 && ( aMachineState == MachineState_PoweredOff
14861 || aMachineState == MachineState_Saved
14862 || aMachineState == MachineState_Teleported
14863 || aMachineState == MachineState_Aborted
14864 )
14865 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14866 * snapshot */
14867 && ( mConsoleTaskData.mSnapshot.isNull()
14868 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14869 )
14870 )
14871 {
14872 /* The EMT thread has just stopped, unlock attached media. Note that as
14873 * opposed to locking that is done from Console, we do unlocking here
14874 * because the VM process may have aborted before having a chance to
14875 * properly unlock all media it locked. */
14876
14877 unlockMedia();
14878 }
14879
14880 if (oldMachineState == MachineState_Restoring)
14881 {
14882 if (aMachineState != MachineState_Saved)
14883 {
14884 /*
14885 * delete the saved state file once the machine has finished
14886 * restoring from it (note that Console sets the state from
14887 * Restoring to Saved if the VM couldn't restore successfully,
14888 * to give the user an ability to fix an error and retry --
14889 * we keep the saved state file in this case)
14890 */
14891 deleteSavedState = true;
14892 }
14893 }
14894 else if ( oldMachineState == MachineState_Saved
14895 && ( aMachineState == MachineState_PoweredOff
14896 || aMachineState == MachineState_Aborted
14897 || aMachineState == MachineState_Teleported
14898 )
14899 )
14900 {
14901 /*
14902 * delete the saved state after Console::ForgetSavedState() is called
14903 * or if the VM process (owning a direct VM session) crashed while the
14904 * VM was Saved
14905 */
14906
14907 /// @todo (dmik)
14908 // Not sure that deleting the saved state file just because of the
14909 // client death before it attempted to restore the VM is a good
14910 // thing. But when it crashes we need to go to the Aborted state
14911 // which cannot have the saved state file associated... The only
14912 // way to fix this is to make the Aborted condition not a VM state
14913 // but a bool flag: i.e., when a crash occurs, set it to true and
14914 // change the state to PoweredOff or Saved depending on the
14915 // saved state presence.
14916
14917 deleteSavedState = true;
14918 mData->mCurrentStateModified = TRUE;
14919 stsFlags |= SaveSTS_CurStateModified;
14920 }
14921
14922 if ( aMachineState == MachineState_Starting
14923 || aMachineState == MachineState_Restoring
14924 || aMachineState == MachineState_TeleportingIn
14925 )
14926 {
14927 /* set the current state modified flag to indicate that the current
14928 * state is no more identical to the state in the
14929 * current snapshot */
14930 if (!mData->mCurrentSnapshot.isNull())
14931 {
14932 mData->mCurrentStateModified = TRUE;
14933 stsFlags |= SaveSTS_CurStateModified;
14934 }
14935 }
14936
14937 if (deleteSavedState)
14938 {
14939 if (mRemoveSavedState)
14940 {
14941 Assert(!mSSData->strStateFilePath.isEmpty());
14942
14943 // it is safe to delete the saved state file if ...
14944 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14945 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14946 // ... none of the snapshots share the saved state file
14947 )
14948 RTFileDelete(mSSData->strStateFilePath.c_str());
14949 }
14950
14951 mSSData->strStateFilePath.setNull();
14952 stsFlags |= SaveSTS_StateFilePath;
14953 }
14954
14955 /* redirect to the underlying peer machine */
14956 mPeer->setMachineState(aMachineState);
14957
14958 if ( aMachineState == MachineState_PoweredOff
14959 || aMachineState == MachineState_Teleported
14960 || aMachineState == MachineState_Aborted
14961 || aMachineState == MachineState_Saved)
14962 {
14963 /* the machine has stopped execution
14964 * (or the saved state file was adopted) */
14965 stsFlags |= SaveSTS_StateTimeStamp;
14966 }
14967
14968 if ( ( oldMachineState == MachineState_PoweredOff
14969 || oldMachineState == MachineState_Aborted
14970 || oldMachineState == MachineState_Teleported
14971 )
14972 && aMachineState == MachineState_Saved)
14973 {
14974 /* the saved state file was adopted */
14975 Assert(!mSSData->strStateFilePath.isEmpty());
14976 stsFlags |= SaveSTS_StateFilePath;
14977 }
14978
14979#ifdef VBOX_WITH_GUEST_PROPS
14980 if ( aMachineState == MachineState_PoweredOff
14981 || aMachineState == MachineState_Aborted
14982 || aMachineState == MachineState_Teleported)
14983 {
14984 /* Make sure any transient guest properties get removed from the
14985 * property store on shutdown. */
14986
14987 HWData::GuestPropertyMap::const_iterator it;
14988 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14989 if (!fNeedsSaving)
14990 for (it = mHWData->mGuestProperties.begin();
14991 it != mHWData->mGuestProperties.end(); ++it)
14992 if ( (it->second.mFlags & guestProp::TRANSIENT)
14993 || (it->second.mFlags & guestProp::TRANSRESET))
14994 {
14995 fNeedsSaving = true;
14996 break;
14997 }
14998 if (fNeedsSaving)
14999 {
15000 mData->mCurrentStateModified = TRUE;
15001 stsFlags |= SaveSTS_CurStateModified;
15002 }
15003 }
15004#endif
15005
15006 rc = saveStateSettings(stsFlags);
15007
15008 if ( ( oldMachineState != MachineState_PoweredOff
15009 && oldMachineState != MachineState_Aborted
15010 && oldMachineState != MachineState_Teleported
15011 )
15012 && ( aMachineState == MachineState_PoweredOff
15013 || aMachineState == MachineState_Aborted
15014 || aMachineState == MachineState_Teleported
15015 )
15016 )
15017 {
15018 /* we've been shut down for any reason */
15019 /* no special action so far */
15020 }
15021
15022 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
15023 LogFlowThisFuncLeave();
15024 return rc;
15025}
15026
15027/**
15028 * Sends the current machine state value to the VM process.
15029 *
15030 * @note Locks this object for reading, then calls a client process.
15031 */
15032HRESULT SessionMachine::updateMachineStateOnClient()
15033{
15034 AutoCaller autoCaller(this);
15035 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15036
15037 ComPtr<IInternalSessionControl> directControl;
15038 {
15039 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15040 AssertReturn(!!mData, E_FAIL);
15041 directControl = mData->mSession.mDirectControl;
15042
15043 /* directControl may be already set to NULL here in #OnSessionEnd()
15044 * called too early by the direct session process while there is still
15045 * some operation (like deleting the snapshot) in progress. The client
15046 * process in this case is waiting inside Session::close() for the
15047 * "end session" process object to complete, while #uninit() called by
15048 * #checkForDeath() on the Watcher thread is waiting for the pending
15049 * operation to complete. For now, we accept this inconsistent behavior
15050 * and simply do nothing here. */
15051
15052 if (mData->mSession.mState == SessionState_Unlocking)
15053 return S_OK;
15054
15055 AssertReturn(!directControl.isNull(), E_FAIL);
15056 }
15057
15058 return directControl->UpdateMachineState(mData->mMachineState);
15059}
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