VirtualBox

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

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

Main: Reattach the medium to the VM when restoring it if hotplugging or silent reconfiguration is enabled

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 484.0 KB
Line 
1/* $Id: MachineImpl.cpp 46056 2013-05-14 10:55:46Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
27# include <errno.h>
28# include <sys/types.h>
29# include <sys/stat.h>
30# include <sys/ipc.h>
31# include <sys/sem.h>
32#endif
33
34#include "Logging.h"
35#include "VirtualBoxImpl.h"
36#include "MachineImpl.h"
37#include "ProgressImpl.h"
38#include "ProgressProxyImpl.h"
39#include "MediumAttachmentImpl.h"
40#include "MediumImpl.h"
41#include "MediumLock.h"
42#include "USBControllerImpl.h"
43#include "HostImpl.h"
44#include "SharedFolderImpl.h"
45#include "GuestOSTypeImpl.h"
46#include "VirtualBoxErrorInfoImpl.h"
47#include "GuestImpl.h"
48#include "StorageControllerImpl.h"
49#include "DisplayImpl.h"
50#include "DisplayUtils.h"
51#include "BandwidthControlImpl.h"
52#include "MachineImplCloneVM.h"
53#include "AutostartDb.h"
54
55// generated header
56#include "VBoxEvents.h"
57
58#ifdef VBOX_WITH_USB
59# include "USBProxyService.h"
60#endif
61
62#include "AutoCaller.h"
63#include "HashedPw.h"
64#include "Performance.h"
65
66#include <iprt/asm.h>
67#include <iprt/path.h>
68#include <iprt/dir.h>
69#include <iprt/env.h>
70#include <iprt/lockvalidator.h>
71#include <iprt/process.h>
72#include <iprt/cpp/utils.h>
73#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
74#include <iprt/sha.h>
75#include <iprt/string.h>
76
77#include <VBox/com/array.h>
78#include <VBox/com/list.h>
79
80#include <VBox/err.h>
81#include <VBox/param.h>
82#include <VBox/settings.h>
83#include <VBox/vmm/ssm.h>
84
85#ifdef VBOX_WITH_GUEST_PROPS
86# include <VBox/HostServices/GuestPropertySvc.h>
87# include <VBox/com/array.h>
88#endif
89
90#include "VBox/com/MultiResult.h"
91
92#include <algorithm>
93
94#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
95# define HOSTSUFF_EXE ".exe"
96#else /* !RT_OS_WINDOWS */
97# define HOSTSUFF_EXE ""
98#endif /* !RT_OS_WINDOWS */
99
100// defines / prototypes
101/////////////////////////////////////////////////////////////////////////////
102
103/////////////////////////////////////////////////////////////////////////////
104// Machine::Data structure
105/////////////////////////////////////////////////////////////////////////////
106
107Machine::Data::Data()
108{
109 mRegistered = FALSE;
110 pMachineConfigFile = NULL;
111 /* Contains hints on what has changed when the user is using the VM (config
112 * changes, running the VM, ...). This is used to decide if a config needs
113 * to be written to disk. */
114 flModifications = 0;
115 /* VM modification usually also trigger setting the current state to
116 * "Modified". Although this is not always the case. An e.g. is the VM
117 * initialization phase or when snapshot related data is changed. The
118 * actually behavior is controlled by the following flag. */
119 m_fAllowStateModification = false;
120 mAccessible = FALSE;
121 /* mUuid is initialized in Machine::init() */
122
123 mMachineState = MachineState_PoweredOff;
124 RTTimeNow(&mLastStateChange);
125
126 mMachineStateDeps = 0;
127 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
128 mMachineStateChangePending = 0;
129
130 mCurrentStateModified = TRUE;
131 mGuestPropertiesModified = FALSE;
132
133 mSession.mPID = NIL_RTPROCESS;
134 mSession.mState = SessionState_Unlocked;
135}
136
137Machine::Data::~Data()
138{
139 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
140 {
141 RTSemEventMultiDestroy(mMachineStateDepsSem);
142 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
143 }
144 if (pMachineConfigFile)
145 {
146 delete pMachineConfigFile;
147 pMachineConfigFile = NULL;
148 }
149}
150
151/////////////////////////////////////////////////////////////////////////////
152// Machine::HWData structure
153/////////////////////////////////////////////////////////////////////////////
154
155Machine::HWData::HWData()
156{
157 /* default values for a newly created machine */
158 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
159 mMemorySize = 128;
160 mCPUCount = 1;
161 mCPUHotPlugEnabled = false;
162 mMemoryBalloonSize = 0;
163 mPageFusionEnabled = false;
164 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
165 mVRAMSize = 8;
166 mAccelerate3DEnabled = false;
167 mAccelerate2DVideoEnabled = false;
168 mMonitorCount = 1;
169 mVideoCaptureFile = "Test.webm";
170 mVideoCaptureWidth = 1024;
171 mVideoCaptureHeight = 768;
172 mVideoCaptureRate = 512;
173 mVideoCaptureFps = 25;
174 mVideoCaptureEnabled = false;
175
176 mHWVirtExEnabled = true;
177 mHWVirtExNestedPagingEnabled = true;
178#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
179 mHWVirtExLargePagesEnabled = true;
180#else
181 /* Not supported on 32 bits hosts. */
182 mHWVirtExLargePagesEnabled = false;
183#endif
184 mHWVirtExVPIDEnabled = true;
185 mHWVirtExUXEnabled = true;
186 mHWVirtExForceEnabled = false;
187#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
188 mHWVirtExExclusive = false;
189#else
190 mHWVirtExExclusive = true;
191#endif
192#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
193 mPAEEnabled = true;
194#else
195 mPAEEnabled = false;
196#endif
197 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
198 mSyntheticCpu = false;
199 mHPETEnabled = false;
200
201 /* default boot order: floppy - DVD - HDD */
202 mBootOrder[0] = DeviceType_Floppy;
203 mBootOrder[1] = DeviceType_DVD;
204 mBootOrder[2] = DeviceType_HardDisk;
205 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
206 mBootOrder[i] = DeviceType_Null;
207
208 mClipboardMode = ClipboardMode_Disabled;
209 mDragAndDropMode = DragAndDropMode_Disabled;
210 mGuestPropertyNotificationPatterns = "";
211
212 mFirmwareType = FirmwareType_BIOS;
213 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
214 mPointingHIDType = PointingHIDType_PS2Mouse;
215 mChipsetType = ChipsetType_PIIX3;
216 mEmulatedUSBWebcamEnabled = FALSE;
217 mEmulatedUSBCardReaderEnabled = FALSE;
218
219 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
220 mCPUAttached[i] = false;
221
222 mIOCacheEnabled = true;
223 mIOCacheSize = 5; /* 5MB */
224
225 /* Maximum CPU execution cap by default. */
226 mCpuExecutionCap = 100;
227}
228
229Machine::HWData::~HWData()
230{
231}
232
233/////////////////////////////////////////////////////////////////////////////
234// Machine::HDData structure
235/////////////////////////////////////////////////////////////////////////////
236
237Machine::MediaData::MediaData()
238{
239}
240
241Machine::MediaData::~MediaData()
242{
243}
244
245/////////////////////////////////////////////////////////////////////////////
246// Machine class
247/////////////////////////////////////////////////////////////////////////////
248
249// constructor / destructor
250/////////////////////////////////////////////////////////////////////////////
251
252Machine::Machine()
253 : mCollectorGuest(NULL),
254 mPeer(NULL),
255 mParent(NULL),
256 mSerialPorts(),
257 mParallelPorts(),
258 uRegistryNeedsSaving(0)
259{}
260
261Machine::~Machine()
262{}
263
264HRESULT Machine::FinalConstruct()
265{
266 LogFlowThisFunc(("\n"));
267 return BaseFinalConstruct();
268}
269
270void Machine::FinalRelease()
271{
272 LogFlowThisFunc(("\n"));
273 uninit();
274 BaseFinalRelease();
275}
276
277/**
278 * Initializes a new machine instance; this init() variant creates a new, empty machine.
279 * This gets called from VirtualBox::CreateMachine().
280 *
281 * @param aParent Associated parent object
282 * @param strConfigFile Local file system path to the VM settings file (can
283 * be relative to the VirtualBox config directory).
284 * @param strName name for the machine
285 * @param llGroups list of groups for the machine
286 * @param aOsType OS Type of this machine or NULL.
287 * @param aId UUID for the new machine.
288 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
289 *
290 * @return Success indicator. if not S_OK, the machine object is invalid
291 */
292HRESULT Machine::init(VirtualBox *aParent,
293 const Utf8Str &strConfigFile,
294 const Utf8Str &strName,
295 const StringsList &llGroups,
296 GuestOSType *aOsType,
297 const Guid &aId,
298 bool fForceOverwrite,
299 bool fDirectoryIncludesUUID)
300{
301 LogFlowThisFuncEnter();
302 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
303
304 /* Enclose the state transition NotReady->InInit->Ready */
305 AutoInitSpan autoInitSpan(this);
306 AssertReturn(autoInitSpan.isOk(), E_FAIL);
307
308 HRESULT rc = initImpl(aParent, strConfigFile);
309 if (FAILED(rc)) return rc;
310
311 rc = tryCreateMachineConfigFile(fForceOverwrite);
312 if (FAILED(rc)) return rc;
313
314 if (SUCCEEDED(rc))
315 {
316 // create an empty machine config
317 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
318
319 rc = initDataAndChildObjects();
320 }
321
322 if (SUCCEEDED(rc))
323 {
324 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
325 mData->mAccessible = TRUE;
326
327 unconst(mData->mUuid) = aId;
328
329 mUserData->s.strName = strName;
330
331 mUserData->s.llGroups = llGroups;
332
333 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
334 // the "name sync" flag determines whether the machine directory gets renamed along
335 // with the machine file; say so if the settings file name is the same as the
336 // settings file parent directory (machine directory)
337 mUserData->s.fNameSync = isInOwnDir();
338
339 // initialize the default snapshots folder
340 rc = COMSETTER(SnapshotFolder)(NULL);
341 AssertComRC(rc);
342
343 if (aOsType)
344 {
345 /* Store OS type */
346 mUserData->s.strOsType = aOsType->id();
347
348 /* Apply BIOS defaults */
349 mBIOSSettings->applyDefaults(aOsType);
350
351 /* Apply network adapters defaults */
352 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
353 mNetworkAdapters[slot]->applyDefaults(aOsType);
354
355 /* Apply serial port defaults */
356 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
357 mSerialPorts[slot]->applyDefaults(aOsType);
358
359 /* Let the OS type select 64-bit ness. */
360 mHWData->mLongMode = aOsType->is64Bit()
361 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
362 }
363
364 /* At this point the changing of the current state modification
365 * flag is allowed. */
366 allowStateModification();
367
368 /* commit all changes made during the initialization */
369 commit();
370 }
371
372 /* Confirm a successful initialization when it's the case */
373 if (SUCCEEDED(rc))
374 {
375 if (mData->mAccessible)
376 autoInitSpan.setSucceeded();
377 else
378 autoInitSpan.setLimited();
379 }
380
381 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
382 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
383 mData->mRegistered,
384 mData->mAccessible,
385 rc));
386
387 LogFlowThisFuncLeave();
388
389 return rc;
390}
391
392/**
393 * Initializes a new instance with data from machine XML (formerly Init_Registered).
394 * Gets called in two modes:
395 *
396 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
397 * UUID is specified and we mark the machine as "registered";
398 *
399 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
400 * and the machine remains unregistered until RegisterMachine() is called.
401 *
402 * @param aParent Associated parent object
403 * @param aConfigFile Local file system path to the VM settings file (can
404 * be relative to the VirtualBox config directory).
405 * @param aId UUID of the machine or NULL (see above).
406 *
407 * @return Success indicator. if not S_OK, the machine object is invalid
408 */
409HRESULT Machine::initFromSettings(VirtualBox *aParent,
410 const Utf8Str &strConfigFile,
411 const Guid *aId)
412{
413 LogFlowThisFuncEnter();
414 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
415
416 /* Enclose the state transition NotReady->InInit->Ready */
417 AutoInitSpan autoInitSpan(this);
418 AssertReturn(autoInitSpan.isOk(), E_FAIL);
419
420 HRESULT rc = initImpl(aParent, strConfigFile);
421 if (FAILED(rc)) return rc;
422
423 if (aId)
424 {
425 // loading a registered VM:
426 unconst(mData->mUuid) = *aId;
427 mData->mRegistered = TRUE;
428 // now load the settings from XML:
429 rc = registeredInit();
430 // this calls initDataAndChildObjects() and loadSettings()
431 }
432 else
433 {
434 // opening an unregistered VM (VirtualBox::OpenMachine()):
435 rc = initDataAndChildObjects();
436
437 if (SUCCEEDED(rc))
438 {
439 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
440 mData->mAccessible = TRUE;
441
442 try
443 {
444 // load and parse machine XML; this will throw on XML or logic errors
445 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
446
447 // reject VM UUID duplicates, they can happen if someone
448 // tries to register an already known VM config again
449 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
450 true /* fPermitInaccessible */,
451 false /* aDoSetError */,
452 NULL) != VBOX_E_OBJECT_NOT_FOUND)
453 {
454 throw setError(E_FAIL,
455 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
456 mData->m_strConfigFile.c_str());
457 }
458
459 // use UUID from machine config
460 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
461
462 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
463 NULL /* puuidRegistry */);
464 if (FAILED(rc)) throw rc;
465
466 /* At this point the changing of the current state modification
467 * flag is allowed. */
468 allowStateModification();
469
470 commit();
471 }
472 catch (HRESULT err)
473 {
474 /* we assume that error info is set by the thrower */
475 rc = err;
476 }
477 catch (...)
478 {
479 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
480 }
481 }
482 }
483
484 /* Confirm a successful initialization when it's the case */
485 if (SUCCEEDED(rc))
486 {
487 if (mData->mAccessible)
488 autoInitSpan.setSucceeded();
489 else
490 {
491 autoInitSpan.setLimited();
492
493 // uninit media from this machine's media registry, or else
494 // reloading the settings will fail
495 mParent->unregisterMachineMedia(getId());
496 }
497 }
498
499 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
500 "rc=%08X\n",
501 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
502 mData->mRegistered, mData->mAccessible, rc));
503
504 LogFlowThisFuncLeave();
505
506 return rc;
507}
508
509/**
510 * Initializes a new instance from a machine config that is already in memory
511 * (import OVF case). Since we are importing, the UUID in the machine
512 * config is ignored and we always generate a fresh one.
513 *
514 * @param strName Name for the new machine; this overrides what is specified in config and is used
515 * for the settings file as well.
516 * @param config Machine configuration loaded and parsed from XML.
517 *
518 * @return Success indicator. if not S_OK, the machine object is invalid
519 */
520HRESULT Machine::init(VirtualBox *aParent,
521 const Utf8Str &strName,
522 const settings::MachineConfigFile &config)
523{
524 LogFlowThisFuncEnter();
525
526 /* Enclose the state transition NotReady->InInit->Ready */
527 AutoInitSpan autoInitSpan(this);
528 AssertReturn(autoInitSpan.isOk(), E_FAIL);
529
530 Utf8Str strConfigFile;
531 aParent->getDefaultMachineFolder(strConfigFile);
532 strConfigFile.append(RTPATH_DELIMITER);
533 strConfigFile.append(strName);
534 strConfigFile.append(RTPATH_DELIMITER);
535 strConfigFile.append(strName);
536 strConfigFile.append(".vbox");
537
538 HRESULT rc = initImpl(aParent, strConfigFile);
539 if (FAILED(rc)) return rc;
540
541 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
542 if (FAILED(rc)) return rc;
543
544 rc = initDataAndChildObjects();
545
546 if (SUCCEEDED(rc))
547 {
548 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
549 mData->mAccessible = TRUE;
550
551 // create empty machine config for instance data
552 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
553
554 // generate fresh UUID, ignore machine config
555 unconst(mData->mUuid).create();
556
557 rc = loadMachineDataFromSettings(config,
558 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
559
560 // override VM name as well, it may be different
561 mUserData->s.strName = strName;
562
563 if (SUCCEEDED(rc))
564 {
565 /* At this point the changing of the current state modification
566 * flag is allowed. */
567 allowStateModification();
568
569 /* commit all changes made during the initialization */
570 commit();
571 }
572 }
573
574 /* Confirm a successful initialization when it's the case */
575 if (SUCCEEDED(rc))
576 {
577 if (mData->mAccessible)
578 autoInitSpan.setSucceeded();
579 else
580 {
581 autoInitSpan.setLimited();
582
583 // uninit media from this machine's media registry, or else
584 // reloading the settings will fail
585 mParent->unregisterMachineMedia(getId());
586 }
587 }
588
589 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
590 "rc=%08X\n",
591 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
592 mData->mRegistered, mData->mAccessible, rc));
593
594 LogFlowThisFuncLeave();
595
596 return rc;
597}
598
599/**
600 * Shared code between the various init() implementations.
601 * @param aParent
602 * @return
603 */
604HRESULT Machine::initImpl(VirtualBox *aParent,
605 const Utf8Str &strConfigFile)
606{
607 LogFlowThisFuncEnter();
608
609 AssertReturn(aParent, E_INVALIDARG);
610 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
611
612 HRESULT rc = S_OK;
613
614 /* share the parent weakly */
615 unconst(mParent) = aParent;
616
617 /* allocate the essential machine data structure (the rest will be
618 * allocated later by initDataAndChildObjects() */
619 mData.allocate();
620
621 /* memorize the config file name (as provided) */
622 mData->m_strConfigFile = strConfigFile;
623
624 /* get the full file name */
625 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
626 if (RT_FAILURE(vrc1))
627 return setError(VBOX_E_FILE_ERROR,
628 tr("Invalid machine settings file name '%s' (%Rrc)"),
629 strConfigFile.c_str(),
630 vrc1);
631
632 LogFlowThisFuncLeave();
633
634 return rc;
635}
636
637/**
638 * Tries to create a machine settings file in the path stored in the machine
639 * instance data. Used when a new machine is created to fail gracefully if
640 * the settings file could not be written (e.g. because machine dir is read-only).
641 * @return
642 */
643HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
644{
645 HRESULT rc = S_OK;
646
647 // when we create a new machine, we must be able to create the settings file
648 RTFILE f = NIL_RTFILE;
649 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
650 if ( RT_SUCCESS(vrc)
651 || vrc == VERR_SHARING_VIOLATION
652 )
653 {
654 if (RT_SUCCESS(vrc))
655 RTFileClose(f);
656 if (!fForceOverwrite)
657 rc = setError(VBOX_E_FILE_ERROR,
658 tr("Machine settings file '%s' already exists"),
659 mData->m_strConfigFileFull.c_str());
660 else
661 {
662 /* try to delete the config file, as otherwise the creation
663 * of a new settings file will fail. */
664 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
665 if (RT_FAILURE(vrc2))
666 rc = setError(VBOX_E_FILE_ERROR,
667 tr("Could not delete the existing settings file '%s' (%Rrc)"),
668 mData->m_strConfigFileFull.c_str(), vrc2);
669 }
670 }
671 else if ( vrc != VERR_FILE_NOT_FOUND
672 && vrc != VERR_PATH_NOT_FOUND
673 )
674 rc = setError(VBOX_E_FILE_ERROR,
675 tr("Invalid machine settings file name '%s' (%Rrc)"),
676 mData->m_strConfigFileFull.c_str(),
677 vrc);
678 return rc;
679}
680
681/**
682 * Initializes the registered machine by loading the settings file.
683 * This method is separated from #init() in order to make it possible to
684 * retry the operation after VirtualBox startup instead of refusing to
685 * startup the whole VirtualBox server in case if the settings file of some
686 * registered VM is invalid or inaccessible.
687 *
688 * @note Must be always called from this object's write lock
689 * (unless called from #init() that doesn't need any locking).
690 * @note Locks the mUSBController method for writing.
691 * @note Subclasses must not call this method.
692 */
693HRESULT Machine::registeredInit()
694{
695 AssertReturn(!isSessionMachine(), E_FAIL);
696 AssertReturn(!isSnapshotMachine(), E_FAIL);
697 AssertReturn(mData->mUuid.isValid(), E_FAIL);
698 AssertReturn(!mData->mAccessible, E_FAIL);
699
700 HRESULT rc = initDataAndChildObjects();
701
702 if (SUCCEEDED(rc))
703 {
704 /* Temporarily reset the registered flag in order to let setters
705 * potentially called from loadSettings() succeed (isMutable() used in
706 * all setters will return FALSE for a Machine instance if mRegistered
707 * is TRUE). */
708 mData->mRegistered = FALSE;
709
710 try
711 {
712 // load and parse machine XML; this will throw on XML or logic errors
713 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
714
715 if (mData->mUuid != mData->pMachineConfigFile->uuid)
716 throw setError(E_FAIL,
717 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
718 mData->pMachineConfigFile->uuid.raw(),
719 mData->m_strConfigFileFull.c_str(),
720 mData->mUuid.toString().c_str(),
721 mParent->settingsFilePath().c_str());
722
723 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
724 NULL /* const Guid *puuidRegistry */);
725 if (FAILED(rc)) throw rc;
726 }
727 catch (HRESULT err)
728 {
729 /* we assume that error info is set by the thrower */
730 rc = err;
731 }
732 catch (...)
733 {
734 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
735 }
736
737 /* Restore the registered flag (even on failure) */
738 mData->mRegistered = TRUE;
739 }
740
741 if (SUCCEEDED(rc))
742 {
743 /* Set mAccessible to TRUE only if we successfully locked and loaded
744 * the settings file */
745 mData->mAccessible = TRUE;
746
747 /* commit all changes made during loading the settings file */
748 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
749 /// @todo r=klaus for some reason the settings loading logic backs up
750 // the settings, and therefore a commit is needed. Should probably be changed.
751 }
752 else
753 {
754 /* If the machine is registered, then, instead of returning a
755 * failure, we mark it as inaccessible and set the result to
756 * success to give it a try later */
757
758 /* fetch the current error info */
759 mData->mAccessError = com::ErrorInfo();
760 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
761 mData->mUuid.raw(),
762 mData->mAccessError.getText().raw()));
763
764 /* rollback all changes */
765 rollback(false /* aNotify */);
766
767 // uninit media from this machine's media registry, or else
768 // reloading the settings will fail
769 mParent->unregisterMachineMedia(getId());
770
771 /* uninitialize the common part to make sure all data is reset to
772 * default (null) values */
773 uninitDataAndChildObjects();
774
775 rc = S_OK;
776 }
777
778 return rc;
779}
780
781/**
782 * Uninitializes the instance.
783 * Called either from FinalRelease() or by the parent when it gets destroyed.
784 *
785 * @note The caller of this method must make sure that this object
786 * a) doesn't have active callers on the current thread and b) is not locked
787 * by the current thread; otherwise uninit() will hang either a) due to
788 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
789 * a dead-lock caused by this thread waiting for all callers on the other
790 * threads are done but preventing them from doing so by holding a lock.
791 */
792void Machine::uninit()
793{
794 LogFlowThisFuncEnter();
795
796 Assert(!isWriteLockOnCurrentThread());
797
798 Assert(!uRegistryNeedsSaving);
799 if (uRegistryNeedsSaving)
800 {
801 AutoCaller autoCaller(this);
802 if (SUCCEEDED(autoCaller.rc()))
803 {
804 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
805 saveSettings(NULL, Machine::SaveS_Force);
806 }
807 }
808
809 /* Enclose the state transition Ready->InUninit->NotReady */
810 AutoUninitSpan autoUninitSpan(this);
811 if (autoUninitSpan.uninitDone())
812 return;
813
814 Assert(!isSnapshotMachine());
815 Assert(!isSessionMachine());
816 Assert(!!mData);
817
818 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
819 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
820
821 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
822
823 if (!mData->mSession.mMachine.isNull())
824 {
825 /* Theoretically, this can only happen if the VirtualBox server has been
826 * terminated while there were clients running that owned open direct
827 * sessions. Since in this case we are definitely called by
828 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
829 * won't happen on the client watcher thread (because it does
830 * VirtualBox::addCaller() for the duration of the
831 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
832 * cannot happen until the VirtualBox caller is released). This is
833 * important, because SessionMachine::uninit() cannot correctly operate
834 * after we return from this method (it expects the Machine instance is
835 * still valid). We'll call it ourselves below.
836 */
837 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
838 (SessionMachine*)mData->mSession.mMachine));
839
840 if (Global::IsOnlineOrTransient(mData->mMachineState))
841 {
842 LogWarningThisFunc(("Setting state to Aborted!\n"));
843 /* set machine state using SessionMachine reimplementation */
844 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
845 }
846
847 /*
848 * Uninitialize SessionMachine using public uninit() to indicate
849 * an unexpected uninitialization.
850 */
851 mData->mSession.mMachine->uninit();
852 /* SessionMachine::uninit() must set mSession.mMachine to null */
853 Assert(mData->mSession.mMachine.isNull());
854 }
855
856 // uninit media from this machine's media registry, if they're still there
857 Guid uuidMachine(getId());
858
859 /* the lock is no more necessary (SessionMachine is uninitialized) */
860 alock.release();
861
862 /* XXX This will fail with
863 * "cannot be closed because it is still attached to 1 virtual machines"
864 * because at this point we did not call uninitDataAndChildObjects() yet
865 * and therefore also removeBackReference() for all these mediums was not called! */
866
867 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
868 mParent->unregisterMachineMedia(uuidMachine);
869
870 // has machine been modified?
871 if (mData->flModifications)
872 {
873 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
874 rollback(false /* aNotify */);
875 }
876
877 if (mData->mAccessible)
878 uninitDataAndChildObjects();
879
880 /* free the essential data structure last */
881 mData.free();
882
883 LogFlowThisFuncLeave();
884}
885
886// IMachine properties
887/////////////////////////////////////////////////////////////////////////////
888
889STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
890{
891 CheckComArgOutPointerValid(aParent);
892
893 AutoLimitedCaller autoCaller(this);
894 if (FAILED(autoCaller.rc())) return autoCaller.rc();
895
896 /* mParent is constant during life time, no need to lock */
897 ComObjPtr<VirtualBox> pVirtualBox(mParent);
898 pVirtualBox.queryInterfaceTo(aParent);
899
900 return S_OK;
901}
902
903STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
904{
905 CheckComArgOutPointerValid(aAccessible);
906
907 AutoLimitedCaller autoCaller(this);
908 if (FAILED(autoCaller.rc())) return autoCaller.rc();
909
910 LogFlowThisFunc(("ENTER\n"));
911
912 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
913
914 HRESULT rc = S_OK;
915
916 if (!mData->mAccessible)
917 {
918 /* try to initialize the VM once more if not accessible */
919
920 AutoReinitSpan autoReinitSpan(this);
921 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
922
923#ifdef DEBUG
924 LogFlowThisFunc(("Dumping media backreferences\n"));
925 mParent->dumpAllBackRefs();
926#endif
927
928 if (mData->pMachineConfigFile)
929 {
930 // reset the XML file to force loadSettings() (called from registeredInit())
931 // to parse it again; the file might have changed
932 delete mData->pMachineConfigFile;
933 mData->pMachineConfigFile = NULL;
934 }
935
936 rc = registeredInit();
937
938 if (SUCCEEDED(rc) && mData->mAccessible)
939 {
940 autoReinitSpan.setSucceeded();
941
942 /* make sure interesting parties will notice the accessibility
943 * state change */
944 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
945 mParent->onMachineDataChange(mData->mUuid);
946 }
947 }
948
949 if (SUCCEEDED(rc))
950 *aAccessible = mData->mAccessible;
951
952 LogFlowThisFuncLeave();
953
954 return rc;
955}
956
957STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
958{
959 CheckComArgOutPointerValid(aAccessError);
960
961 AutoLimitedCaller autoCaller(this);
962 if (FAILED(autoCaller.rc())) return autoCaller.rc();
963
964 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
965
966 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
967 {
968 /* return shortly */
969 aAccessError = NULL;
970 return S_OK;
971 }
972
973 HRESULT rc = S_OK;
974
975 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
976 rc = errorInfo.createObject();
977 if (SUCCEEDED(rc))
978 {
979 errorInfo->init(mData->mAccessError.getResultCode(),
980 mData->mAccessError.getInterfaceID().ref(),
981 Utf8Str(mData->mAccessError.getComponent()).c_str(),
982 Utf8Str(mData->mAccessError.getText()));
983 rc = errorInfo.queryInterfaceTo(aAccessError);
984 }
985
986 return rc;
987}
988
989STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
990{
991 CheckComArgOutPointerValid(aName);
992
993 AutoCaller autoCaller(this);
994 if (FAILED(autoCaller.rc())) return autoCaller.rc();
995
996 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
997
998 mUserData->s.strName.cloneTo(aName);
999
1000 return S_OK;
1001}
1002
1003STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
1004{
1005 CheckComArgStrNotEmptyOrNull(aName);
1006
1007 AutoCaller autoCaller(this);
1008 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1009
1010 // prohibit setting a UUID only as the machine name, or else it can
1011 // never be found by findMachine()
1012 Guid test(aName);
1013
1014 if (test.isValid())
1015 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1016
1017 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1018
1019 HRESULT rc = checkStateDependency(MutableStateDep);
1020 if (FAILED(rc)) return rc;
1021
1022 setModified(IsModified_MachineData);
1023 mUserData.backup();
1024 mUserData->s.strName = aName;
1025
1026 return S_OK;
1027}
1028
1029STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1030{
1031 CheckComArgOutPointerValid(aDescription);
1032
1033 AutoCaller autoCaller(this);
1034 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1035
1036 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1037
1038 mUserData->s.strDescription.cloneTo(aDescription);
1039
1040 return S_OK;
1041}
1042
1043STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1044{
1045 AutoCaller autoCaller(this);
1046 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1047
1048 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1049
1050 // this can be done in principle in any state as it doesn't affect the VM
1051 // significantly, but play safe by not messing around while complex
1052 // activities are going on
1053 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1054 if (FAILED(rc)) return rc;
1055
1056 setModified(IsModified_MachineData);
1057 mUserData.backup();
1058 mUserData->s.strDescription = aDescription;
1059
1060 return S_OK;
1061}
1062
1063STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1064{
1065 CheckComArgOutPointerValid(aId);
1066
1067 AutoLimitedCaller autoCaller(this);
1068 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1069
1070 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1071
1072 mData->mUuid.toUtf16().cloneTo(aId);
1073
1074 return S_OK;
1075}
1076
1077STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1078{
1079 CheckComArgOutSafeArrayPointerValid(aGroups);
1080
1081 AutoCaller autoCaller(this);
1082 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1083
1084 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1085 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1086 size_t i = 0;
1087 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1088 it != mUserData->s.llGroups.end();
1089 ++it, i++)
1090 {
1091 Bstr tmp = *it;
1092 tmp.cloneTo(&groups[i]);
1093 }
1094 groups.detachTo(ComSafeArrayOutArg(aGroups));
1095
1096 return S_OK;
1097}
1098
1099STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1100{
1101 AutoCaller autoCaller(this);
1102 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1103
1104 StringsList llGroups;
1105 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1106 if (FAILED(rc))
1107 return rc;
1108
1109 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1110
1111 // changing machine groups is possible while the VM is offline
1112 rc = checkStateDependency(OfflineStateDep);
1113 if (FAILED(rc)) return rc;
1114
1115 setModified(IsModified_MachineData);
1116 mUserData.backup();
1117 mUserData->s.llGroups = llGroups;
1118
1119 return S_OK;
1120}
1121
1122STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1123{
1124 CheckComArgOutPointerValid(aOSTypeId);
1125
1126 AutoCaller autoCaller(this);
1127 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1128
1129 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1130
1131 mUserData->s.strOsType.cloneTo(aOSTypeId);
1132
1133 return S_OK;
1134}
1135
1136STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1137{
1138 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1139
1140 AutoCaller autoCaller(this);
1141 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1142
1143 /* look up the object by Id to check it is valid */
1144 ComPtr<IGuestOSType> guestOSType;
1145 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1146 if (FAILED(rc)) return rc;
1147
1148 /* when setting, always use the "etalon" value for consistency -- lookup
1149 * by ID is case-insensitive and the input value may have different case */
1150 Bstr osTypeId;
1151 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1152 if (FAILED(rc)) return rc;
1153
1154 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1155
1156 rc = checkStateDependency(MutableStateDep);
1157 if (FAILED(rc)) return rc;
1158
1159 setModified(IsModified_MachineData);
1160 mUserData.backup();
1161 mUserData->s.strOsType = osTypeId;
1162
1163 return S_OK;
1164}
1165
1166
1167STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1168{
1169 CheckComArgOutPointerValid(aFirmwareType);
1170
1171 AutoCaller autoCaller(this);
1172 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1173
1174 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1175
1176 *aFirmwareType = mHWData->mFirmwareType;
1177
1178 return S_OK;
1179}
1180
1181STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1182{
1183 AutoCaller autoCaller(this);
1184 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1185 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1186
1187 HRESULT rc = checkStateDependency(MutableStateDep);
1188 if (FAILED(rc)) return rc;
1189
1190 setModified(IsModified_MachineData);
1191 mHWData.backup();
1192 mHWData->mFirmwareType = aFirmwareType;
1193
1194 return S_OK;
1195}
1196
1197STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1198{
1199 CheckComArgOutPointerValid(aKeyboardHIDType);
1200
1201 AutoCaller autoCaller(this);
1202 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1203
1204 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1205
1206 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1207
1208 return S_OK;
1209}
1210
1211STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1212{
1213 AutoCaller autoCaller(this);
1214 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1215 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1216
1217 HRESULT rc = checkStateDependency(MutableStateDep);
1218 if (FAILED(rc)) return rc;
1219
1220 setModified(IsModified_MachineData);
1221 mHWData.backup();
1222 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1223
1224 return S_OK;
1225}
1226
1227STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1228{
1229 CheckComArgOutPointerValid(aPointingHIDType);
1230
1231 AutoCaller autoCaller(this);
1232 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1233
1234 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1235
1236 *aPointingHIDType = mHWData->mPointingHIDType;
1237
1238 return S_OK;
1239}
1240
1241STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1242{
1243 AutoCaller autoCaller(this);
1244 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1245 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1246
1247 HRESULT rc = checkStateDependency(MutableStateDep);
1248 if (FAILED(rc)) return rc;
1249
1250 setModified(IsModified_MachineData);
1251 mHWData.backup();
1252 mHWData->mPointingHIDType = aPointingHIDType;
1253
1254 return S_OK;
1255}
1256
1257STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1258{
1259 CheckComArgOutPointerValid(aChipsetType);
1260
1261 AutoCaller autoCaller(this);
1262 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1263
1264 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1265
1266 *aChipsetType = mHWData->mChipsetType;
1267
1268 return S_OK;
1269}
1270
1271STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1272{
1273 AutoCaller autoCaller(this);
1274 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1275 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1276
1277 HRESULT rc = checkStateDependency(MutableStateDep);
1278 if (FAILED(rc)) return rc;
1279
1280 if (aChipsetType != mHWData->mChipsetType)
1281 {
1282 setModified(IsModified_MachineData);
1283 mHWData.backup();
1284 mHWData->mChipsetType = aChipsetType;
1285
1286 // Resize network adapter array, to be finalized on commit/rollback.
1287 // We must not throw away entries yet, otherwise settings are lost
1288 // without a way to roll back.
1289 uint32_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1290 uint32_t oldCount = mNetworkAdapters.size();
1291 if (newCount > oldCount)
1292 {
1293 mNetworkAdapters.resize(newCount);
1294 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1295 {
1296 unconst(mNetworkAdapters[slot]).createObject();
1297 mNetworkAdapters[slot]->init(this, slot);
1298 }
1299 }
1300 }
1301
1302 return S_OK;
1303}
1304
1305STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1306{
1307 CheckComArgOutPointerValid(aHWVersion);
1308
1309 AutoCaller autoCaller(this);
1310 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1311
1312 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1313
1314 mHWData->mHWVersion.cloneTo(aHWVersion);
1315
1316 return S_OK;
1317}
1318
1319STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1320{
1321 /* check known version */
1322 Utf8Str hwVersion = aHWVersion;
1323 if ( hwVersion.compare("1") != 0
1324 && hwVersion.compare("2") != 0)
1325 return setError(E_INVALIDARG,
1326 tr("Invalid hardware version: %ls\n"), aHWVersion);
1327
1328 AutoCaller autoCaller(this);
1329 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1330
1331 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1332
1333 HRESULT rc = checkStateDependency(MutableStateDep);
1334 if (FAILED(rc)) return rc;
1335
1336 setModified(IsModified_MachineData);
1337 mHWData.backup();
1338 mHWData->mHWVersion = hwVersion;
1339
1340 return S_OK;
1341}
1342
1343STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1344{
1345 CheckComArgOutPointerValid(aUUID);
1346
1347 AutoCaller autoCaller(this);
1348 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1349
1350 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1351
1352 if (mHWData->mHardwareUUID.isValid())
1353 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1354 else
1355 mData->mUuid.toUtf16().cloneTo(aUUID);
1356
1357 return S_OK;
1358}
1359
1360STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1361{
1362 Guid hardwareUUID(aUUID);
1363 if (!hardwareUUID.isValid())
1364 return E_INVALIDARG;
1365
1366 AutoCaller autoCaller(this);
1367 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1368
1369 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1370
1371 HRESULT rc = checkStateDependency(MutableStateDep);
1372 if (FAILED(rc)) return rc;
1373
1374 setModified(IsModified_MachineData);
1375 mHWData.backup();
1376 if (hardwareUUID == mData->mUuid)
1377 mHWData->mHardwareUUID.clear();
1378 else
1379 mHWData->mHardwareUUID = hardwareUUID;
1380
1381 return S_OK;
1382}
1383
1384STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1385{
1386 CheckComArgOutPointerValid(memorySize);
1387
1388 AutoCaller autoCaller(this);
1389 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1390
1391 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1392
1393 *memorySize = mHWData->mMemorySize;
1394
1395 return S_OK;
1396}
1397
1398STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1399{
1400 /* check RAM limits */
1401 if ( memorySize < MM_RAM_MIN_IN_MB
1402 || memorySize > MM_RAM_MAX_IN_MB
1403 )
1404 return setError(E_INVALIDARG,
1405 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1406 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1407
1408 AutoCaller autoCaller(this);
1409 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1410
1411 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1412
1413 HRESULT rc = checkStateDependency(MutableStateDep);
1414 if (FAILED(rc)) return rc;
1415
1416 setModified(IsModified_MachineData);
1417 mHWData.backup();
1418 mHWData->mMemorySize = memorySize;
1419
1420 return S_OK;
1421}
1422
1423STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1424{
1425 CheckComArgOutPointerValid(CPUCount);
1426
1427 AutoCaller autoCaller(this);
1428 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1429
1430 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1431
1432 *CPUCount = mHWData->mCPUCount;
1433
1434 return S_OK;
1435}
1436
1437STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1438{
1439 /* check CPU limits */
1440 if ( CPUCount < SchemaDefs::MinCPUCount
1441 || CPUCount > SchemaDefs::MaxCPUCount
1442 )
1443 return setError(E_INVALIDARG,
1444 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1445 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1446
1447 AutoCaller autoCaller(this);
1448 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1449
1450 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1451
1452 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1453 if (mHWData->mCPUHotPlugEnabled)
1454 {
1455 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1456 {
1457 if (mHWData->mCPUAttached[idx])
1458 return setError(E_INVALIDARG,
1459 tr("There is still a CPU attached to socket %lu."
1460 "Detach the CPU before removing the socket"),
1461 CPUCount, idx+1);
1462 }
1463 }
1464
1465 HRESULT rc = checkStateDependency(MutableStateDep);
1466 if (FAILED(rc)) return rc;
1467
1468 setModified(IsModified_MachineData);
1469 mHWData.backup();
1470 mHWData->mCPUCount = CPUCount;
1471
1472 return S_OK;
1473}
1474
1475STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1476{
1477 CheckComArgOutPointerValid(aExecutionCap);
1478
1479 AutoCaller autoCaller(this);
1480 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1481
1482 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1483
1484 *aExecutionCap = mHWData->mCpuExecutionCap;
1485
1486 return S_OK;
1487}
1488
1489STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1490{
1491 HRESULT rc = S_OK;
1492
1493 /* check throttle limits */
1494 if ( aExecutionCap < 1
1495 || aExecutionCap > 100
1496 )
1497 return setError(E_INVALIDARG,
1498 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1499 aExecutionCap, 1, 100);
1500
1501 AutoCaller autoCaller(this);
1502 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1503
1504 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1505
1506 alock.release();
1507 rc = onCPUExecutionCapChange(aExecutionCap);
1508 alock.acquire();
1509 if (FAILED(rc)) return rc;
1510
1511 setModified(IsModified_MachineData);
1512 mHWData.backup();
1513 mHWData->mCpuExecutionCap = aExecutionCap;
1514
1515 /* Save settings if online - todo why is this required?? */
1516 if (Global::IsOnline(mData->mMachineState))
1517 saveSettings(NULL);
1518
1519 return S_OK;
1520}
1521
1522
1523STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *aEnabled)
1524{
1525 CheckComArgOutPointerValid(aEnabled);
1526
1527 AutoCaller autoCaller(this);
1528 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1529
1530 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1531
1532 *aEnabled = mHWData->mCPUHotPlugEnabled;
1533
1534 return S_OK;
1535}
1536
1537STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL aEnabled)
1538{
1539 HRESULT rc = S_OK;
1540
1541 AutoCaller autoCaller(this);
1542 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1543
1544 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1545
1546 rc = checkStateDependency(MutableStateDep);
1547 if (FAILED(rc)) return rc;
1548
1549 if (mHWData->mCPUHotPlugEnabled != aEnabled)
1550 {
1551 if (aEnabled)
1552 {
1553 setModified(IsModified_MachineData);
1554 mHWData.backup();
1555
1556 /* Add the amount of CPUs currently attached */
1557 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1558 {
1559 mHWData->mCPUAttached[i] = true;
1560 }
1561 }
1562 else
1563 {
1564 /*
1565 * We can disable hotplug only if the amount of maximum CPUs is equal
1566 * to the amount of attached CPUs
1567 */
1568 unsigned cCpusAttached = 0;
1569 unsigned iHighestId = 0;
1570
1571 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1572 {
1573 if (mHWData->mCPUAttached[i])
1574 {
1575 cCpusAttached++;
1576 iHighestId = i;
1577 }
1578 }
1579
1580 if ( (cCpusAttached != mHWData->mCPUCount)
1581 || (iHighestId >= mHWData->mCPUCount))
1582 return setError(E_INVALIDARG,
1583 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1584
1585 setModified(IsModified_MachineData);
1586 mHWData.backup();
1587 }
1588 }
1589
1590 mHWData->mCPUHotPlugEnabled = aEnabled;
1591
1592 return rc;
1593}
1594
1595STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *aEnabled)
1596{
1597#ifdef VBOX_WITH_USB_CARDREADER
1598 CheckComArgOutPointerValid(aEnabled);
1599
1600 AutoCaller autoCaller(this);
1601 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1602
1603 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1604
1605 *aEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1606
1607 return S_OK;
1608#else
1609 NOREF(aEnabled);
1610 return E_NOTIMPL;
1611#endif
1612}
1613
1614STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL aEnabled)
1615{
1616#ifdef VBOX_WITH_USB_CARDREADER
1617 AutoCaller autoCaller(this);
1618 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1619 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1620
1621 HRESULT rc = checkStateDependency(MutableStateDep);
1622 if (FAILED(rc)) return rc;
1623
1624 setModified(IsModified_MachineData);
1625 mHWData.backup();
1626 mHWData->mEmulatedUSBCardReaderEnabled = aEnabled;
1627
1628 return S_OK;
1629#else
1630 NOREF(aEnabled);
1631 return E_NOTIMPL;
1632#endif
1633}
1634
1635STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *aEnabled)
1636{
1637#ifdef VBOX_WITH_USB_VIDEO
1638 CheckComArgOutPointerValid(aEnabled);
1639
1640 AutoCaller autoCaller(this);
1641 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1642
1643 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1644
1645 *aEnabled = mHWData->mEmulatedUSBWebcamEnabled;
1646
1647 return S_OK;
1648#else
1649 NOREF(aEnabled);
1650 return E_NOTIMPL;
1651#endif
1652}
1653
1654STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL aEnabled)
1655{
1656#ifdef VBOX_WITH_USB_VIDEO
1657 AutoCaller autoCaller(this);
1658 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1659 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1660
1661 HRESULT rc = checkStateDependency(MutableStateDep);
1662 if (FAILED(rc)) return rc;
1663
1664 setModified(IsModified_MachineData);
1665 mHWData.backup();
1666 mHWData->mEmulatedUSBWebcamEnabled = aEnabled;
1667
1668 return S_OK;
1669#else
1670 NOREF(aEnabled);
1671 return E_NOTIMPL;
1672#endif
1673}
1674
1675STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *aEnabled)
1676{
1677 CheckComArgOutPointerValid(aEnabled);
1678
1679 AutoCaller autoCaller(this);
1680 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1682
1683 *aEnabled = mHWData->mHPETEnabled;
1684
1685 return S_OK;
1686}
1687
1688STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL aEnabled)
1689{
1690 HRESULT rc = S_OK;
1691
1692 AutoCaller autoCaller(this);
1693 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1694 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1695
1696 rc = checkStateDependency(MutableStateDep);
1697 if (FAILED(rc)) return rc;
1698
1699 setModified(IsModified_MachineData);
1700 mHWData.backup();
1701
1702 mHWData->mHPETEnabled = aEnabled;
1703
1704 return rc;
1705}
1706
1707STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
1708{
1709 AutoCaller autoCaller(this);
1710 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1711
1712 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1713
1714 *fEnabled = mHWData->mVideoCaptureEnabled;
1715 return S_OK;
1716}
1717
1718STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1719{
1720 AutoCaller autoCaller(this);
1721 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1722
1723 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1724 mHWData->mVideoCaptureEnabled = fEnabled;
1725 return S_OK;
1726}
1727
1728STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR *apFile)
1729{
1730 AutoCaller autoCaller(this);
1731 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1732
1733 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1734 mHWData->mVideoCaptureFile.cloneTo(apFile);
1735 return S_OK;
1736}
1737
1738STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1739{
1740 Utf8Str strFile(aFile);
1741 AutoCaller autoCaller(this);
1742 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1743
1744 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1745 if (strFile.isEmpty())
1746 strFile = "VideoCap.webm";
1747 mHWData->mVideoCaptureFile = strFile;
1748 return S_OK;
1749}
1750
1751STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *aHorzRes)
1752{
1753 AutoCaller autoCaller(this);
1754 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1755
1756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1757 *aHorzRes = mHWData->mVideoCaptureWidth;
1758 return S_OK;
1759}
1760
1761STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG aHorzRes)
1762{
1763 AutoCaller autoCaller(this);
1764 if (FAILED(autoCaller.rc()))
1765 {
1766 LogFlow(("Autolocked failed\n"));
1767 return autoCaller.rc();
1768 }
1769
1770 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1771 mHWData->mVideoCaptureWidth = aHorzRes;
1772 return S_OK;
1773}
1774
1775STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *aVertRes)
1776{
1777 AutoCaller autoCaller(this);
1778 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1779
1780 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1781 *aVertRes = mHWData->mVideoCaptureHeight;
1782 return S_OK;
1783}
1784
1785STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG aVertRes)
1786{
1787 AutoCaller autoCaller(this);
1788 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1789
1790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1791 mHWData->mVideoCaptureHeight = aVertRes;
1792 return S_OK;
1793}
1794
1795STDMETHODIMP Machine::COMGETTER(VideoCaptureRate)(ULONG *aRate)
1796{
1797 AutoCaller autoCaller(this);
1798 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1799
1800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1801 *aRate = mHWData->mVideoCaptureRate;
1802 return S_OK;
1803}
1804
1805STDMETHODIMP Machine::COMSETTER(VideoCaptureRate)(ULONG aRate)
1806{
1807 AutoCaller autoCaller(this);
1808 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1809
1810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1811 mHWData->mVideoCaptureRate = aRate;
1812 return S_OK;
1813}
1814
1815STDMETHODIMP Machine::COMGETTER(VideoCaptureFps)(ULONG *aFps)
1816{
1817 AutoCaller autoCaller(this);
1818 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1819
1820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1821 *aFps = mHWData->mVideoCaptureFps;
1822 return S_OK;
1823}
1824
1825STDMETHODIMP Machine::COMSETTER(VideoCaptureFps)(ULONG aFps)
1826{
1827 AutoCaller autoCaller(this);
1828 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1829
1830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1831 mHWData->mVideoCaptureFps = aFps;
1832 return S_OK;
1833}
1834
1835STDMETHODIMP Machine::COMGETTER(GraphicsControllerType)(GraphicsControllerType_T *aGraphicsControllerType)
1836{
1837 CheckComArgOutPointerValid(aGraphicsControllerType);
1838
1839 AutoCaller autoCaller(this);
1840 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1841
1842 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1843
1844 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1845
1846 return S_OK;
1847}
1848
1849STDMETHODIMP Machine::COMSETTER(GraphicsControllerType)(GraphicsControllerType_T aGraphicsControllerType)
1850{
1851 switch (aGraphicsControllerType)
1852 {
1853 case GraphicsControllerType_Null:
1854 case GraphicsControllerType_VBoxVGA:
1855 break;
1856 default:
1857 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1858 }
1859
1860 AutoCaller autoCaller(this);
1861 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1862
1863 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1864
1865 HRESULT rc = checkStateDependency(MutableStateDep);
1866 if (FAILED(rc)) return rc;
1867
1868 setModified(IsModified_MachineData);
1869 mHWData.backup();
1870 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1871
1872 return S_OK;
1873}
1874
1875STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1876{
1877 CheckComArgOutPointerValid(memorySize);
1878
1879 AutoCaller autoCaller(this);
1880 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1881
1882 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1883
1884 *memorySize = mHWData->mVRAMSize;
1885
1886 return S_OK;
1887}
1888
1889STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1890{
1891 /* check VRAM limits */
1892 if (memorySize < SchemaDefs::MinGuestVRAM ||
1893 memorySize > SchemaDefs::MaxGuestVRAM)
1894 return setError(E_INVALIDARG,
1895 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1896 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1897
1898 AutoCaller autoCaller(this);
1899 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1900
1901 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1902
1903 HRESULT rc = checkStateDependency(MutableStateDep);
1904 if (FAILED(rc)) return rc;
1905
1906 setModified(IsModified_MachineData);
1907 mHWData.backup();
1908 mHWData->mVRAMSize = memorySize;
1909
1910 return S_OK;
1911}
1912
1913/** @todo this method should not be public */
1914STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1915{
1916 CheckComArgOutPointerValid(memoryBalloonSize);
1917
1918 AutoCaller autoCaller(this);
1919 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1920
1921 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1922
1923 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
1924
1925 return S_OK;
1926}
1927
1928/**
1929 * Set the memory balloon size.
1930 *
1931 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1932 * we have to make sure that we never call IGuest from here.
1933 */
1934STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
1935{
1936 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1937#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1938 /* check limits */
1939 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1940 return setError(E_INVALIDARG,
1941 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1942 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1943
1944 AutoCaller autoCaller(this);
1945 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1946
1947 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1948
1949 setModified(IsModified_MachineData);
1950 mHWData.backup();
1951 mHWData->mMemoryBalloonSize = memoryBalloonSize;
1952
1953 return S_OK;
1954#else
1955 NOREF(memoryBalloonSize);
1956 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1957#endif
1958}
1959
1960STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *aEnabled)
1961{
1962 CheckComArgOutPointerValid(aEnabled);
1963
1964 AutoCaller autoCaller(this);
1965 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1966
1967 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1968
1969 *aEnabled = mHWData->mPageFusionEnabled;
1970 return S_OK;
1971}
1972
1973STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL aEnabled)
1974{
1975#ifdef VBOX_WITH_PAGE_SHARING
1976 AutoCaller autoCaller(this);
1977 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1978
1979 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1980
1981 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1982 setModified(IsModified_MachineData);
1983 mHWData.backup();
1984 mHWData->mPageFusionEnabled = aEnabled;
1985 return S_OK;
1986#else
1987 NOREF(aEnabled);
1988 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1989#endif
1990}
1991
1992STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *aEnabled)
1993{
1994 CheckComArgOutPointerValid(aEnabled);
1995
1996 AutoCaller autoCaller(this);
1997 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1998
1999 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2000
2001 *aEnabled = mHWData->mAccelerate3DEnabled;
2002
2003 return S_OK;
2004}
2005
2006STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
2007{
2008 AutoCaller autoCaller(this);
2009 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2010
2011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2012
2013 HRESULT rc = checkStateDependency(MutableStateDep);
2014 if (FAILED(rc)) return rc;
2015
2016 /** @todo check validity! */
2017
2018 setModified(IsModified_MachineData);
2019 mHWData.backup();
2020 mHWData->mAccelerate3DEnabled = enable;
2021
2022 return S_OK;
2023}
2024
2025
2026STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *aEnabled)
2027{
2028 CheckComArgOutPointerValid(aEnabled);
2029
2030 AutoCaller autoCaller(this);
2031 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2032
2033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2034
2035 *aEnabled = mHWData->mAccelerate2DVideoEnabled;
2036
2037 return S_OK;
2038}
2039
2040STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
2041{
2042 AutoCaller autoCaller(this);
2043 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2044
2045 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2046
2047 HRESULT rc = checkStateDependency(MutableStateDep);
2048 if (FAILED(rc)) return rc;
2049
2050 /** @todo check validity! */
2051
2052 setModified(IsModified_MachineData);
2053 mHWData.backup();
2054 mHWData->mAccelerate2DVideoEnabled = enable;
2055
2056 return S_OK;
2057}
2058
2059STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
2060{
2061 CheckComArgOutPointerValid(monitorCount);
2062
2063 AutoCaller autoCaller(this);
2064 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2065
2066 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2067
2068 *monitorCount = mHWData->mMonitorCount;
2069
2070 return S_OK;
2071}
2072
2073STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
2074{
2075 /* make sure monitor count is a sensible number */
2076 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
2077 return setError(E_INVALIDARG,
2078 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2079 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
2080
2081 AutoCaller autoCaller(this);
2082 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2083
2084 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2085
2086 HRESULT rc = checkStateDependency(MutableStateDep);
2087 if (FAILED(rc)) return rc;
2088
2089 setModified(IsModified_MachineData);
2090 mHWData.backup();
2091 mHWData->mMonitorCount = monitorCount;
2092
2093 return S_OK;
2094}
2095
2096STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2097{
2098 CheckComArgOutPointerValid(biosSettings);
2099
2100 AutoCaller autoCaller(this);
2101 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2102
2103 /* mBIOSSettings is constant during life time, no need to lock */
2104 mBIOSSettings.queryInterfaceTo(biosSettings);
2105
2106 return S_OK;
2107}
2108
2109STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2110{
2111 CheckComArgOutPointerValid(aVal);
2112
2113 AutoCaller autoCaller(this);
2114 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2115
2116 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2117
2118 switch (property)
2119 {
2120 case CPUPropertyType_PAE:
2121 *aVal = mHWData->mPAEEnabled;
2122 break;
2123
2124 case CPUPropertyType_Synthetic:
2125 *aVal = mHWData->mSyntheticCpu;
2126 break;
2127
2128 case CPUPropertyType_LongMode:
2129 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2130 *aVal = TRUE;
2131 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2132 *aVal = FALSE;
2133#if HC_ARCH_BITS == 64
2134 else
2135 *aVal = TRUE;
2136#else
2137 else
2138 {
2139 *aVal = FALSE;
2140
2141 ComPtr<IGuestOSType> ptrGuestOSType;
2142 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2143 if (SUCCEEDED(hrc2))
2144 {
2145 BOOL fIs64Bit = FALSE;
2146 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2147 if (SUCCEEDED(hrc2) && fIs64Bit)
2148 {
2149 ComObjPtr<Host> ptrHost = mParent->host();
2150 alock.release();
2151
2152 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal); AssertComRC(hrc2);
2153 if (FAILED(hrc2))
2154 *aVal = FALSE;
2155 }
2156 }
2157 }
2158#endif
2159 break;
2160
2161 default:
2162 return E_INVALIDARG;
2163 }
2164 return S_OK;
2165}
2166
2167STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2168{
2169 AutoCaller autoCaller(this);
2170 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2171
2172 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2173
2174 HRESULT rc = checkStateDependency(MutableStateDep);
2175 if (FAILED(rc)) return rc;
2176
2177 switch (property)
2178 {
2179 case CPUPropertyType_PAE:
2180 setModified(IsModified_MachineData);
2181 mHWData.backup();
2182 mHWData->mPAEEnabled = !!aVal;
2183 break;
2184
2185 case CPUPropertyType_Synthetic:
2186 setModified(IsModified_MachineData);
2187 mHWData.backup();
2188 mHWData->mSyntheticCpu = !!aVal;
2189 break;
2190
2191 case CPUPropertyType_LongMode:
2192 setModified(IsModified_MachineData);
2193 mHWData.backup();
2194 mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2195 break;
2196
2197 default:
2198 return E_INVALIDARG;
2199 }
2200 return S_OK;
2201}
2202
2203STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2204{
2205 CheckComArgOutPointerValid(aValEax);
2206 CheckComArgOutPointerValid(aValEbx);
2207 CheckComArgOutPointerValid(aValEcx);
2208 CheckComArgOutPointerValid(aValEdx);
2209
2210 AutoCaller autoCaller(this);
2211 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2212
2213 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2214
2215 switch(aId)
2216 {
2217 case 0x0:
2218 case 0x1:
2219 case 0x2:
2220 case 0x3:
2221 case 0x4:
2222 case 0x5:
2223 case 0x6:
2224 case 0x7:
2225 case 0x8:
2226 case 0x9:
2227 case 0xA:
2228 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2229 return E_INVALIDARG;
2230
2231 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2232 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2233 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2234 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2235 break;
2236
2237 case 0x80000000:
2238 case 0x80000001:
2239 case 0x80000002:
2240 case 0x80000003:
2241 case 0x80000004:
2242 case 0x80000005:
2243 case 0x80000006:
2244 case 0x80000007:
2245 case 0x80000008:
2246 case 0x80000009:
2247 case 0x8000000A:
2248 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2249 return E_INVALIDARG;
2250
2251 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2252 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2253 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2254 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2255 break;
2256
2257 default:
2258 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2259 }
2260 return S_OK;
2261}
2262
2263STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2264{
2265 AutoCaller autoCaller(this);
2266 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2267
2268 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2269
2270 HRESULT rc = checkStateDependency(MutableStateDep);
2271 if (FAILED(rc)) return rc;
2272
2273 switch(aId)
2274 {
2275 case 0x0:
2276 case 0x1:
2277 case 0x2:
2278 case 0x3:
2279 case 0x4:
2280 case 0x5:
2281 case 0x6:
2282 case 0x7:
2283 case 0x8:
2284 case 0x9:
2285 case 0xA:
2286 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2287 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2288 setModified(IsModified_MachineData);
2289 mHWData.backup();
2290 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2291 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2292 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2293 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2294 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2295 break;
2296
2297 case 0x80000000:
2298 case 0x80000001:
2299 case 0x80000002:
2300 case 0x80000003:
2301 case 0x80000004:
2302 case 0x80000005:
2303 case 0x80000006:
2304 case 0x80000007:
2305 case 0x80000008:
2306 case 0x80000009:
2307 case 0x8000000A:
2308 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2309 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2310 setModified(IsModified_MachineData);
2311 mHWData.backup();
2312 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2313 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2314 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2315 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2316 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2317 break;
2318
2319 default:
2320 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2321 }
2322 return S_OK;
2323}
2324
2325STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2326{
2327 AutoCaller autoCaller(this);
2328 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2329
2330 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2331
2332 HRESULT rc = checkStateDependency(MutableStateDep);
2333 if (FAILED(rc)) return rc;
2334
2335 switch(aId)
2336 {
2337 case 0x0:
2338 case 0x1:
2339 case 0x2:
2340 case 0x3:
2341 case 0x4:
2342 case 0x5:
2343 case 0x6:
2344 case 0x7:
2345 case 0x8:
2346 case 0x9:
2347 case 0xA:
2348 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2349 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2350 setModified(IsModified_MachineData);
2351 mHWData.backup();
2352 /* Invalidate leaf. */
2353 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2354 break;
2355
2356 case 0x80000000:
2357 case 0x80000001:
2358 case 0x80000002:
2359 case 0x80000003:
2360 case 0x80000004:
2361 case 0x80000005:
2362 case 0x80000006:
2363 case 0x80000007:
2364 case 0x80000008:
2365 case 0x80000009:
2366 case 0x8000000A:
2367 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2368 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2369 setModified(IsModified_MachineData);
2370 mHWData.backup();
2371 /* Invalidate leaf. */
2372 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2373 break;
2374
2375 default:
2376 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2377 }
2378 return S_OK;
2379}
2380
2381STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2382{
2383 AutoCaller autoCaller(this);
2384 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2385
2386 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2387
2388 HRESULT rc = checkStateDependency(MutableStateDep);
2389 if (FAILED(rc)) return rc;
2390
2391 setModified(IsModified_MachineData);
2392 mHWData.backup();
2393
2394 /* Invalidate all standard leafs. */
2395 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2396 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2397
2398 /* Invalidate all extended leafs. */
2399 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2400 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2401
2402 return S_OK;
2403}
2404
2405STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2406{
2407 CheckComArgOutPointerValid(aVal);
2408
2409 AutoCaller autoCaller(this);
2410 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2411
2412 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2413
2414 switch(property)
2415 {
2416 case HWVirtExPropertyType_Enabled:
2417 *aVal = mHWData->mHWVirtExEnabled;
2418 break;
2419
2420 case HWVirtExPropertyType_Exclusive:
2421 *aVal = mHWData->mHWVirtExExclusive;
2422 break;
2423
2424 case HWVirtExPropertyType_VPID:
2425 *aVal = mHWData->mHWVirtExVPIDEnabled;
2426 break;
2427
2428 case HWVirtExPropertyType_NestedPaging:
2429 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2430 break;
2431
2432 case HWVirtExPropertyType_UnrestrictedExecution:
2433 *aVal = mHWData->mHWVirtExUXEnabled;
2434 break;
2435
2436 case HWVirtExPropertyType_LargePages:
2437 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2438#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2439 *aVal = FALSE;
2440#endif
2441 break;
2442
2443 case HWVirtExPropertyType_Force:
2444 *aVal = mHWData->mHWVirtExForceEnabled;
2445 break;
2446
2447 default:
2448 return E_INVALIDARG;
2449 }
2450 return S_OK;
2451}
2452
2453STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2454{
2455 AutoCaller autoCaller(this);
2456 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2457
2458 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2459
2460 HRESULT rc = checkStateDependency(MutableStateDep);
2461 if (FAILED(rc)) return rc;
2462
2463 switch(property)
2464 {
2465 case HWVirtExPropertyType_Enabled:
2466 setModified(IsModified_MachineData);
2467 mHWData.backup();
2468 mHWData->mHWVirtExEnabled = !!aVal;
2469 break;
2470
2471 case HWVirtExPropertyType_Exclusive:
2472 setModified(IsModified_MachineData);
2473 mHWData.backup();
2474 mHWData->mHWVirtExExclusive = !!aVal;
2475 break;
2476
2477 case HWVirtExPropertyType_VPID:
2478 setModified(IsModified_MachineData);
2479 mHWData.backup();
2480 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2481 break;
2482
2483 case HWVirtExPropertyType_NestedPaging:
2484 setModified(IsModified_MachineData);
2485 mHWData.backup();
2486 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2487 break;
2488
2489 case HWVirtExPropertyType_UnrestrictedExecution:
2490 setModified(IsModified_MachineData);
2491 mHWData.backup();
2492 mHWData->mHWVirtExUXEnabled = !!aVal;
2493 break;
2494
2495 case HWVirtExPropertyType_LargePages:
2496 setModified(IsModified_MachineData);
2497 mHWData.backup();
2498 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2499 break;
2500
2501 case HWVirtExPropertyType_Force:
2502 setModified(IsModified_MachineData);
2503 mHWData.backup();
2504 mHWData->mHWVirtExForceEnabled = !!aVal;
2505 break;
2506
2507 default:
2508 return E_INVALIDARG;
2509 }
2510
2511 return S_OK;
2512}
2513
2514STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2515{
2516 CheckComArgOutPointerValid(aSnapshotFolder);
2517
2518 AutoCaller autoCaller(this);
2519 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2520
2521 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2522
2523 Utf8Str strFullSnapshotFolder;
2524 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2525 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2526
2527 return S_OK;
2528}
2529
2530STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2531{
2532 /* @todo (r=dmik):
2533 * 1. Allow to change the name of the snapshot folder containing snapshots
2534 * 2. Rename the folder on disk instead of just changing the property
2535 * value (to be smart and not to leave garbage). Note that it cannot be
2536 * done here because the change may be rolled back. Thus, the right
2537 * place is #saveSettings().
2538 */
2539
2540 AutoCaller autoCaller(this);
2541 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2542
2543 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2544
2545 HRESULT rc = checkStateDependency(MutableStateDep);
2546 if (FAILED(rc)) return rc;
2547
2548 if (!mData->mCurrentSnapshot.isNull())
2549 return setError(E_FAIL,
2550 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2551
2552 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2553
2554 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2555 if (strSnapshotFolder.isEmpty())
2556 strSnapshotFolder = "Snapshots";
2557 int vrc = calculateFullPath(strSnapshotFolder,
2558 strSnapshotFolder);
2559 if (RT_FAILURE(vrc))
2560 return setError(E_FAIL,
2561 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2562 aSnapshotFolder, vrc);
2563
2564 setModified(IsModified_MachineData);
2565 mUserData.backup();
2566
2567 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2568
2569 return S_OK;
2570}
2571
2572STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2573{
2574 CheckComArgOutSafeArrayPointerValid(aAttachments);
2575
2576 AutoCaller autoCaller(this);
2577 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2578
2579 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2580
2581 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2582 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2583
2584 return S_OK;
2585}
2586
2587STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2588{
2589 CheckComArgOutPointerValid(vrdeServer);
2590
2591 AutoCaller autoCaller(this);
2592 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2593
2594 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2595
2596 Assert(!!mVRDEServer);
2597 mVRDEServer.queryInterfaceTo(vrdeServer);
2598
2599 return S_OK;
2600}
2601
2602STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2603{
2604 CheckComArgOutPointerValid(audioAdapter);
2605
2606 AutoCaller autoCaller(this);
2607 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2608
2609 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2610
2611 mAudioAdapter.queryInterfaceTo(audioAdapter);
2612 return S_OK;
2613}
2614
2615STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2616{
2617#ifdef VBOX_WITH_VUSB
2618 CheckComArgOutPointerValid(aUSBController);
2619
2620 AutoCaller autoCaller(this);
2621 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2622
2623 clearError();
2624 MultiResult rc(S_OK);
2625
2626# ifdef VBOX_WITH_USB
2627 rc = mParent->host()->checkUSBProxyService();
2628 if (FAILED(rc)) return rc;
2629# endif
2630
2631 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2632
2633 return rc = mUSBController.queryInterfaceTo(aUSBController);
2634#else
2635 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2636 * extended error info to indicate that USB is simply not available
2637 * (w/o treating it as a failure), for example, as in OSE */
2638 NOREF(aUSBController);
2639 ReturnComNotImplemented();
2640#endif /* VBOX_WITH_VUSB */
2641}
2642
2643STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2644{
2645 CheckComArgOutPointerValid(aFilePath);
2646
2647 AutoLimitedCaller autoCaller(this);
2648 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2649
2650 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2651
2652 mData->m_strConfigFileFull.cloneTo(aFilePath);
2653 return S_OK;
2654}
2655
2656STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2657{
2658 CheckComArgOutPointerValid(aModified);
2659
2660 AutoCaller autoCaller(this);
2661 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2662
2663 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2664
2665 HRESULT rc = checkStateDependency(MutableStateDep);
2666 if (FAILED(rc)) return rc;
2667
2668 if (!mData->pMachineConfigFile->fileExists())
2669 // this is a new machine, and no config file exists yet:
2670 *aModified = TRUE;
2671 else
2672 *aModified = (mData->flModifications != 0);
2673
2674 return S_OK;
2675}
2676
2677STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2678{
2679 CheckComArgOutPointerValid(aSessionState);
2680
2681 AutoCaller autoCaller(this);
2682 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2683
2684 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2685
2686 *aSessionState = mData->mSession.mState;
2687
2688 return S_OK;
2689}
2690
2691STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2692{
2693 CheckComArgOutPointerValid(aSessionType);
2694
2695 AutoCaller autoCaller(this);
2696 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2697
2698 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2699
2700 mData->mSession.mType.cloneTo(aSessionType);
2701
2702 return S_OK;
2703}
2704
2705STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2706{
2707 CheckComArgOutPointerValid(aSessionPID);
2708
2709 AutoCaller autoCaller(this);
2710 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2711
2712 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2713
2714 *aSessionPID = mData->mSession.mPID;
2715
2716 return S_OK;
2717}
2718
2719STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2720{
2721 CheckComArgOutPointerValid(machineState);
2722
2723 AutoCaller autoCaller(this);
2724 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2725
2726 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2727
2728 *machineState = mData->mMachineState;
2729
2730 return S_OK;
2731}
2732
2733STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2734{
2735 CheckComArgOutPointerValid(aLastStateChange);
2736
2737 AutoCaller autoCaller(this);
2738 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2739
2740 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2741
2742 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2743
2744 return S_OK;
2745}
2746
2747STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2748{
2749 CheckComArgOutPointerValid(aStateFilePath);
2750
2751 AutoCaller autoCaller(this);
2752 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2753
2754 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2755
2756 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2757
2758 return S_OK;
2759}
2760
2761STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2762{
2763 CheckComArgOutPointerValid(aLogFolder);
2764
2765 AutoCaller autoCaller(this);
2766 AssertComRCReturnRC(autoCaller.rc());
2767
2768 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2769
2770 Utf8Str logFolder;
2771 getLogFolder(logFolder);
2772 logFolder.cloneTo(aLogFolder);
2773
2774 return S_OK;
2775}
2776
2777STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2778{
2779 CheckComArgOutPointerValid(aCurrentSnapshot);
2780
2781 AutoCaller autoCaller(this);
2782 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2783
2784 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2785
2786 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2787
2788 return S_OK;
2789}
2790
2791STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2792{
2793 CheckComArgOutPointerValid(aSnapshotCount);
2794
2795 AutoCaller autoCaller(this);
2796 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2797
2798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2799
2800 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2801 ? 0
2802 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2803
2804 return S_OK;
2805}
2806
2807STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2808{
2809 CheckComArgOutPointerValid(aCurrentStateModified);
2810
2811 AutoCaller autoCaller(this);
2812 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2813
2814 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2815
2816 /* Note: for machines with no snapshots, we always return FALSE
2817 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2818 * reasons :) */
2819
2820 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2821 ? FALSE
2822 : mData->mCurrentStateModified;
2823
2824 return S_OK;
2825}
2826
2827STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2828{
2829 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2830
2831 AutoCaller autoCaller(this);
2832 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2833
2834 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2835
2836 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2837 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2838
2839 return S_OK;
2840}
2841
2842STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2843{
2844 CheckComArgOutPointerValid(aClipboardMode);
2845
2846 AutoCaller autoCaller(this);
2847 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2848
2849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2850
2851 *aClipboardMode = mHWData->mClipboardMode;
2852
2853 return S_OK;
2854}
2855
2856STDMETHODIMP
2857Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2858{
2859 HRESULT rc = S_OK;
2860
2861 AutoCaller autoCaller(this);
2862 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2863
2864 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2865
2866 alock.release();
2867 rc = onClipboardModeChange(aClipboardMode);
2868 alock.acquire();
2869 if (FAILED(rc)) return rc;
2870
2871 setModified(IsModified_MachineData);
2872 mHWData.backup();
2873 mHWData->mClipboardMode = aClipboardMode;
2874
2875 /* Save settings if online - todo why is this required?? */
2876 if (Global::IsOnline(mData->mMachineState))
2877 saveSettings(NULL);
2878
2879 return S_OK;
2880}
2881
2882STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
2883{
2884 CheckComArgOutPointerValid(aDragAndDropMode);
2885
2886 AutoCaller autoCaller(this);
2887 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2888
2889 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2890
2891 *aDragAndDropMode = mHWData->mDragAndDropMode;
2892
2893 return S_OK;
2894}
2895
2896STDMETHODIMP
2897Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
2898{
2899 HRESULT rc = S_OK;
2900
2901 AutoCaller autoCaller(this);
2902 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2903
2904 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2905
2906 alock.release();
2907 rc = onDragAndDropModeChange(aDragAndDropMode);
2908 alock.acquire();
2909 if (FAILED(rc)) return rc;
2910
2911 setModified(IsModified_MachineData);
2912 mHWData.backup();
2913 mHWData->mDragAndDropMode = aDragAndDropMode;
2914
2915 /* Save settings if online - todo why is this required?? */
2916 if (Global::IsOnline(mData->mMachineState))
2917 saveSettings(NULL);
2918
2919 return S_OK;
2920}
2921
2922STDMETHODIMP
2923Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
2924{
2925 CheckComArgOutPointerValid(aPatterns);
2926
2927 AutoCaller autoCaller(this);
2928 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2929
2930 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2931
2932 try
2933 {
2934 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
2935 }
2936 catch (...)
2937 {
2938 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2939 }
2940
2941 return S_OK;
2942}
2943
2944STDMETHODIMP
2945Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
2946{
2947 AutoCaller autoCaller(this);
2948 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2949
2950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2951
2952 HRESULT rc = checkStateDependency(MutableStateDep);
2953 if (FAILED(rc)) return rc;
2954
2955 setModified(IsModified_MachineData);
2956 mHWData.backup();
2957 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
2958 return rc;
2959}
2960
2961STDMETHODIMP
2962Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
2963{
2964 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
2965
2966 AutoCaller autoCaller(this);
2967 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2968
2969 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2970
2971 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
2972 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
2973
2974 return S_OK;
2975}
2976
2977STDMETHODIMP
2978Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
2979{
2980 CheckComArgOutPointerValid(aEnabled);
2981
2982 AutoCaller autoCaller(this);
2983 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2984
2985 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2986
2987 *aEnabled = mUserData->s.fTeleporterEnabled;
2988
2989 return S_OK;
2990}
2991
2992STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
2993{
2994 AutoCaller autoCaller(this);
2995 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2996
2997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2998
2999 /* Only allow it to be set to true when PoweredOff or Aborted.
3000 (Clearing it is always permitted.) */
3001 if ( aEnabled
3002 && mData->mRegistered
3003 && ( !isSessionMachine()
3004 || ( mData->mMachineState != MachineState_PoweredOff
3005 && mData->mMachineState != MachineState_Teleported
3006 && mData->mMachineState != MachineState_Aborted
3007 )
3008 )
3009 )
3010 return setError(VBOX_E_INVALID_VM_STATE,
3011 tr("The machine is not powered off (state is %s)"),
3012 Global::stringifyMachineState(mData->mMachineState));
3013
3014 setModified(IsModified_MachineData);
3015 mUserData.backup();
3016 mUserData->s.fTeleporterEnabled = !!aEnabled;
3017
3018 return S_OK;
3019}
3020
3021STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
3022{
3023 CheckComArgOutPointerValid(aPort);
3024
3025 AutoCaller autoCaller(this);
3026 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3027
3028 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3029
3030 *aPort = (ULONG)mUserData->s.uTeleporterPort;
3031
3032 return S_OK;
3033}
3034
3035STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
3036{
3037 if (aPort >= _64K)
3038 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
3039
3040 AutoCaller autoCaller(this);
3041 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3042
3043 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3044
3045 HRESULT rc = checkStateDependency(MutableStateDep);
3046 if (FAILED(rc)) return rc;
3047
3048 setModified(IsModified_MachineData);
3049 mUserData.backup();
3050 mUserData->s.uTeleporterPort = (uint32_t)aPort;
3051
3052 return S_OK;
3053}
3054
3055STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
3056{
3057 CheckComArgOutPointerValid(aAddress);
3058
3059 AutoCaller autoCaller(this);
3060 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3061
3062 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3063
3064 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
3065
3066 return S_OK;
3067}
3068
3069STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
3070{
3071 AutoCaller autoCaller(this);
3072 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3073
3074 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3075
3076 HRESULT rc = checkStateDependency(MutableStateDep);
3077 if (FAILED(rc)) return rc;
3078
3079 setModified(IsModified_MachineData);
3080 mUserData.backup();
3081 mUserData->s.strTeleporterAddress = aAddress;
3082
3083 return S_OK;
3084}
3085
3086STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
3087{
3088 CheckComArgOutPointerValid(aPassword);
3089
3090 AutoCaller autoCaller(this);
3091 HRESULT hrc = autoCaller.rc();
3092 if (SUCCEEDED(hrc))
3093 {
3094 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3095 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
3096 }
3097
3098 return hrc;
3099}
3100
3101STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
3102{
3103 /*
3104 * Hash the password first.
3105 */
3106 Utf8Str strPassword(aPassword);
3107 if (!strPassword.isEmpty())
3108 {
3109 if (VBoxIsPasswordHashed(&strPassword))
3110 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3111 VBoxHashPassword(&strPassword);
3112 }
3113
3114 /*
3115 * Do the update.
3116 */
3117 AutoCaller autoCaller(this);
3118 HRESULT hrc = autoCaller.rc();
3119 if (SUCCEEDED(hrc))
3120 {
3121 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3122 hrc = checkStateDependency(MutableStateDep);
3123 if (SUCCEEDED(hrc))
3124 {
3125 setModified(IsModified_MachineData);
3126 mUserData.backup();
3127 mUserData->s.strTeleporterPassword = strPassword;
3128 }
3129 }
3130
3131 return hrc;
3132}
3133
3134STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
3135{
3136 CheckComArgOutPointerValid(aState);
3137
3138 AutoCaller autoCaller(this);
3139 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3140
3141 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3142
3143 *aState = mUserData->s.enmFaultToleranceState;
3144 return S_OK;
3145}
3146
3147STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
3148{
3149 AutoCaller autoCaller(this);
3150 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3151
3152 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3153
3154 /* @todo deal with running state change. */
3155 HRESULT rc = checkStateDependency(MutableStateDep);
3156 if (FAILED(rc)) return rc;
3157
3158 setModified(IsModified_MachineData);
3159 mUserData.backup();
3160 mUserData->s.enmFaultToleranceState = aState;
3161 return S_OK;
3162}
3163
3164STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3165{
3166 CheckComArgOutPointerValid(aAddress);
3167
3168 AutoCaller autoCaller(this);
3169 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3170
3171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3172
3173 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3174 return S_OK;
3175}
3176
3177STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3178{
3179 AutoCaller autoCaller(this);
3180 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3181
3182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3183
3184 /* @todo deal with running state change. */
3185 HRESULT rc = checkStateDependency(MutableStateDep);
3186 if (FAILED(rc)) return rc;
3187
3188 setModified(IsModified_MachineData);
3189 mUserData.backup();
3190 mUserData->s.strFaultToleranceAddress = aAddress;
3191 return S_OK;
3192}
3193
3194STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3195{
3196 CheckComArgOutPointerValid(aPort);
3197
3198 AutoCaller autoCaller(this);
3199 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3200
3201 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3202
3203 *aPort = mUserData->s.uFaultTolerancePort;
3204 return S_OK;
3205}
3206
3207STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3208{
3209 AutoCaller autoCaller(this);
3210 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3211
3212 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3213
3214 /* @todo deal with running state change. */
3215 HRESULT rc = checkStateDependency(MutableStateDep);
3216 if (FAILED(rc)) return rc;
3217
3218 setModified(IsModified_MachineData);
3219 mUserData.backup();
3220 mUserData->s.uFaultTolerancePort = aPort;
3221 return S_OK;
3222}
3223
3224STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3225{
3226 CheckComArgOutPointerValid(aPassword);
3227
3228 AutoCaller autoCaller(this);
3229 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3230
3231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3232
3233 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3234
3235 return S_OK;
3236}
3237
3238STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3239{
3240 AutoCaller autoCaller(this);
3241 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3242
3243 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3244
3245 /* @todo deal with running state change. */
3246 HRESULT rc = checkStateDependency(MutableStateDep);
3247 if (FAILED(rc)) return rc;
3248
3249 setModified(IsModified_MachineData);
3250 mUserData.backup();
3251 mUserData->s.strFaultTolerancePassword = aPassword;
3252
3253 return S_OK;
3254}
3255
3256STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3257{
3258 CheckComArgOutPointerValid(aInterval);
3259
3260 AutoCaller autoCaller(this);
3261 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3262
3263 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3264
3265 *aInterval = mUserData->s.uFaultToleranceInterval;
3266 return S_OK;
3267}
3268
3269STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3270{
3271 AutoCaller autoCaller(this);
3272 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3273
3274 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3275
3276 /* @todo deal with running state change. */
3277 HRESULT rc = checkStateDependency(MutableStateDep);
3278 if (FAILED(rc)) return rc;
3279
3280 setModified(IsModified_MachineData);
3281 mUserData.backup();
3282 mUserData->s.uFaultToleranceInterval = aInterval;
3283 return S_OK;
3284}
3285
3286STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3287{
3288 CheckComArgOutPointerValid(aEnabled);
3289
3290 AutoCaller autoCaller(this);
3291 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3292
3293 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3294
3295 *aEnabled = mUserData->s.fRTCUseUTC;
3296
3297 return S_OK;
3298}
3299
3300STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3301{
3302 AutoCaller autoCaller(this);
3303 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3304
3305 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3306
3307 /* Only allow it to be set to true when PoweredOff or Aborted.
3308 (Clearing it is always permitted.) */
3309 if ( aEnabled
3310 && mData->mRegistered
3311 && ( !isSessionMachine()
3312 || ( mData->mMachineState != MachineState_PoweredOff
3313 && mData->mMachineState != MachineState_Teleported
3314 && mData->mMachineState != MachineState_Aborted
3315 )
3316 )
3317 )
3318 return setError(VBOX_E_INVALID_VM_STATE,
3319 tr("The machine is not powered off (state is %s)"),
3320 Global::stringifyMachineState(mData->mMachineState));
3321
3322 setModified(IsModified_MachineData);
3323 mUserData.backup();
3324 mUserData->s.fRTCUseUTC = !!aEnabled;
3325
3326 return S_OK;
3327}
3328
3329STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3330{
3331 CheckComArgOutPointerValid(aEnabled);
3332
3333 AutoCaller autoCaller(this);
3334 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3335
3336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3337
3338 *aEnabled = mHWData->mIOCacheEnabled;
3339
3340 return S_OK;
3341}
3342
3343STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3344{
3345 AutoCaller autoCaller(this);
3346 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3347
3348 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3349
3350 HRESULT rc = checkStateDependency(MutableStateDep);
3351 if (FAILED(rc)) return rc;
3352
3353 setModified(IsModified_MachineData);
3354 mHWData.backup();
3355 mHWData->mIOCacheEnabled = aEnabled;
3356
3357 return S_OK;
3358}
3359
3360STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3361{
3362 CheckComArgOutPointerValid(aIOCacheSize);
3363
3364 AutoCaller autoCaller(this);
3365 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3366
3367 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3368
3369 *aIOCacheSize = mHWData->mIOCacheSize;
3370
3371 return S_OK;
3372}
3373
3374STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3375{
3376 AutoCaller autoCaller(this);
3377 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3378
3379 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3380
3381 HRESULT rc = checkStateDependency(MutableStateDep);
3382 if (FAILED(rc)) return rc;
3383
3384 setModified(IsModified_MachineData);
3385 mHWData.backup();
3386 mHWData->mIOCacheSize = aIOCacheSize;
3387
3388 return S_OK;
3389}
3390
3391
3392/**
3393 * @note Locks objects!
3394 */
3395STDMETHODIMP Machine::LockMachine(ISession *aSession,
3396 LockType_T lockType)
3397{
3398 CheckComArgNotNull(aSession);
3399
3400 AutoCaller autoCaller(this);
3401 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3402
3403 /* check the session state */
3404 SessionState_T state;
3405 HRESULT rc = aSession->COMGETTER(State)(&state);
3406 if (FAILED(rc)) return rc;
3407
3408 if (state != SessionState_Unlocked)
3409 return setError(VBOX_E_INVALID_OBJECT_STATE,
3410 tr("The given session is busy"));
3411
3412 // get the client's IInternalSessionControl interface
3413 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3414 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3415 E_INVALIDARG);
3416
3417 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3418
3419 if (!mData->mRegistered)
3420 return setError(E_UNEXPECTED,
3421 tr("The machine '%s' is not registered"),
3422 mUserData->s.strName.c_str());
3423
3424 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3425
3426 SessionState_T oldState = mData->mSession.mState;
3427 /* Hack: in case the session is closing and there is a progress object
3428 * which allows waiting for the session to be closed, take the opportunity
3429 * and do a limited wait (max. 1 second). This helps a lot when the system
3430 * is busy and thus session closing can take a little while. */
3431 if ( mData->mSession.mState == SessionState_Unlocking
3432 && mData->mSession.mProgress)
3433 {
3434 alock.release();
3435 mData->mSession.mProgress->WaitForCompletion(1000);
3436 alock.acquire();
3437 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3438 }
3439
3440 // try again now
3441 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3442 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3443 )
3444 {
3445 // OK, share the session... we are now dealing with three processes:
3446 // 1) VBoxSVC (where this code runs);
3447 // 2) process C: the caller's client process (who wants a shared session);
3448 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3449
3450 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3451 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3452 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3453 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3454 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3455
3456 /*
3457 * Release the lock before calling the client process. It's safe here
3458 * since the only thing to do after we get the lock again is to add
3459 * the remote control to the list (which doesn't directly influence
3460 * anything).
3461 */
3462 alock.release();
3463
3464 // get the console of the session holding the write lock (this is a remote call)
3465 ComPtr<IConsole> pConsoleW;
3466 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3467 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3468 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3469 if (FAILED(rc))
3470 // the failure may occur w/o any error info (from RPC), so provide one
3471 return setError(VBOX_E_VM_ERROR,
3472 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3473
3474 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3475
3476 // share the session machine and W's console with the caller's session
3477 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3478 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3479 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3480
3481 if (FAILED(rc))
3482 // the failure may occur w/o any error info (from RPC), so provide one
3483 return setError(VBOX_E_VM_ERROR,
3484 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3485 alock.acquire();
3486
3487 // need to revalidate the state after acquiring the lock again
3488 if (mData->mSession.mState != SessionState_Locked)
3489 {
3490 pSessionControl->Uninitialize();
3491 return setError(VBOX_E_INVALID_SESSION_STATE,
3492 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3493 mUserData->s.strName.c_str());
3494 }
3495
3496 // add the caller's session to the list
3497 mData->mSession.mRemoteControls.push_back(pSessionControl);
3498 }
3499 else if ( mData->mSession.mState == SessionState_Locked
3500 || mData->mSession.mState == SessionState_Unlocking
3501 )
3502 {
3503 // sharing not permitted, or machine still unlocking:
3504 return setError(VBOX_E_INVALID_OBJECT_STATE,
3505 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3506 mUserData->s.strName.c_str());
3507 }
3508 else
3509 {
3510 // machine is not locked: then write-lock the machine (create the session machine)
3511
3512 // must not be busy
3513 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3514
3515 // get the caller's session PID
3516 RTPROCESS pid = NIL_RTPROCESS;
3517 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3518 pSessionControl->GetPID((ULONG*)&pid);
3519 Assert(pid != NIL_RTPROCESS);
3520
3521 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3522
3523 if (fLaunchingVMProcess)
3524 {
3525 // this machine is awaiting for a spawning session to be opened:
3526 // then the calling process must be the one that got started by
3527 // LaunchVMProcess()
3528
3529 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3530 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3531
3532 if (mData->mSession.mPID != pid)
3533 return setError(E_ACCESSDENIED,
3534 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3535 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3536 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3537 }
3538
3539 // create the mutable SessionMachine from the current machine
3540 ComObjPtr<SessionMachine> sessionMachine;
3541 sessionMachine.createObject();
3542 rc = sessionMachine->init(this);
3543 AssertComRC(rc);
3544
3545 /* NOTE: doing return from this function after this point but
3546 * before the end is forbidden since it may call SessionMachine::uninit()
3547 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3548 * lock while still holding the Machine lock in alock so that a deadlock
3549 * is possible due to the wrong lock order. */
3550
3551 if (SUCCEEDED(rc))
3552 {
3553 /*
3554 * Set the session state to Spawning to protect against subsequent
3555 * attempts to open a session and to unregister the machine after
3556 * we release the lock.
3557 */
3558 SessionState_T origState = mData->mSession.mState;
3559 mData->mSession.mState = SessionState_Spawning;
3560
3561 /*
3562 * Release the lock before calling the client process -- it will call
3563 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3564 * because the state is Spawning, so that LaunchVMProcess() and
3565 * LockMachine() calls will fail. This method, called before we
3566 * acquire the lock again, will fail because of the wrong PID.
3567 *
3568 * Note that mData->mSession.mRemoteControls accessed outside
3569 * the lock may not be modified when state is Spawning, so it's safe.
3570 */
3571 alock.release();
3572
3573 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3574 rc = pSessionControl->AssignMachine(sessionMachine, lockType);
3575 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3576
3577 /* The failure may occur w/o any error info (from RPC), so provide one */
3578 if (FAILED(rc))
3579 setError(VBOX_E_VM_ERROR,
3580 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3581
3582 if ( SUCCEEDED(rc)
3583 && fLaunchingVMProcess
3584 )
3585 {
3586 /* complete the remote session initialization */
3587
3588 /* get the console from the direct session */
3589 ComPtr<IConsole> console;
3590 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3591 ComAssertComRC(rc);
3592
3593 if (SUCCEEDED(rc) && !console)
3594 {
3595 ComAssert(!!console);
3596 rc = E_FAIL;
3597 }
3598
3599 /* assign machine & console to the remote session */
3600 if (SUCCEEDED(rc))
3601 {
3602 /*
3603 * after LaunchVMProcess(), the first and the only
3604 * entry in remoteControls is that remote session
3605 */
3606 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3607 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3608 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3609
3610 /* The failure may occur w/o any error info (from RPC), so provide one */
3611 if (FAILED(rc))
3612 setError(VBOX_E_VM_ERROR,
3613 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3614 }
3615
3616 if (FAILED(rc))
3617 pSessionControl->Uninitialize();
3618 }
3619
3620 /* acquire the lock again */
3621 alock.acquire();
3622
3623 /* Restore the session state */
3624 mData->mSession.mState = origState;
3625 }
3626
3627 // finalize spawning anyway (this is why we don't return on errors above)
3628 if (fLaunchingVMProcess)
3629 {
3630 /* Note that the progress object is finalized later */
3631 /** @todo Consider checking mData->mSession.mProgress for cancellation
3632 * around here. */
3633
3634 /* We don't reset mSession.mPID here because it is necessary for
3635 * SessionMachine::uninit() to reap the child process later. */
3636
3637 if (FAILED(rc))
3638 {
3639 /* Close the remote session, remove the remote control from the list
3640 * and reset session state to Closed (@note keep the code in sync
3641 * with the relevant part in openSession()). */
3642
3643 Assert(mData->mSession.mRemoteControls.size() == 1);
3644 if (mData->mSession.mRemoteControls.size() == 1)
3645 {
3646 ErrorInfoKeeper eik;
3647 mData->mSession.mRemoteControls.front()->Uninitialize();
3648 }
3649
3650 mData->mSession.mRemoteControls.clear();
3651 mData->mSession.mState = SessionState_Unlocked;
3652 }
3653 }
3654 else
3655 {
3656 /* memorize PID of the directly opened session */
3657 if (SUCCEEDED(rc))
3658 mData->mSession.mPID = pid;
3659 }
3660
3661 if (SUCCEEDED(rc))
3662 {
3663 /* memorize the direct session control and cache IUnknown for it */
3664 mData->mSession.mDirectControl = pSessionControl;
3665 mData->mSession.mState = SessionState_Locked;
3666 /* associate the SessionMachine with this Machine */
3667 mData->mSession.mMachine = sessionMachine;
3668
3669 /* request an IUnknown pointer early from the remote party for later
3670 * identity checks (it will be internally cached within mDirectControl
3671 * at least on XPCOM) */
3672 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3673 NOREF(unk);
3674 }
3675
3676 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3677 * would break the lock order */
3678 alock.release();
3679
3680 /* uninitialize the created session machine on failure */
3681 if (FAILED(rc))
3682 sessionMachine->uninit();
3683
3684 }
3685
3686 if (SUCCEEDED(rc))
3687 {
3688 /*
3689 * tell the client watcher thread to update the set of
3690 * machines that have open sessions
3691 */
3692 mParent->updateClientWatcher();
3693
3694 if (oldState != SessionState_Locked)
3695 /* fire an event */
3696 mParent->onSessionStateChange(getId(), SessionState_Locked);
3697 }
3698
3699 return rc;
3700}
3701
3702/**
3703 * @note Locks objects!
3704 */
3705STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3706 IN_BSTR aFrontend,
3707 IN_BSTR aEnvironment,
3708 IProgress **aProgress)
3709{
3710 CheckComArgStr(aFrontend);
3711 Utf8Str strFrontend(aFrontend);
3712 Utf8Str strEnvironment(aEnvironment);
3713 /* "emergencystop" doesn't need the session, so skip the checks/interface
3714 * retrieval. This code doesn't quite fit in here, but introducing a
3715 * special API method would be even more effort, and would require explicit
3716 * support by every API client. It's better to hide the feature a bit. */
3717 if (strFrontend != "emergencystop")
3718 CheckComArgNotNull(aSession);
3719 CheckComArgOutPointerValid(aProgress);
3720
3721 AutoCaller autoCaller(this);
3722 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3723
3724 HRESULT rc = S_OK;
3725 if (strFrontend.isEmpty())
3726 {
3727 Bstr bstrFrontend;
3728 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3729 if (FAILED(rc))
3730 return rc;
3731 strFrontend = bstrFrontend;
3732 if (strFrontend.isEmpty())
3733 {
3734 ComPtr<ISystemProperties> systemProperties;
3735 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3736 if (FAILED(rc))
3737 return rc;
3738 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3739 if (FAILED(rc))
3740 return rc;
3741 strFrontend = bstrFrontend;
3742 }
3743 /* paranoia - emergencystop is not a valid default */
3744 if (strFrontend == "emergencystop")
3745 strFrontend = Utf8Str::Empty;
3746 }
3747
3748 if (strFrontend != "emergencystop")
3749 {
3750 /* check the session state */
3751 SessionState_T state;
3752 rc = aSession->COMGETTER(State)(&state);
3753 if (FAILED(rc))
3754 return rc;
3755
3756 if (state != SessionState_Unlocked)
3757 return setError(VBOX_E_INVALID_OBJECT_STATE,
3758 tr("The given session is busy"));
3759
3760 /* get the IInternalSessionControl interface */
3761 ComPtr<IInternalSessionControl> control(aSession);
3762 ComAssertMsgRet(!control.isNull(),
3763 ("No IInternalSessionControl interface"),
3764 E_INVALIDARG);
3765
3766 /* get the teleporter enable state for the progress object init. */
3767 BOOL fTeleporterEnabled;
3768 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3769 if (FAILED(rc))
3770 return rc;
3771
3772 /* create a progress object */
3773 ComObjPtr<ProgressProxy> progress;
3774 progress.createObject();
3775 rc = progress->init(mParent,
3776 static_cast<IMachine*>(this),
3777 Bstr(tr("Starting VM")).raw(),
3778 TRUE /* aCancelable */,
3779 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3780 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3781 2 /* uFirstOperationWeight */,
3782 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3783
3784 if (SUCCEEDED(rc))
3785 {
3786 rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
3787 if (SUCCEEDED(rc))
3788 {
3789 progress.queryInterfaceTo(aProgress);
3790
3791 /* signal the client watcher thread */
3792 mParent->updateClientWatcher();
3793
3794 /* fire an event */
3795 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3796 }
3797 }
3798 }
3799 else
3800 {
3801 /* no progress object - either instant success or failure */
3802 *aProgress = NULL;
3803
3804 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3805
3806 if (mData->mSession.mState != SessionState_Locked)
3807 return setError(VBOX_E_INVALID_OBJECT_STATE,
3808 tr("The machine '%s' is not locked by a session"),
3809 mUserData->s.strName.c_str());
3810
3811 /* must have a VM process associated - do not kill normal API clients
3812 * with an open session */
3813 if (!Global::IsOnline(mData->mMachineState))
3814 return setError(VBOX_E_INVALID_OBJECT_STATE,
3815 tr("The machine '%s' does not have a VM process"),
3816 mUserData->s.strName.c_str());
3817
3818 /* forcibly terminate the VM process */
3819 if (mData->mSession.mPID != NIL_RTPROCESS)
3820 RTProcTerminate(mData->mSession.mPID);
3821
3822 /* signal the client watcher thread, as most likely the client has
3823 * been terminated */
3824 mParent->updateClientWatcher();
3825 }
3826
3827 return rc;
3828}
3829
3830STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3831{
3832 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3833 return setError(E_INVALIDARG,
3834 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3835 aPosition, SchemaDefs::MaxBootPosition);
3836
3837 if (aDevice == DeviceType_USB)
3838 return setError(E_NOTIMPL,
3839 tr("Booting from USB device is currently not supported"));
3840
3841 AutoCaller autoCaller(this);
3842 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3843
3844 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3845
3846 HRESULT rc = checkStateDependency(MutableStateDep);
3847 if (FAILED(rc)) return rc;
3848
3849 setModified(IsModified_MachineData);
3850 mHWData.backup();
3851 mHWData->mBootOrder[aPosition - 1] = aDevice;
3852
3853 return S_OK;
3854}
3855
3856STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3857{
3858 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3859 return setError(E_INVALIDARG,
3860 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3861 aPosition, SchemaDefs::MaxBootPosition);
3862
3863 AutoCaller autoCaller(this);
3864 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3865
3866 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3867
3868 *aDevice = mHWData->mBootOrder[aPosition - 1];
3869
3870 return S_OK;
3871}
3872
3873STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
3874 LONG aControllerPort,
3875 LONG aDevice,
3876 DeviceType_T aType,
3877 IMedium *aMedium)
3878{
3879 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3880 aControllerName, aControllerPort, aDevice, aType, aMedium));
3881
3882 CheckComArgStrNotEmptyOrNull(aControllerName);
3883
3884 AutoCaller autoCaller(this);
3885 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3886
3887 // request the host lock first, since might be calling Host methods for getting host drives;
3888 // next, protect the media tree all the while we're in here, as well as our member variables
3889 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
3890 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3891
3892 HRESULT rc = checkStateDependency(MutableStateDep);
3893 if (FAILED(rc)) return rc;
3894
3895 /// @todo NEWMEDIA implicit machine registration
3896 if (!mData->mRegistered)
3897 return setError(VBOX_E_INVALID_OBJECT_STATE,
3898 tr("Cannot attach storage devices to an unregistered machine"));
3899
3900 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3901
3902 /* Check for an existing controller. */
3903 ComObjPtr<StorageController> ctl;
3904 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
3905 if (FAILED(rc)) return rc;
3906
3907 StorageControllerType_T ctrlType;
3908 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3909 if (FAILED(rc))
3910 return setError(E_FAIL,
3911 tr("Could not get type of controller '%ls'"),
3912 aControllerName);
3913
3914 bool fSilent = false;
3915 Utf8Str strReconfig;
3916
3917 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3918 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3919 if (FAILED(rc))
3920 return rc;
3921 if ( mData->mMachineState == MachineState_Paused
3922 && strReconfig == "1")
3923 fSilent = true;
3924
3925 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3926 bool fHotplug = false;
3927 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3928 fHotplug = true;
3929
3930 if (fHotplug && !isControllerHotplugCapable(ctrlType))
3931 return setError(VBOX_E_INVALID_VM_STATE,
3932 tr("Controller '%ls' does not support hotplugging"),
3933 aControllerName);
3934
3935 // check that the port and device are not out of range
3936 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
3937 if (FAILED(rc)) return rc;
3938
3939 /* check if the device slot is already busy */
3940 MediumAttachment *pAttachTemp;
3941 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
3942 aControllerName,
3943 aControllerPort,
3944 aDevice)))
3945 {
3946 Medium *pMedium = pAttachTemp->getMedium();
3947 if (pMedium)
3948 {
3949 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3950 return setError(VBOX_E_OBJECT_IN_USE,
3951 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3952 pMedium->getLocationFull().c_str(),
3953 aControllerPort,
3954 aDevice,
3955 aControllerName);
3956 }
3957 else
3958 return setError(VBOX_E_OBJECT_IN_USE,
3959 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3960 aControllerPort, aDevice, aControllerName);
3961 }
3962
3963 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
3964 if (aMedium && medium.isNull())
3965 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3966
3967 AutoCaller mediumCaller(medium);
3968 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3969
3970 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3971
3972 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
3973 && !medium.isNull()
3974 )
3975 return setError(VBOX_E_OBJECT_IN_USE,
3976 tr("Medium '%s' is already attached to this virtual machine"),
3977 medium->getLocationFull().c_str());
3978
3979 if (!medium.isNull())
3980 {
3981 MediumType_T mtype = medium->getType();
3982 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3983 // For DVDs it's not written to the config file, so needs no global config
3984 // version bump. For floppies it's a new attribute "type", which is ignored
3985 // by older VirtualBox version, so needs no global config version bump either.
3986 // For hard disks this type is not accepted.
3987 if (mtype == MediumType_MultiAttach)
3988 {
3989 // This type is new with VirtualBox 4.0 and therefore requires settings
3990 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3991 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3992 // two reasons: The medium type is a property of the media registry tree, which
3993 // can reside in the global config file (for pre-4.0 media); we would therefore
3994 // possibly need to bump the global config version. We don't want to do that though
3995 // because that might make downgrading to pre-4.0 impossible.
3996 // As a result, we can only use these two new types if the medium is NOT in the
3997 // global registry:
3998 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
3999 if ( medium->isInRegistry(uuidGlobalRegistry)
4000 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
4001 )
4002 return setError(VBOX_E_INVALID_OBJECT_STATE,
4003 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
4004 "to machines that were created with VirtualBox 4.0 or later"),
4005 medium->getLocationFull().c_str());
4006 }
4007 }
4008
4009 bool fIndirect = false;
4010 if (!medium.isNull())
4011 fIndirect = medium->isReadOnly();
4012 bool associate = true;
4013
4014 do
4015 {
4016 if ( aType == DeviceType_HardDisk
4017 && mMediaData.isBackedUp())
4018 {
4019 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4020
4021 /* check if the medium was attached to the VM before we started
4022 * changing attachments in which case the attachment just needs to
4023 * be restored */
4024 if ((pAttachTemp = findAttachment(oldAtts, medium)))
4025 {
4026 AssertReturn(!fIndirect, E_FAIL);
4027
4028 /* see if it's the same bus/channel/device */
4029 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
4030 {
4031 /* the simplest case: restore the whole attachment
4032 * and return, nothing else to do */
4033 mMediaData->mAttachments.push_back(pAttachTemp);
4034
4035 /* Reattach the medium to the VM. */
4036 if (fHotplug || fSilent)
4037 {
4038 mediumLock.release();
4039 treeLock.release();
4040 alock.release();
4041
4042 MediumLockList *pMediumLockList(new MediumLockList());
4043
4044 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4045 true /* fMediumLockWrite */,
4046 NULL,
4047 *pMediumLockList);
4048 alock.acquire();
4049 if (FAILED(rc))
4050 delete pMediumLockList;
4051 else
4052 {
4053 mData->mSession.mLockedMedia.Unlock();
4054 alock.release();
4055 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4056 mData->mSession.mLockedMedia.Lock();
4057 alock.acquire();
4058 }
4059 alock.release();
4060
4061 if (SUCCEEDED(rc))
4062 {
4063 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4064 /* Remove lock list in case of error. */
4065 if (FAILED(rc))
4066 {
4067 mData->mSession.mLockedMedia.Unlock();
4068 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4069 mData->mSession.mLockedMedia.Lock();
4070 }
4071 }
4072 }
4073
4074 return S_OK;
4075 }
4076
4077 /* bus/channel/device differ; we need a new attachment object,
4078 * but don't try to associate it again */
4079 associate = false;
4080 break;
4081 }
4082 }
4083
4084 /* go further only if the attachment is to be indirect */
4085 if (!fIndirect)
4086 break;
4087
4088 /* perform the so called smart attachment logic for indirect
4089 * attachments. Note that smart attachment is only applicable to base
4090 * hard disks. */
4091
4092 if (medium->getParent().isNull())
4093 {
4094 /* first, investigate the backup copy of the current hard disk
4095 * attachments to make it possible to re-attach existing diffs to
4096 * another device slot w/o losing their contents */
4097 if (mMediaData.isBackedUp())
4098 {
4099 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4100
4101 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4102 uint32_t foundLevel = 0;
4103
4104 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
4105 it != oldAtts.end();
4106 ++it)
4107 {
4108 uint32_t level = 0;
4109 MediumAttachment *pAttach = *it;
4110 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4111 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4112 if (pMedium.isNull())
4113 continue;
4114
4115 if (pMedium->getBase(&level) == medium)
4116 {
4117 /* skip the hard disk if its currently attached (we
4118 * cannot attach the same hard disk twice) */
4119 if (findAttachment(mMediaData->mAttachments,
4120 pMedium))
4121 continue;
4122
4123 /* matched device, channel and bus (i.e. attached to the
4124 * same place) will win and immediately stop the search;
4125 * otherwise the attachment that has the youngest
4126 * descendant of medium will be used
4127 */
4128 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
4129 {
4130 /* the simplest case: restore the whole attachment
4131 * and return, nothing else to do */
4132 mMediaData->mAttachments.push_back(*it);
4133
4134 /* Reattach the medium to the VM. */
4135 if (fHotplug || fSilent)
4136 {
4137 mediumLock.release();
4138 treeLock.release();
4139 alock.release();
4140
4141 MediumLockList *pMediumLockList(new MediumLockList());
4142
4143 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4144 true /* fMediumLockWrite */,
4145 NULL,
4146 *pMediumLockList);
4147 alock.acquire();
4148 if (FAILED(rc))
4149 delete pMediumLockList;
4150 else
4151 {
4152 mData->mSession.mLockedMedia.Unlock();
4153 alock.release();
4154 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4155 mData->mSession.mLockedMedia.Lock();
4156 alock.acquire();
4157 }
4158 alock.release();
4159
4160 if (SUCCEEDED(rc))
4161 {
4162 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4163 /* Remove lock list in case of error. */
4164 if (FAILED(rc))
4165 {
4166 mData->mSession.mLockedMedia.Unlock();
4167 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4168 mData->mSession.mLockedMedia.Lock();
4169 }
4170 }
4171 }
4172
4173 return S_OK;
4174 }
4175 else if ( foundIt == oldAtts.end()
4176 || level > foundLevel /* prefer younger */
4177 )
4178 {
4179 foundIt = it;
4180 foundLevel = level;
4181 }
4182 }
4183 }
4184
4185 if (foundIt != oldAtts.end())
4186 {
4187 /* use the previously attached hard disk */
4188 medium = (*foundIt)->getMedium();
4189 mediumCaller.attach(medium);
4190 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4191 mediumLock.attach(medium);
4192 /* not implicit, doesn't require association with this VM */
4193 fIndirect = false;
4194 associate = false;
4195 /* go right to the MediumAttachment creation */
4196 break;
4197 }
4198 }
4199
4200 /* must give up the medium lock and medium tree lock as below we
4201 * go over snapshots, which needs a lock with higher lock order. */
4202 mediumLock.release();
4203 treeLock.release();
4204
4205 /* then, search through snapshots for the best diff in the given
4206 * hard disk's chain to base the new diff on */
4207
4208 ComObjPtr<Medium> base;
4209 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4210 while (snap)
4211 {
4212 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4213
4214 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
4215
4216 MediumAttachment *pAttachFound = NULL;
4217 uint32_t foundLevel = 0;
4218
4219 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
4220 it != snapAtts.end();
4221 ++it)
4222 {
4223 MediumAttachment *pAttach = *it;
4224 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4225 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4226 if (pMedium.isNull())
4227 continue;
4228
4229 uint32_t level = 0;
4230 if (pMedium->getBase(&level) == medium)
4231 {
4232 /* matched device, channel and bus (i.e. attached to the
4233 * same place) will win and immediately stop the search;
4234 * otherwise the attachment that has the youngest
4235 * descendant of medium will be used
4236 */
4237 if ( pAttach->getDevice() == aDevice
4238 && pAttach->getPort() == aControllerPort
4239 && pAttach->getControllerName() == aControllerName
4240 )
4241 {
4242 pAttachFound = pAttach;
4243 break;
4244 }
4245 else if ( !pAttachFound
4246 || level > foundLevel /* prefer younger */
4247 )
4248 {
4249 pAttachFound = pAttach;
4250 foundLevel = level;
4251 }
4252 }
4253 }
4254
4255 if (pAttachFound)
4256 {
4257 base = pAttachFound->getMedium();
4258 break;
4259 }
4260
4261 snap = snap->getParent();
4262 }
4263
4264 /* re-lock medium tree and the medium, as we need it below */
4265 treeLock.acquire();
4266 mediumLock.acquire();
4267
4268 /* found a suitable diff, use it as a base */
4269 if (!base.isNull())
4270 {
4271 medium = base;
4272 mediumCaller.attach(medium);
4273 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4274 mediumLock.attach(medium);
4275 }
4276 }
4277
4278 Utf8Str strFullSnapshotFolder;
4279 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4280
4281 ComObjPtr<Medium> diff;
4282 diff.createObject();
4283 // store this diff in the same registry as the parent
4284 Guid uuidRegistryParent;
4285 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4286 {
4287 // parent image has no registry: this can happen if we're attaching a new immutable
4288 // image that has not yet been attached (medium then points to the base and we're
4289 // creating the diff image for the immutable, and the parent is not yet registered);
4290 // put the parent in the machine registry then
4291 mediumLock.release();
4292 treeLock.release();
4293 alock.release();
4294 addMediumToRegistry(medium);
4295 alock.acquire();
4296 treeLock.acquire();
4297 mediumLock.acquire();
4298 medium->getFirstRegistryMachineId(uuidRegistryParent);
4299 }
4300 rc = diff->init(mParent,
4301 medium->getPreferredDiffFormat(),
4302 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4303 uuidRegistryParent);
4304 if (FAILED(rc)) return rc;
4305
4306 /* Apply the normal locking logic to the entire chain. */
4307 MediumLockList *pMediumLockList(new MediumLockList());
4308 mediumLock.release();
4309 treeLock.release();
4310 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4311 true /* fMediumLockWrite */,
4312 medium,
4313 *pMediumLockList);
4314 treeLock.acquire();
4315 mediumLock.acquire();
4316 if (SUCCEEDED(rc))
4317 {
4318 mediumLock.release();
4319 treeLock.release();
4320 rc = pMediumLockList->Lock();
4321 treeLock.acquire();
4322 mediumLock.acquire();
4323 if (FAILED(rc))
4324 setError(rc,
4325 tr("Could not lock medium when creating diff '%s'"),
4326 diff->getLocationFull().c_str());
4327 else
4328 {
4329 /* will release the lock before the potentially lengthy
4330 * operation, so protect with the special state */
4331 MachineState_T oldState = mData->mMachineState;
4332 setMachineState(MachineState_SettingUp);
4333
4334 mediumLock.release();
4335 treeLock.release();
4336 alock.release();
4337
4338 rc = medium->createDiffStorage(diff,
4339 MediumVariant_Standard,
4340 pMediumLockList,
4341 NULL /* aProgress */,
4342 true /* aWait */);
4343
4344 alock.acquire();
4345 treeLock.acquire();
4346 mediumLock.acquire();
4347
4348 setMachineState(oldState);
4349 }
4350 }
4351
4352 /* Unlock the media and free the associated memory. */
4353 delete pMediumLockList;
4354
4355 if (FAILED(rc)) return rc;
4356
4357 /* use the created diff for the actual attachment */
4358 medium = diff;
4359 mediumCaller.attach(medium);
4360 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4361 mediumLock.attach(medium);
4362 }
4363 while (0);
4364
4365 ComObjPtr<MediumAttachment> attachment;
4366 attachment.createObject();
4367 rc = attachment->init(this,
4368 medium,
4369 aControllerName,
4370 aControllerPort,
4371 aDevice,
4372 aType,
4373 fIndirect,
4374 false /* fPassthrough */,
4375 false /* fTempEject */,
4376 false /* fNonRotational */,
4377 false /* fDiscard */,
4378 Utf8Str::Empty);
4379 if (FAILED(rc)) return rc;
4380
4381 if (associate && !medium.isNull())
4382 {
4383 // as the last step, associate the medium to the VM
4384 rc = medium->addBackReference(mData->mUuid);
4385 // here we can fail because of Deleting, or being in process of creating a Diff
4386 if (FAILED(rc)) return rc;
4387
4388 mediumLock.release();
4389 treeLock.release();
4390 alock.release();
4391 addMediumToRegistry(medium);
4392 alock.acquire();
4393 treeLock.acquire();
4394 mediumLock.acquire();
4395 }
4396
4397 /* success: finally remember the attachment */
4398 setModified(IsModified_Storage);
4399 mMediaData.backup();
4400 mMediaData->mAttachments.push_back(attachment);
4401
4402 mediumLock.release();
4403 treeLock.release();
4404 alock.release();
4405
4406 if (fHotplug || fSilent)
4407 {
4408 MediumLockList *pMediumLockList(new MediumLockList());
4409
4410 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4411 true /* fMediumLockWrite */,
4412 NULL,
4413 *pMediumLockList);
4414 alock.acquire();
4415 if (FAILED(rc))
4416 delete pMediumLockList;
4417 else
4418 {
4419 mData->mSession.mLockedMedia.Unlock();
4420 alock.release();
4421 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4422 mData->mSession.mLockedMedia.Lock();
4423 alock.acquire();
4424 }
4425 alock.release();
4426
4427 if (SUCCEEDED(rc))
4428 {
4429 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4430 /* Remove lock list in case of error. */
4431 if (FAILED(rc))
4432 {
4433 mData->mSession.mLockedMedia.Unlock();
4434 mData->mSession.mLockedMedia.Remove(attachment);
4435 mData->mSession.mLockedMedia.Lock();
4436 }
4437 }
4438 }
4439
4440 mParent->saveModifiedRegistries();
4441
4442 return rc;
4443}
4444
4445STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4446 LONG aDevice)
4447{
4448 CheckComArgStrNotEmptyOrNull(aControllerName);
4449
4450 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4451 aControllerName, aControllerPort, aDevice));
4452
4453 AutoCaller autoCaller(this);
4454 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4455
4456 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4457
4458 HRESULT rc = checkStateDependency(MutableStateDep);
4459 if (FAILED(rc)) return rc;
4460
4461 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4462
4463 /* Check for an existing controller. */
4464 ComObjPtr<StorageController> ctl;
4465 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4466 if (FAILED(rc)) return rc;
4467
4468 StorageControllerType_T ctrlType;
4469 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4470 if (FAILED(rc))
4471 return setError(E_FAIL,
4472 tr("Could not get type of controller '%ls'"),
4473 aControllerName);
4474
4475 bool fSilent = false;
4476 Utf8Str strReconfig;
4477
4478 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4479 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4480 if (FAILED(rc))
4481 return rc;
4482 if ( mData->mMachineState == MachineState_Paused
4483 && strReconfig == "1")
4484 fSilent = true;
4485
4486 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4487 bool fHotplug = false;
4488 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4489 fHotplug = true;
4490
4491 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4492 return setError(VBOX_E_INVALID_VM_STATE,
4493 tr("Controller '%ls' does not support hotplugging"),
4494 aControllerName);
4495
4496 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4497 aControllerName,
4498 aControllerPort,
4499 aDevice);
4500 if (!pAttach)
4501 return setError(VBOX_E_OBJECT_NOT_FOUND,
4502 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4503 aDevice, aControllerPort, aControllerName);
4504
4505 /*
4506 * The VM has to detach the device before we delete any implicit diffs.
4507 * If this fails we can roll back without loosing data.
4508 */
4509 if (fHotplug || fSilent)
4510 {
4511 alock.release();
4512 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4513 alock.acquire();
4514 }
4515 if (FAILED(rc)) return rc;
4516
4517 /* If we are here everything went well and we can delete the implicit now. */
4518 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4519
4520 alock.release();
4521
4522 mParent->saveModifiedRegistries();
4523
4524 return rc;
4525}
4526
4527STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4528 LONG aDevice, BOOL aPassthrough)
4529{
4530 CheckComArgStrNotEmptyOrNull(aControllerName);
4531
4532 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4533 aControllerName, aControllerPort, aDevice, aPassthrough));
4534
4535 AutoCaller autoCaller(this);
4536 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4537
4538 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4539
4540 HRESULT rc = checkStateDependency(MutableStateDep);
4541 if (FAILED(rc)) return rc;
4542
4543 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4544
4545 if (Global::IsOnlineOrTransient(mData->mMachineState))
4546 return setError(VBOX_E_INVALID_VM_STATE,
4547 tr("Invalid machine state: %s"),
4548 Global::stringifyMachineState(mData->mMachineState));
4549
4550 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4551 aControllerName,
4552 aControllerPort,
4553 aDevice);
4554 if (!pAttach)
4555 return setError(VBOX_E_OBJECT_NOT_FOUND,
4556 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4557 aDevice, aControllerPort, aControllerName);
4558
4559
4560 setModified(IsModified_Storage);
4561 mMediaData.backup();
4562
4563 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4564
4565 if (pAttach->getType() != DeviceType_DVD)
4566 return setError(E_INVALIDARG,
4567 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4568 aDevice, aControllerPort, aControllerName);
4569 pAttach->updatePassthrough(!!aPassthrough);
4570
4571 return S_OK;
4572}
4573
4574STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4575 LONG aDevice, BOOL aTemporaryEject)
4576{
4577 CheckComArgStrNotEmptyOrNull(aControllerName);
4578
4579 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4580 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4581
4582 AutoCaller autoCaller(this);
4583 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4584
4585 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4586
4587 HRESULT rc = checkStateDependency(MutableStateDep);
4588 if (FAILED(rc)) return rc;
4589
4590 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4591 aControllerName,
4592 aControllerPort,
4593 aDevice);
4594 if (!pAttach)
4595 return setError(VBOX_E_OBJECT_NOT_FOUND,
4596 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4597 aDevice, aControllerPort, aControllerName);
4598
4599
4600 setModified(IsModified_Storage);
4601 mMediaData.backup();
4602
4603 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4604
4605 if (pAttach->getType() != DeviceType_DVD)
4606 return setError(E_INVALIDARG,
4607 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4608 aDevice, aControllerPort, aControllerName);
4609 pAttach->updateTempEject(!!aTemporaryEject);
4610
4611 return S_OK;
4612}
4613
4614STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4615 LONG aDevice, BOOL aNonRotational)
4616{
4617 CheckComArgStrNotEmptyOrNull(aControllerName);
4618
4619 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4620 aControllerName, aControllerPort, aDevice, aNonRotational));
4621
4622 AutoCaller autoCaller(this);
4623 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4624
4625 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4626
4627 HRESULT rc = checkStateDependency(MutableStateDep);
4628 if (FAILED(rc)) return rc;
4629
4630 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4631
4632 if (Global::IsOnlineOrTransient(mData->mMachineState))
4633 return setError(VBOX_E_INVALID_VM_STATE,
4634 tr("Invalid machine state: %s"),
4635 Global::stringifyMachineState(mData->mMachineState));
4636
4637 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4638 aControllerName,
4639 aControllerPort,
4640 aDevice);
4641 if (!pAttach)
4642 return setError(VBOX_E_OBJECT_NOT_FOUND,
4643 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4644 aDevice, aControllerPort, aControllerName);
4645
4646
4647 setModified(IsModified_Storage);
4648 mMediaData.backup();
4649
4650 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4651
4652 if (pAttach->getType() != DeviceType_HardDisk)
4653 return setError(E_INVALIDARG,
4654 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"),
4655 aDevice, aControllerPort, aControllerName);
4656 pAttach->updateNonRotational(!!aNonRotational);
4657
4658 return S_OK;
4659}
4660
4661STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4662 LONG aDevice, BOOL aDiscard)
4663{
4664 CheckComArgStrNotEmptyOrNull(aControllerName);
4665
4666 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4667 aControllerName, aControllerPort, aDevice, aDiscard));
4668
4669 AutoCaller autoCaller(this);
4670 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4671
4672 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4673
4674 HRESULT rc = checkStateDependency(MutableStateDep);
4675 if (FAILED(rc)) return rc;
4676
4677 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4678
4679 if (Global::IsOnlineOrTransient(mData->mMachineState))
4680 return setError(VBOX_E_INVALID_VM_STATE,
4681 tr("Invalid machine state: %s"),
4682 Global::stringifyMachineState(mData->mMachineState));
4683
4684 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4685 aControllerName,
4686 aControllerPort,
4687 aDevice);
4688 if (!pAttach)
4689 return setError(VBOX_E_OBJECT_NOT_FOUND,
4690 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4691 aDevice, aControllerPort, aControllerName);
4692
4693
4694 setModified(IsModified_Storage);
4695 mMediaData.backup();
4696
4697 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4698
4699 if (pAttach->getType() != DeviceType_HardDisk)
4700 return setError(E_INVALIDARG,
4701 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"),
4702 aDevice, aControllerPort, aControllerName);
4703 pAttach->updateDiscard(!!aDiscard);
4704
4705 return S_OK;
4706}
4707
4708STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4709 LONG aDevice)
4710{
4711 int rc = S_OK;
4712 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4713 aControllerName, aControllerPort, aDevice));
4714
4715 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4716
4717 return rc;
4718}
4719
4720STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4721 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4722{
4723 CheckComArgStrNotEmptyOrNull(aControllerName);
4724
4725 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4726 aControllerName, aControllerPort, aDevice));
4727
4728 AutoCaller autoCaller(this);
4729 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4730
4731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4732
4733 HRESULT rc = checkStateDependency(MutableStateDep);
4734 if (FAILED(rc)) return rc;
4735
4736 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4737
4738 if (Global::IsOnlineOrTransient(mData->mMachineState))
4739 return setError(VBOX_E_INVALID_VM_STATE,
4740 tr("Invalid machine state: %s"),
4741 Global::stringifyMachineState(mData->mMachineState));
4742
4743 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4744 aControllerName,
4745 aControllerPort,
4746 aDevice);
4747 if (!pAttach)
4748 return setError(VBOX_E_OBJECT_NOT_FOUND,
4749 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4750 aDevice, aControllerPort, aControllerName);
4751
4752
4753 setModified(IsModified_Storage);
4754 mMediaData.backup();
4755
4756 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4757 if (aBandwidthGroup && group.isNull())
4758 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4759
4760 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4761
4762 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4763 if (strBandwidthGroupOld.isNotEmpty())
4764 {
4765 /* Get the bandwidth group object and release it - this must not fail. */
4766 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4767 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4768 Assert(SUCCEEDED(rc));
4769
4770 pBandwidthGroupOld->release();
4771 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4772 }
4773
4774 if (!group.isNull())
4775 {
4776 group->reference();
4777 pAttach->updateBandwidthGroup(group->getName());
4778 }
4779
4780 return S_OK;
4781}
4782
4783STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4784 LONG aControllerPort,
4785 LONG aDevice,
4786 DeviceType_T aType)
4787{
4788 HRESULT rc = S_OK;
4789
4790 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4791 aControllerName, aControllerPort, aDevice, aType));
4792
4793 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4794
4795 return rc;
4796}
4797
4798
4799
4800STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4801 LONG aControllerPort,
4802 LONG aDevice,
4803 BOOL aForce)
4804{
4805 int rc = S_OK;
4806 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4807 aControllerName, aControllerPort, aForce));
4808
4809 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4810
4811 return rc;
4812}
4813
4814STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4815 LONG aControllerPort,
4816 LONG aDevice,
4817 IMedium *aMedium,
4818 BOOL aForce)
4819{
4820 int rc = S_OK;
4821 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4822 aControllerName, aControllerPort, aDevice, aForce));
4823
4824 CheckComArgStrNotEmptyOrNull(aControllerName);
4825
4826 AutoCaller autoCaller(this);
4827 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4828
4829 // request the host lock first, since might be calling Host methods for getting host drives;
4830 // next, protect the media tree all the while we're in here, as well as our member variables
4831 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4832 this->lockHandle(),
4833 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4834
4835 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4836 aControllerName,
4837 aControllerPort,
4838 aDevice);
4839 if (pAttach.isNull())
4840 return setError(VBOX_E_OBJECT_NOT_FOUND,
4841 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4842 aDevice, aControllerPort, aControllerName);
4843
4844 /* Remember previously mounted medium. The medium before taking the
4845 * backup is not necessarily the same thing. */
4846 ComObjPtr<Medium> oldmedium;
4847 oldmedium = pAttach->getMedium();
4848
4849 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4850 if (aMedium && pMedium.isNull())
4851 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4852
4853 AutoCaller mediumCaller(pMedium);
4854 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4855
4856 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4857 if (pMedium)
4858 {
4859 DeviceType_T mediumType = pAttach->getType();
4860 switch (mediumType)
4861 {
4862 case DeviceType_DVD:
4863 case DeviceType_Floppy:
4864 break;
4865
4866 default:
4867 return setError(VBOX_E_INVALID_OBJECT_STATE,
4868 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
4869 aControllerPort,
4870 aDevice,
4871 aControllerName);
4872 }
4873 }
4874
4875 setModified(IsModified_Storage);
4876 mMediaData.backup();
4877
4878 {
4879 // The backup operation makes the pAttach reference point to the
4880 // old settings. Re-get the correct reference.
4881 pAttach = findAttachment(mMediaData->mAttachments,
4882 aControllerName,
4883 aControllerPort,
4884 aDevice);
4885 if (!oldmedium.isNull())
4886 oldmedium->removeBackReference(mData->mUuid);
4887 if (!pMedium.isNull())
4888 {
4889 pMedium->addBackReference(mData->mUuid);
4890
4891 mediumLock.release();
4892 multiLock.release();
4893 addMediumToRegistry(pMedium);
4894 multiLock.acquire();
4895 mediumLock.acquire();
4896 }
4897
4898 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4899 pAttach->updateMedium(pMedium);
4900 }
4901
4902 setModified(IsModified_Storage);
4903
4904 mediumLock.release();
4905 multiLock.release();
4906 rc = onMediumChange(pAttach, aForce);
4907 multiLock.acquire();
4908 mediumLock.acquire();
4909
4910 /* On error roll back this change only. */
4911 if (FAILED(rc))
4912 {
4913 if (!pMedium.isNull())
4914 pMedium->removeBackReference(mData->mUuid);
4915 pAttach = findAttachment(mMediaData->mAttachments,
4916 aControllerName,
4917 aControllerPort,
4918 aDevice);
4919 /* If the attachment is gone in the meantime, bail out. */
4920 if (pAttach.isNull())
4921 return rc;
4922 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4923 if (!oldmedium.isNull())
4924 oldmedium->addBackReference(mData->mUuid);
4925 pAttach->updateMedium(oldmedium);
4926 }
4927
4928 mediumLock.release();
4929 multiLock.release();
4930
4931 mParent->saveModifiedRegistries();
4932
4933 return rc;
4934}
4935
4936STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
4937 LONG aControllerPort,
4938 LONG aDevice,
4939 IMedium **aMedium)
4940{
4941 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4942 aControllerName, aControllerPort, aDevice));
4943
4944 CheckComArgStrNotEmptyOrNull(aControllerName);
4945 CheckComArgOutPointerValid(aMedium);
4946
4947 AutoCaller autoCaller(this);
4948 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4949
4950 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4951
4952 *aMedium = NULL;
4953
4954 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4955 aControllerName,
4956 aControllerPort,
4957 aDevice);
4958 if (pAttach.isNull())
4959 return setError(VBOX_E_OBJECT_NOT_FOUND,
4960 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4961 aDevice, aControllerPort, aControllerName);
4962
4963 pAttach->getMedium().queryInterfaceTo(aMedium);
4964
4965 return S_OK;
4966}
4967
4968STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
4969{
4970 CheckComArgOutPointerValid(port);
4971 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
4972
4973 AutoCaller autoCaller(this);
4974 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4975
4976 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4977
4978 mSerialPorts[slot].queryInterfaceTo(port);
4979
4980 return S_OK;
4981}
4982
4983STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
4984{
4985 CheckComArgOutPointerValid(port);
4986 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
4987
4988 AutoCaller autoCaller(this);
4989 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4990
4991 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4992
4993 mParallelPorts[slot].queryInterfaceTo(port);
4994
4995 return S_OK;
4996}
4997
4998STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
4999{
5000 CheckComArgOutPointerValid(adapter);
5001 /* Do not assert if slot is out of range, just return the advertised
5002 status. testdriver/vbox.py triggers this in logVmInfo. */
5003 if (slot >= mNetworkAdapters.size())
5004 return setError(E_INVALIDARG,
5005 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
5006 slot, mNetworkAdapters.size());
5007
5008 AutoCaller autoCaller(this);
5009 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5010
5011 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5012
5013 mNetworkAdapters[slot].queryInterfaceTo(adapter);
5014
5015 return S_OK;
5016}
5017
5018STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
5019{
5020 CheckComArgOutSafeArrayPointerValid(aKeys);
5021
5022 AutoCaller autoCaller(this);
5023 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5024
5025 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5026
5027 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
5028 int i = 0;
5029 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5030 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5031 ++it, ++i)
5032 {
5033 const Utf8Str &strKey = it->first;
5034 strKey.cloneTo(&saKeys[i]);
5035 }
5036 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
5037
5038 return S_OK;
5039 }
5040
5041 /**
5042 * @note Locks this object for reading.
5043 */
5044STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
5045 BSTR *aValue)
5046{
5047 CheckComArgStrNotEmptyOrNull(aKey);
5048 CheckComArgOutPointerValid(aValue);
5049
5050 AutoCaller autoCaller(this);
5051 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5052
5053 /* start with nothing found */
5054 Bstr bstrResult("");
5055
5056 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5057
5058 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
5059 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5060 // found:
5061 bstrResult = it->second; // source is a Utf8Str
5062
5063 /* return the result to caller (may be empty) */
5064 bstrResult.cloneTo(aValue);
5065
5066 return S_OK;
5067}
5068
5069 /**
5070 * @note Locks mParent for writing + this object for writing.
5071 */
5072STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
5073{
5074 CheckComArgStrNotEmptyOrNull(aKey);
5075
5076 AutoCaller autoCaller(this);
5077 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5078
5079 Utf8Str strKey(aKey);
5080 Utf8Str strValue(aValue);
5081 Utf8Str strOldValue; // empty
5082
5083 // locking note: we only hold the read lock briefly to look up the old value,
5084 // then release it and call the onExtraCanChange callbacks. There is a small
5085 // chance of a race insofar as the callback might be called twice if two callers
5086 // change the same key at the same time, but that's a much better solution
5087 // than the deadlock we had here before. The actual changing of the extradata
5088 // is then performed under the write lock and race-free.
5089
5090 // look up the old value first; if nothing has changed then we need not do anything
5091 {
5092 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5093 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
5094 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5095 strOldValue = it->second;
5096 }
5097
5098 bool fChanged;
5099 if ((fChanged = (strOldValue != strValue)))
5100 {
5101 // ask for permission from all listeners outside the locks;
5102 // onExtraDataCanChange() only briefly requests the VirtualBox
5103 // lock to copy the list of callbacks to invoke
5104 Bstr error;
5105 Bstr bstrValue(aValue);
5106
5107 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
5108 {
5109 const char *sep = error.isEmpty() ? "" : ": ";
5110 CBSTR err = error.raw();
5111 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
5112 sep, err));
5113 return setError(E_ACCESSDENIED,
5114 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
5115 aKey,
5116 bstrValue.raw(),
5117 sep,
5118 err);
5119 }
5120
5121 // data is changing and change not vetoed: then write it out under the lock
5122 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5123
5124 if (isSnapshotMachine())
5125 {
5126 HRESULT rc = checkStateDependency(MutableStateDep);
5127 if (FAILED(rc)) return rc;
5128 }
5129
5130 if (strValue.isEmpty())
5131 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
5132 else
5133 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
5134 // creates a new key if needed
5135
5136 bool fNeedsGlobalSaveSettings = false;
5137 saveSettings(&fNeedsGlobalSaveSettings);
5138
5139 if (fNeedsGlobalSaveSettings)
5140 {
5141 // save the global settings; for that we should hold only the VirtualBox lock
5142 alock.release();
5143 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5144 mParent->saveSettings();
5145 }
5146 }
5147
5148 // fire notification outside the lock
5149 if (fChanged)
5150 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
5151
5152 return S_OK;
5153}
5154
5155STDMETHODIMP Machine::SaveSettings()
5156{
5157 AutoCaller autoCaller(this);
5158 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5159
5160 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5161
5162 /* when there was auto-conversion, we want to save the file even if
5163 * the VM is saved */
5164 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
5165 if (FAILED(rc)) return rc;
5166
5167 /* the settings file path may never be null */
5168 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5169
5170 /* save all VM data excluding snapshots */
5171 bool fNeedsGlobalSaveSettings = false;
5172 rc = saveSettings(&fNeedsGlobalSaveSettings);
5173 mlock.release();
5174
5175 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5176 {
5177 // save the global settings; for that we should hold only the VirtualBox lock
5178 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5179 rc = mParent->saveSettings();
5180 }
5181
5182 return rc;
5183}
5184
5185STDMETHODIMP Machine::DiscardSettings()
5186{
5187 AutoCaller autoCaller(this);
5188 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5189
5190 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5191
5192 HRESULT rc = checkStateDependency(MutableStateDep);
5193 if (FAILED(rc)) return rc;
5194
5195 /*
5196 * during this rollback, the session will be notified if data has
5197 * been actually changed
5198 */
5199 rollback(true /* aNotify */);
5200
5201 return S_OK;
5202}
5203
5204/** @note Locks objects! */
5205STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
5206 ComSafeArrayOut(IMedium*, aMedia))
5207{
5208 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5209 AutoLimitedCaller autoCaller(this);
5210 AssertComRCReturnRC(autoCaller.rc());
5211
5212 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5213
5214 Guid id(getId());
5215
5216 if (mData->mSession.mState != SessionState_Unlocked)
5217 return setError(VBOX_E_INVALID_OBJECT_STATE,
5218 tr("Cannot unregister the machine '%s' while it is locked"),
5219 mUserData->s.strName.c_str());
5220
5221 // wait for state dependents to drop to zero
5222 ensureNoStateDependencies();
5223
5224 if (!mData->mAccessible)
5225 {
5226 // inaccessible maschines can only be unregistered; uninitialize ourselves
5227 // here because currently there may be no unregistered that are inaccessible
5228 // (this state combination is not supported). Note releasing the caller and
5229 // leaving the lock before calling uninit()
5230 alock.release();
5231 autoCaller.release();
5232
5233 uninit();
5234
5235 mParent->unregisterMachine(this, id);
5236 // calls VirtualBox::saveSettings()
5237
5238 return S_OK;
5239 }
5240
5241 HRESULT rc = S_OK;
5242
5243 // discard saved state
5244 if (mData->mMachineState == MachineState_Saved)
5245 {
5246 // add the saved state file to the list of files the caller should delete
5247 Assert(!mSSData->strStateFilePath.isEmpty());
5248 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5249
5250 mSSData->strStateFilePath.setNull();
5251
5252 // unconditionally set the machine state to powered off, we now
5253 // know no session has locked the machine
5254 mData->mMachineState = MachineState_PoweredOff;
5255 }
5256
5257 size_t cSnapshots = 0;
5258 if (mData->mFirstSnapshot)
5259 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5260 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
5261 // fail now before we start detaching media
5262 return setError(VBOX_E_INVALID_OBJECT_STATE,
5263 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5264 mUserData->s.strName.c_str(), cSnapshots);
5265
5266 // This list collects the medium objects from all medium attachments
5267 // which we will detach from the machine and its snapshots, in a specific
5268 // order which allows for closing all media without getting "media in use"
5269 // errors, simply by going through the list from the front to the back:
5270 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5271 // and must be closed before the parent media from the snapshots, or closing the parents
5272 // will fail because they still have children);
5273 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5274 // the root ("first") snapshot of the machine.
5275 MediaList llMedia;
5276
5277 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5278 && mMediaData->mAttachments.size()
5279 )
5280 {
5281 // we have media attachments: detach them all and add the Medium objects to our list
5282 if (cleanupMode != CleanupMode_UnregisterOnly)
5283 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5284 else
5285 return setError(VBOX_E_INVALID_OBJECT_STATE,
5286 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5287 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5288 }
5289
5290 if (cSnapshots)
5291 {
5292 // autoCleanup must be true here, or we would have failed above
5293
5294 // add the media from the medium attachments of the snapshots to llMedia
5295 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5296 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5297 // into the children first
5298
5299 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5300 MachineState_T oldState = mData->mMachineState;
5301 mData->mMachineState = MachineState_DeletingSnapshot;
5302
5303 // make a copy of the first snapshot so the refcount does not drop to 0
5304 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5305 // because of the AutoCaller voodoo)
5306 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5307
5308 // GO!
5309 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5310
5311 mData->mMachineState = oldState;
5312 }
5313
5314 if (FAILED(rc))
5315 {
5316 rollbackMedia();
5317 return rc;
5318 }
5319
5320 // commit all the media changes made above
5321 commitMedia();
5322
5323 mData->mRegistered = false;
5324
5325 // machine lock no longer needed
5326 alock.release();
5327
5328 // return media to caller
5329 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5330 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5331
5332 mParent->unregisterMachine(this, id);
5333 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5334
5335 return S_OK;
5336}
5337
5338struct Machine::DeleteTask
5339{
5340 ComObjPtr<Machine> pMachine;
5341 RTCList<ComPtr<IMedium> > llMediums;
5342 StringsList llFilesToDelete;
5343 ComObjPtr<Progress> pProgress;
5344};
5345
5346STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5347{
5348 LogFlowFuncEnter();
5349
5350 AutoCaller autoCaller(this);
5351 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5352
5353 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5354
5355 HRESULT rc = checkStateDependency(MutableStateDep);
5356 if (FAILED(rc)) return rc;
5357
5358 if (mData->mRegistered)
5359 return setError(VBOX_E_INVALID_VM_STATE,
5360 tr("Cannot delete settings of a registered machine"));
5361
5362 DeleteTask *pTask = new DeleteTask;
5363 pTask->pMachine = this;
5364 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5365
5366 // collect files to delete
5367 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5368
5369 for (size_t i = 0; i < sfaMedia.size(); ++i)
5370 {
5371 IMedium *pIMedium(sfaMedia[i]);
5372 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5373 if (pMedium.isNull())
5374 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5375 SafeArray<BSTR> ids;
5376 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5377 if (FAILED(rc)) return rc;
5378 /* At this point the medium should not have any back references
5379 * anymore. If it has it is attached to another VM and *must* not
5380 * deleted. */
5381 if (ids.size() < 1)
5382 pTask->llMediums.append(pMedium);
5383 }
5384 if (mData->pMachineConfigFile->fileExists())
5385 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5386
5387 pTask->pProgress.createObject();
5388 pTask->pProgress->init(getVirtualBox(),
5389 static_cast<IMachine*>(this) /* aInitiator */,
5390 Bstr(tr("Deleting files")).raw(),
5391 true /* fCancellable */,
5392 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5393 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5394
5395 int vrc = RTThreadCreate(NULL,
5396 Machine::deleteThread,
5397 (void*)pTask,
5398 0,
5399 RTTHREADTYPE_MAIN_WORKER,
5400 0,
5401 "MachineDelete");
5402
5403 pTask->pProgress.queryInterfaceTo(aProgress);
5404
5405 if (RT_FAILURE(vrc))
5406 {
5407 delete pTask;
5408 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5409 }
5410
5411 LogFlowFuncLeave();
5412
5413 return S_OK;
5414}
5415
5416/**
5417 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5418 * calls Machine::deleteTaskWorker() on the actual machine object.
5419 * @param Thread
5420 * @param pvUser
5421 * @return
5422 */
5423/*static*/
5424DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5425{
5426 LogFlowFuncEnter();
5427
5428 DeleteTask *pTask = (DeleteTask*)pvUser;
5429 Assert(pTask);
5430 Assert(pTask->pMachine);
5431 Assert(pTask->pProgress);
5432
5433 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5434 pTask->pProgress->notifyComplete(rc);
5435
5436 delete pTask;
5437
5438 LogFlowFuncLeave();
5439
5440 NOREF(Thread);
5441
5442 return VINF_SUCCESS;
5443}
5444
5445/**
5446 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5447 * @param task
5448 * @return
5449 */
5450HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5451{
5452 AutoCaller autoCaller(this);
5453 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5454
5455 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5456
5457 HRESULT rc = S_OK;
5458
5459 try
5460 {
5461 ULONG uLogHistoryCount = 3;
5462 ComPtr<ISystemProperties> systemProperties;
5463 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5464 if (FAILED(rc)) throw rc;
5465
5466 if (!systemProperties.isNull())
5467 {
5468 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5469 if (FAILED(rc)) throw rc;
5470 }
5471
5472 MachineState_T oldState = mData->mMachineState;
5473 setMachineState(MachineState_SettingUp);
5474 alock.release();
5475 for (size_t i = 0; i < task.llMediums.size(); ++i)
5476 {
5477 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5478 {
5479 AutoCaller mac(pMedium);
5480 if (FAILED(mac.rc())) throw mac.rc();
5481 Utf8Str strLocation = pMedium->getLocationFull();
5482 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5483 if (FAILED(rc)) throw rc;
5484 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5485 }
5486 ComPtr<IProgress> pProgress2;
5487 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5488 if (FAILED(rc)) throw rc;
5489 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5490 if (FAILED(rc)) throw rc;
5491 /* Check the result of the asynchrony process. */
5492 LONG iRc;
5493 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5494 if (FAILED(rc)) throw rc;
5495 /* If the thread of the progress object has an error, then
5496 * retrieve the error info from there, or it'll be lost. */
5497 if (FAILED(iRc))
5498 throw setError(ProgressErrorInfo(pProgress2));
5499 }
5500 setMachineState(oldState);
5501 alock.acquire();
5502
5503 // delete the files pushed on the task list by Machine::Delete()
5504 // (this includes saved states of the machine and snapshots and
5505 // medium storage files from the IMedium list passed in, and the
5506 // machine XML file)
5507 StringsList::const_iterator it = task.llFilesToDelete.begin();
5508 while (it != task.llFilesToDelete.end())
5509 {
5510 const Utf8Str &strFile = *it;
5511 LogFunc(("Deleting file %s\n", strFile.c_str()));
5512 int vrc = RTFileDelete(strFile.c_str());
5513 if (RT_FAILURE(vrc))
5514 throw setError(VBOX_E_IPRT_ERROR,
5515 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5516
5517 ++it;
5518 if (it == task.llFilesToDelete.end())
5519 {
5520 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5521 if (FAILED(rc)) throw rc;
5522 break;
5523 }
5524
5525 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5526 if (FAILED(rc)) throw rc;
5527 }
5528
5529 /* delete the settings only when the file actually exists */
5530 if (mData->pMachineConfigFile->fileExists())
5531 {
5532 /* Delete any backup or uncommitted XML files. Ignore failures.
5533 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5534 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5535 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5536 RTFileDelete(otherXml.c_str());
5537 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5538 RTFileDelete(otherXml.c_str());
5539
5540 /* delete the Logs folder, nothing important should be left
5541 * there (we don't check for errors because the user might have
5542 * some private files there that we don't want to delete) */
5543 Utf8Str logFolder;
5544 getLogFolder(logFolder);
5545 Assert(logFolder.length());
5546 if (RTDirExists(logFolder.c_str()))
5547 {
5548 /* Delete all VBox.log[.N] files from the Logs folder
5549 * (this must be in sync with the rotation logic in
5550 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5551 * files that may have been created by the GUI. */
5552 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5553 logFolder.c_str(), RTPATH_DELIMITER);
5554 RTFileDelete(log.c_str());
5555 log = Utf8StrFmt("%s%cVBox.png",
5556 logFolder.c_str(), RTPATH_DELIMITER);
5557 RTFileDelete(log.c_str());
5558 for (int i = uLogHistoryCount; i > 0; i--)
5559 {
5560 log = Utf8StrFmt("%s%cVBox.log.%d",
5561 logFolder.c_str(), RTPATH_DELIMITER, i);
5562 RTFileDelete(log.c_str());
5563 log = Utf8StrFmt("%s%cVBox.png.%d",
5564 logFolder.c_str(), RTPATH_DELIMITER, i);
5565 RTFileDelete(log.c_str());
5566 }
5567
5568 RTDirRemove(logFolder.c_str());
5569 }
5570
5571 /* delete the Snapshots folder, nothing important should be left
5572 * there (we don't check for errors because the user might have
5573 * some private files there that we don't want to delete) */
5574 Utf8Str strFullSnapshotFolder;
5575 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5576 Assert(!strFullSnapshotFolder.isEmpty());
5577 if (RTDirExists(strFullSnapshotFolder.c_str()))
5578 RTDirRemove(strFullSnapshotFolder.c_str());
5579
5580 // delete the directory that contains the settings file, but only
5581 // if it matches the VM name
5582 Utf8Str settingsDir;
5583 if (isInOwnDir(&settingsDir))
5584 RTDirRemove(settingsDir.c_str());
5585 }
5586
5587 alock.release();
5588
5589 mParent->saveModifiedRegistries();
5590 }
5591 catch (HRESULT aRC) { rc = aRC; }
5592
5593 return rc;
5594}
5595
5596STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5597{
5598 CheckComArgOutPointerValid(aSnapshot);
5599
5600 AutoCaller autoCaller(this);
5601 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5602
5603 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5604
5605 ComObjPtr<Snapshot> pSnapshot;
5606 HRESULT rc;
5607
5608 if (!aNameOrId || !*aNameOrId)
5609 // null case (caller wants root snapshot): findSnapshotById() handles this
5610 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5611 else
5612 {
5613 Guid uuid(aNameOrId);
5614 if (uuid.isValid())
5615 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5616 else
5617 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5618 }
5619 pSnapshot.queryInterfaceTo(aSnapshot);
5620
5621 return rc;
5622}
5623
5624STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5625{
5626 CheckComArgStrNotEmptyOrNull(aName);
5627 CheckComArgStrNotEmptyOrNull(aHostPath);
5628
5629 AutoCaller autoCaller(this);
5630 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5631
5632 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5633
5634 HRESULT rc = checkStateDependency(MutableStateDep);
5635 if (FAILED(rc)) return rc;
5636
5637 Utf8Str strName(aName);
5638
5639 ComObjPtr<SharedFolder> sharedFolder;
5640 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5641 if (SUCCEEDED(rc))
5642 return setError(VBOX_E_OBJECT_IN_USE,
5643 tr("Shared folder named '%s' already exists"),
5644 strName.c_str());
5645
5646 sharedFolder.createObject();
5647 rc = sharedFolder->init(getMachine(),
5648 strName,
5649 aHostPath,
5650 !!aWritable,
5651 !!aAutoMount,
5652 true /* fFailOnError */);
5653 if (FAILED(rc)) return rc;
5654
5655 setModified(IsModified_SharedFolders);
5656 mHWData.backup();
5657 mHWData->mSharedFolders.push_back(sharedFolder);
5658
5659 /* inform the direct session if any */
5660 alock.release();
5661 onSharedFolderChange();
5662
5663 return S_OK;
5664}
5665
5666STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5667{
5668 CheckComArgStrNotEmptyOrNull(aName);
5669
5670 AutoCaller autoCaller(this);
5671 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5672
5673 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5674
5675 HRESULT rc = checkStateDependency(MutableStateDep);
5676 if (FAILED(rc)) return rc;
5677
5678 ComObjPtr<SharedFolder> sharedFolder;
5679 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5680 if (FAILED(rc)) return rc;
5681
5682 setModified(IsModified_SharedFolders);
5683 mHWData.backup();
5684 mHWData->mSharedFolders.remove(sharedFolder);
5685
5686 /* inform the direct session if any */
5687 alock.release();
5688 onSharedFolderChange();
5689
5690 return S_OK;
5691}
5692
5693STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5694{
5695 CheckComArgOutPointerValid(aCanShow);
5696
5697 /* start with No */
5698 *aCanShow = FALSE;
5699
5700 AutoCaller autoCaller(this);
5701 AssertComRCReturnRC(autoCaller.rc());
5702
5703 ComPtr<IInternalSessionControl> directControl;
5704 {
5705 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5706
5707 if (mData->mSession.mState != SessionState_Locked)
5708 return setError(VBOX_E_INVALID_VM_STATE,
5709 tr("Machine is not locked for session (session state: %s)"),
5710 Global::stringifySessionState(mData->mSession.mState));
5711
5712 directControl = mData->mSession.mDirectControl;
5713 }
5714
5715 /* ignore calls made after #OnSessionEnd() is called */
5716 if (!directControl)
5717 return S_OK;
5718
5719 LONG64 dummy;
5720 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5721}
5722
5723STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5724{
5725 CheckComArgOutPointerValid(aWinId);
5726
5727 AutoCaller autoCaller(this);
5728 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5729
5730 ComPtr<IInternalSessionControl> directControl;
5731 {
5732 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5733
5734 if (mData->mSession.mState != SessionState_Locked)
5735 return setError(E_FAIL,
5736 tr("Machine is not locked for session (session state: %s)"),
5737 Global::stringifySessionState(mData->mSession.mState));
5738
5739 directControl = mData->mSession.mDirectControl;
5740 }
5741
5742 /* ignore calls made after #OnSessionEnd() is called */
5743 if (!directControl)
5744 return S_OK;
5745
5746 BOOL dummy;
5747 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5748}
5749
5750#ifdef VBOX_WITH_GUEST_PROPS
5751/**
5752 * Look up a guest property in VBoxSVC's internal structures.
5753 */
5754HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5755 BSTR *aValue,
5756 LONG64 *aTimestamp,
5757 BSTR *aFlags) const
5758{
5759 using namespace guestProp;
5760
5761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5762 Utf8Str strName(aName);
5763 HWData::GuestPropertyMap::const_iterator it =
5764 mHWData->mGuestProperties.find(strName);
5765
5766 if (it != mHWData->mGuestProperties.end())
5767 {
5768 char szFlags[MAX_FLAGS_LEN + 1];
5769 it->second.strValue.cloneTo(aValue);
5770 *aTimestamp = it->second.mTimestamp;
5771 writeFlags(it->second.mFlags, szFlags);
5772 Bstr(szFlags).cloneTo(aFlags);
5773 }
5774
5775 return S_OK;
5776}
5777
5778/**
5779 * Query the VM that a guest property belongs to for the property.
5780 * @returns E_ACCESSDENIED if the VM process is not available or not
5781 * currently handling queries and the lookup should then be done in
5782 * VBoxSVC.
5783 */
5784HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5785 BSTR *aValue,
5786 LONG64 *aTimestamp,
5787 BSTR *aFlags) const
5788{
5789 HRESULT rc;
5790 ComPtr<IInternalSessionControl> directControl;
5791 directControl = mData->mSession.mDirectControl;
5792
5793 /* fail if we were called after #OnSessionEnd() is called. This is a
5794 * silly race condition. */
5795
5796 if (!directControl)
5797 rc = E_ACCESSDENIED;
5798 else
5799 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5800 false /* isSetter */,
5801 aValue, aTimestamp, aFlags);
5802 return rc;
5803}
5804#endif // VBOX_WITH_GUEST_PROPS
5805
5806STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5807 BSTR *aValue,
5808 LONG64 *aTimestamp,
5809 BSTR *aFlags)
5810{
5811#ifndef VBOX_WITH_GUEST_PROPS
5812 ReturnComNotImplemented();
5813#else // VBOX_WITH_GUEST_PROPS
5814 CheckComArgStrNotEmptyOrNull(aName);
5815 CheckComArgOutPointerValid(aValue);
5816 CheckComArgOutPointerValid(aTimestamp);
5817 CheckComArgOutPointerValid(aFlags);
5818
5819 AutoCaller autoCaller(this);
5820 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5821
5822 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5823 if (rc == E_ACCESSDENIED)
5824 /* The VM is not running or the service is not (yet) accessible */
5825 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5826 return rc;
5827#endif // VBOX_WITH_GUEST_PROPS
5828}
5829
5830STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5831{
5832 LONG64 dummyTimestamp;
5833 Bstr dummyFlags;
5834 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5835}
5836
5837STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5838{
5839 Bstr dummyValue;
5840 Bstr dummyFlags;
5841 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5842}
5843
5844#ifdef VBOX_WITH_GUEST_PROPS
5845/**
5846 * Set a guest property in VBoxSVC's internal structures.
5847 */
5848HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5849 IN_BSTR aFlags)
5850{
5851 using namespace guestProp;
5852
5853 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5854 HRESULT rc = S_OK;
5855 HWData::GuestProperty property;
5856 property.mFlags = NILFLAG;
5857
5858 rc = checkStateDependency(MutableStateDep);
5859 if (FAILED(rc)) return rc;
5860
5861 try
5862 {
5863 Utf8Str utf8Name(aName);
5864 Utf8Str utf8Flags(aFlags);
5865 uint32_t fFlags = NILFLAG;
5866 if ( aFlags != NULL
5867 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags)))
5868 return setError(E_INVALIDARG,
5869 tr("Invalid guest property flag values: '%ls'"),
5870 aFlags);
5871
5872 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
5873 HWData::GuestPropertyMap::iterator it =
5874 mHWData->mGuestProperties.find(utf8Name);
5875
5876 if (it == mHWData->mGuestProperties.end())
5877 {
5878 /* only create the new property if this is really desired */
5879 if (!fDelete)
5880 {
5881 setModified(IsModified_MachineData);
5882 mHWData.backupEx();
5883
5884 RTTIMESPEC time;
5885 HWData::GuestProperty prop;
5886 prop.strValue = aValue;
5887 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5888 prop.mFlags = fFlags;
5889
5890 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
5891 }
5892 }
5893 else
5894 {
5895 if (it->second.mFlags & (RDONLYHOST))
5896 {
5897 rc = setError(E_ACCESSDENIED,
5898 tr("The property '%ls' cannot be changed by the host"),
5899 aName);
5900 }
5901 else
5902 {
5903 setModified(IsModified_MachineData);
5904 mHWData.backupEx();
5905
5906 /* The backupEx() operation invalidates our iterator,
5907 * so get a new one. */
5908 it = mHWData->mGuestProperties.find(utf8Name);
5909 Assert(it != mHWData->mGuestProperties.end());
5910
5911 if (!fDelete)
5912 {
5913 RTTIMESPEC time;
5914 it->second.strValue = aValue;
5915 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5916 if (aFlags != NULL)
5917 it->second.mFlags = fFlags;
5918 }
5919 else
5920 {
5921 mHWData->mGuestProperties.erase(it);
5922 }
5923 }
5924 }
5925
5926 if ( SUCCEEDED(rc)
5927 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5928 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5929 RTSTR_MAX,
5930 utf8Name.c_str(),
5931 RTSTR_MAX,
5932 NULL)
5933 )
5934 )
5935 {
5936 alock.release();
5937
5938 mParent->onGuestPropertyChange(mData->mUuid, aName,
5939 aValue ? aValue : Bstr("").raw(),
5940 aFlags ? aFlags : Bstr("").raw());
5941 }
5942 }
5943 catch (std::bad_alloc &)
5944 {
5945 rc = E_OUTOFMEMORY;
5946 }
5947
5948 return rc;
5949}
5950
5951/**
5952 * Set a property on the VM that that property belongs to.
5953 * @returns E_ACCESSDENIED if the VM process is not available or not
5954 * currently handling queries and the setting should then be done in
5955 * VBoxSVC.
5956 */
5957HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
5958 IN_BSTR aFlags)
5959{
5960 HRESULT rc;
5961
5962 try
5963 {
5964 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5965
5966 BSTR dummy = NULL; /* will not be changed (setter) */
5967 LONG64 dummy64;
5968 if (!directControl)
5969 rc = E_ACCESSDENIED;
5970 else
5971 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5972 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
5973 true /* isSetter */,
5974 &dummy, &dummy64, &dummy);
5975 }
5976 catch (std::bad_alloc &)
5977 {
5978 rc = E_OUTOFMEMORY;
5979 }
5980
5981 return rc;
5982}
5983#endif // VBOX_WITH_GUEST_PROPS
5984
5985STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
5986 IN_BSTR aFlags)
5987{
5988#ifndef VBOX_WITH_GUEST_PROPS
5989 ReturnComNotImplemented();
5990#else // VBOX_WITH_GUEST_PROPS
5991 CheckComArgStrNotEmptyOrNull(aName);
5992 CheckComArgMaybeNull(aFlags);
5993 CheckComArgMaybeNull(aValue);
5994
5995 AutoCaller autoCaller(this);
5996 if (FAILED(autoCaller.rc()))
5997 return autoCaller.rc();
5998
5999 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
6000 if (rc == E_ACCESSDENIED)
6001 /* The VM is not running or the service is not (yet) accessible */
6002 rc = setGuestPropertyToService(aName, aValue, aFlags);
6003 return rc;
6004#endif // VBOX_WITH_GUEST_PROPS
6005}
6006
6007STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
6008{
6009 return SetGuestProperty(aName, aValue, NULL);
6010}
6011
6012STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
6013{
6014 return SetGuestProperty(aName, NULL, NULL);
6015}
6016
6017#ifdef VBOX_WITH_GUEST_PROPS
6018/**
6019 * Enumerate the guest properties in VBoxSVC's internal structures.
6020 */
6021HRESULT Machine::enumerateGuestPropertiesInService
6022 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6023 ComSafeArrayOut(BSTR, aValues),
6024 ComSafeArrayOut(LONG64, aTimestamps),
6025 ComSafeArrayOut(BSTR, aFlags))
6026{
6027 using namespace guestProp;
6028
6029 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6030 Utf8Str strPatterns(aPatterns);
6031
6032 HWData::GuestPropertyMap propMap;
6033
6034 /*
6035 * Look for matching patterns and build up a list.
6036 */
6037 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
6038 while (it != mHWData->mGuestProperties.end())
6039 {
6040 if ( strPatterns.isEmpty()
6041 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6042 RTSTR_MAX,
6043 it->first.c_str(),
6044 RTSTR_MAX,
6045 NULL)
6046 )
6047 {
6048 propMap.insert(*it);
6049 }
6050
6051 it++;
6052 }
6053
6054 alock.release();
6055
6056 /*
6057 * And build up the arrays for returning the property information.
6058 */
6059 size_t cEntries = propMap.size();
6060 SafeArray<BSTR> names(cEntries);
6061 SafeArray<BSTR> values(cEntries);
6062 SafeArray<LONG64> timestamps(cEntries);
6063 SafeArray<BSTR> flags(cEntries);
6064 size_t iProp = 0;
6065
6066 it = propMap.begin();
6067 while (it != propMap.end())
6068 {
6069 char szFlags[MAX_FLAGS_LEN + 1];
6070 it->first.cloneTo(&names[iProp]);
6071 it->second.strValue.cloneTo(&values[iProp]);
6072 timestamps[iProp] = it->second.mTimestamp;
6073 writeFlags(it->second.mFlags, szFlags);
6074 Bstr(szFlags).cloneTo(&flags[iProp++]);
6075 it++;
6076 }
6077 names.detachTo(ComSafeArrayOutArg(aNames));
6078 values.detachTo(ComSafeArrayOutArg(aValues));
6079 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
6080 flags.detachTo(ComSafeArrayOutArg(aFlags));
6081 return S_OK;
6082}
6083
6084/**
6085 * Enumerate the properties managed by a VM.
6086 * @returns E_ACCESSDENIED if the VM process is not available or not
6087 * currently handling queries and the setting should then be done in
6088 * VBoxSVC.
6089 */
6090HRESULT Machine::enumerateGuestPropertiesOnVM
6091 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6092 ComSafeArrayOut(BSTR, aValues),
6093 ComSafeArrayOut(LONG64, aTimestamps),
6094 ComSafeArrayOut(BSTR, aFlags))
6095{
6096 HRESULT rc;
6097 ComPtr<IInternalSessionControl> directControl;
6098 directControl = mData->mSession.mDirectControl;
6099
6100 if (!directControl)
6101 rc = E_ACCESSDENIED;
6102 else
6103 rc = directControl->EnumerateGuestProperties
6104 (aPatterns, ComSafeArrayOutArg(aNames),
6105 ComSafeArrayOutArg(aValues),
6106 ComSafeArrayOutArg(aTimestamps),
6107 ComSafeArrayOutArg(aFlags));
6108 return rc;
6109}
6110#endif // VBOX_WITH_GUEST_PROPS
6111
6112STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
6113 ComSafeArrayOut(BSTR, aNames),
6114 ComSafeArrayOut(BSTR, aValues),
6115 ComSafeArrayOut(LONG64, aTimestamps),
6116 ComSafeArrayOut(BSTR, aFlags))
6117{
6118#ifndef VBOX_WITH_GUEST_PROPS
6119 ReturnComNotImplemented();
6120#else // VBOX_WITH_GUEST_PROPS
6121 CheckComArgMaybeNull(aPatterns);
6122 CheckComArgOutSafeArrayPointerValid(aNames);
6123 CheckComArgOutSafeArrayPointerValid(aValues);
6124 CheckComArgOutSafeArrayPointerValid(aTimestamps);
6125 CheckComArgOutSafeArrayPointerValid(aFlags);
6126
6127 AutoCaller autoCaller(this);
6128 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6129
6130 HRESULT rc = enumerateGuestPropertiesOnVM
6131 (aPatterns, ComSafeArrayOutArg(aNames),
6132 ComSafeArrayOutArg(aValues),
6133 ComSafeArrayOutArg(aTimestamps),
6134 ComSafeArrayOutArg(aFlags));
6135 if (rc == E_ACCESSDENIED)
6136 /* The VM is not running or the service is not (yet) accessible */
6137 rc = enumerateGuestPropertiesInService
6138 (aPatterns, ComSafeArrayOutArg(aNames),
6139 ComSafeArrayOutArg(aValues),
6140 ComSafeArrayOutArg(aTimestamps),
6141 ComSafeArrayOutArg(aFlags));
6142 return rc;
6143#endif // VBOX_WITH_GUEST_PROPS
6144}
6145
6146STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
6147 ComSafeArrayOut(IMediumAttachment*, aAttachments))
6148{
6149 MediaData::AttachmentList atts;
6150
6151 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
6152 if (FAILED(rc)) return rc;
6153
6154 SafeIfaceArray<IMediumAttachment> attachments(atts);
6155 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
6156
6157 return S_OK;
6158}
6159
6160STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
6161 LONG aControllerPort,
6162 LONG aDevice,
6163 IMediumAttachment **aAttachment)
6164{
6165 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
6166 aControllerName, aControllerPort, aDevice));
6167
6168 CheckComArgStrNotEmptyOrNull(aControllerName);
6169 CheckComArgOutPointerValid(aAttachment);
6170
6171 AutoCaller autoCaller(this);
6172 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6173
6174 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6175
6176 *aAttachment = NULL;
6177
6178 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
6179 aControllerName,
6180 aControllerPort,
6181 aDevice);
6182 if (pAttach.isNull())
6183 return setError(VBOX_E_OBJECT_NOT_FOUND,
6184 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
6185 aDevice, aControllerPort, aControllerName);
6186
6187 pAttach.queryInterfaceTo(aAttachment);
6188
6189 return S_OK;
6190}
6191
6192STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
6193 StorageBus_T aConnectionType,
6194 IStorageController **controller)
6195{
6196 CheckComArgStrNotEmptyOrNull(aName);
6197
6198 if ( (aConnectionType <= StorageBus_Null)
6199 || (aConnectionType > StorageBus_SAS))
6200 return setError(E_INVALIDARG,
6201 tr("Invalid connection type: %d"),
6202 aConnectionType);
6203
6204 AutoCaller autoCaller(this);
6205 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6206
6207 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6208
6209 HRESULT rc = checkStateDependency(MutableStateDep);
6210 if (FAILED(rc)) return rc;
6211
6212 /* try to find one with the name first. */
6213 ComObjPtr<StorageController> ctrl;
6214
6215 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
6216 if (SUCCEEDED(rc))
6217 return setError(VBOX_E_OBJECT_IN_USE,
6218 tr("Storage controller named '%ls' already exists"),
6219 aName);
6220
6221 ctrl.createObject();
6222
6223 /* get a new instance number for the storage controller */
6224 ULONG ulInstance = 0;
6225 bool fBootable = true;
6226 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6227 it != mStorageControllers->end();
6228 ++it)
6229 {
6230 if ((*it)->getStorageBus() == aConnectionType)
6231 {
6232 ULONG ulCurInst = (*it)->getInstance();
6233
6234 if (ulCurInst >= ulInstance)
6235 ulInstance = ulCurInst + 1;
6236
6237 /* Only one controller of each type can be marked as bootable. */
6238 if ((*it)->getBootable())
6239 fBootable = false;
6240 }
6241 }
6242
6243 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6244 if (FAILED(rc)) return rc;
6245
6246 setModified(IsModified_Storage);
6247 mStorageControllers.backup();
6248 mStorageControllers->push_back(ctrl);
6249
6250 ctrl.queryInterfaceTo(controller);
6251
6252 /* inform the direct session if any */
6253 alock.release();
6254 onStorageControllerChange();
6255
6256 return S_OK;
6257}
6258
6259STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6260 IStorageController **aStorageController)
6261{
6262 CheckComArgStrNotEmptyOrNull(aName);
6263
6264 AutoCaller autoCaller(this);
6265 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6266
6267 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6268
6269 ComObjPtr<StorageController> ctrl;
6270
6271 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6272 if (SUCCEEDED(rc))
6273 ctrl.queryInterfaceTo(aStorageController);
6274
6275 return rc;
6276}
6277
6278STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6279 IStorageController **aStorageController)
6280{
6281 AutoCaller autoCaller(this);
6282 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6283
6284 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6285
6286 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6287 it != mStorageControllers->end();
6288 ++it)
6289 {
6290 if ((*it)->getInstance() == aInstance)
6291 {
6292 (*it).queryInterfaceTo(aStorageController);
6293 return S_OK;
6294 }
6295 }
6296
6297 return setError(VBOX_E_OBJECT_NOT_FOUND,
6298 tr("Could not find a storage controller with instance number '%lu'"),
6299 aInstance);
6300}
6301
6302STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6303{
6304 AutoCaller autoCaller(this);
6305 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6306
6307 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6308
6309 HRESULT rc = checkStateDependency(MutableStateDep);
6310 if (FAILED(rc)) return rc;
6311
6312 ComObjPtr<StorageController> ctrl;
6313
6314 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6315 if (SUCCEEDED(rc))
6316 {
6317 /* Ensure that only one controller of each type is marked as bootable. */
6318 if (fBootable == TRUE)
6319 {
6320 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6321 it != mStorageControllers->end();
6322 ++it)
6323 {
6324 ComObjPtr<StorageController> aCtrl = (*it);
6325
6326 if ( (aCtrl->getName() != Utf8Str(aName))
6327 && aCtrl->getBootable() == TRUE
6328 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6329 && aCtrl->getControllerType() == ctrl->getControllerType())
6330 {
6331 aCtrl->setBootable(FALSE);
6332 break;
6333 }
6334 }
6335 }
6336
6337 if (SUCCEEDED(rc))
6338 {
6339 ctrl->setBootable(fBootable);
6340 setModified(IsModified_Storage);
6341 }
6342 }
6343
6344 if (SUCCEEDED(rc))
6345 {
6346 /* inform the direct session if any */
6347 alock.release();
6348 onStorageControllerChange();
6349 }
6350
6351 return rc;
6352}
6353
6354STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6355{
6356 CheckComArgStrNotEmptyOrNull(aName);
6357
6358 AutoCaller autoCaller(this);
6359 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6360
6361 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6362
6363 HRESULT rc = checkStateDependency(MutableStateDep);
6364 if (FAILED(rc)) return rc;
6365
6366 ComObjPtr<StorageController> ctrl;
6367 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6368 if (FAILED(rc)) return rc;
6369
6370 {
6371 /* find all attached devices to the appropriate storage controller and detach them all */
6372 // make a temporary list because detachDevice invalidates iterators into
6373 // mMediaData->mAttachments
6374 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6375
6376 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6377 it != llAttachments2.end();
6378 ++it)
6379 {
6380 MediumAttachment *pAttachTemp = *it;
6381
6382 AutoCaller localAutoCaller(pAttachTemp);
6383 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6384
6385 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6386
6387 if (pAttachTemp->getControllerName() == aName)
6388 {
6389 rc = detachDevice(pAttachTemp, alock, NULL);
6390 if (FAILED(rc)) return rc;
6391 }
6392 }
6393 }
6394
6395 /* We can remove it now. */
6396 setModified(IsModified_Storage);
6397 mStorageControllers.backup();
6398
6399 ctrl->unshare();
6400
6401 mStorageControllers->remove(ctrl);
6402
6403 /* inform the direct session if any */
6404 alock.release();
6405 onStorageControllerChange();
6406
6407 return S_OK;
6408}
6409
6410STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6411 ULONG *puOriginX,
6412 ULONG *puOriginY,
6413 ULONG *puWidth,
6414 ULONG *puHeight,
6415 BOOL *pfEnabled)
6416{
6417 LogFlowThisFunc(("\n"));
6418
6419 CheckComArgNotNull(puOriginX);
6420 CheckComArgNotNull(puOriginY);
6421 CheckComArgNotNull(puWidth);
6422 CheckComArgNotNull(puHeight);
6423 CheckComArgNotNull(pfEnabled);
6424
6425 uint32_t u32OriginX= 0;
6426 uint32_t u32OriginY= 0;
6427 uint32_t u32Width = 0;
6428 uint32_t u32Height = 0;
6429 uint16_t u16Flags = 0;
6430
6431 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6432 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6433 if (RT_FAILURE(vrc))
6434 {
6435#ifdef RT_OS_WINDOWS
6436 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6437 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6438 * So just assign fEnable to TRUE again.
6439 * The right fix would be to change GUI API wrappers to make sure that parameters
6440 * are changed only if API succeeds.
6441 */
6442 *pfEnabled = TRUE;
6443#endif
6444 return setError(VBOX_E_IPRT_ERROR,
6445 tr("Saved guest size is not available (%Rrc)"),
6446 vrc);
6447 }
6448
6449 *puOriginX = u32OriginX;
6450 *puOriginY = u32OriginY;
6451 *puWidth = u32Width;
6452 *puHeight = u32Height;
6453 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6454
6455 return S_OK;
6456}
6457
6458STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6459{
6460 LogFlowThisFunc(("\n"));
6461
6462 CheckComArgNotNull(aSize);
6463 CheckComArgNotNull(aWidth);
6464 CheckComArgNotNull(aHeight);
6465
6466 if (aScreenId != 0)
6467 return E_NOTIMPL;
6468
6469 AutoCaller autoCaller(this);
6470 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6471
6472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6473
6474 uint8_t *pu8Data = NULL;
6475 uint32_t cbData = 0;
6476 uint32_t u32Width = 0;
6477 uint32_t u32Height = 0;
6478
6479 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6480
6481 if (RT_FAILURE(vrc))
6482 return setError(VBOX_E_IPRT_ERROR,
6483 tr("Saved screenshot data is not available (%Rrc)"),
6484 vrc);
6485
6486 *aSize = cbData;
6487 *aWidth = u32Width;
6488 *aHeight = u32Height;
6489
6490 freeSavedDisplayScreenshot(pu8Data);
6491
6492 return S_OK;
6493}
6494
6495STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6496{
6497 LogFlowThisFunc(("\n"));
6498
6499 CheckComArgNotNull(aWidth);
6500 CheckComArgNotNull(aHeight);
6501 CheckComArgOutSafeArrayPointerValid(aData);
6502
6503 if (aScreenId != 0)
6504 return E_NOTIMPL;
6505
6506 AutoCaller autoCaller(this);
6507 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6508
6509 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6510
6511 uint8_t *pu8Data = NULL;
6512 uint32_t cbData = 0;
6513 uint32_t u32Width = 0;
6514 uint32_t u32Height = 0;
6515
6516 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6517
6518 if (RT_FAILURE(vrc))
6519 return setError(VBOX_E_IPRT_ERROR,
6520 tr("Saved screenshot data is not available (%Rrc)"),
6521 vrc);
6522
6523 *aWidth = u32Width;
6524 *aHeight = u32Height;
6525
6526 com::SafeArray<BYTE> bitmap(cbData);
6527 /* Convert pixels to format expected by the API caller. */
6528 if (aBGR)
6529 {
6530 /* [0] B, [1] G, [2] R, [3] A. */
6531 for (unsigned i = 0; i < cbData; i += 4)
6532 {
6533 bitmap[i] = pu8Data[i];
6534 bitmap[i + 1] = pu8Data[i + 1];
6535 bitmap[i + 2] = pu8Data[i + 2];
6536 bitmap[i + 3] = 0xff;
6537 }
6538 }
6539 else
6540 {
6541 /* [0] R, [1] G, [2] B, [3] A. */
6542 for (unsigned i = 0; i < cbData; i += 4)
6543 {
6544 bitmap[i] = pu8Data[i + 2];
6545 bitmap[i + 1] = pu8Data[i + 1];
6546 bitmap[i + 2] = pu8Data[i];
6547 bitmap[i + 3] = 0xff;
6548 }
6549 }
6550 bitmap.detachTo(ComSafeArrayOutArg(aData));
6551
6552 freeSavedDisplayScreenshot(pu8Data);
6553
6554 return S_OK;
6555}
6556
6557
6558STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6559{
6560 LogFlowThisFunc(("\n"));
6561
6562 CheckComArgNotNull(aWidth);
6563 CheckComArgNotNull(aHeight);
6564 CheckComArgOutSafeArrayPointerValid(aData);
6565
6566 if (aScreenId != 0)
6567 return E_NOTIMPL;
6568
6569 AutoCaller autoCaller(this);
6570 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6571
6572 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6573
6574 uint8_t *pu8Data = NULL;
6575 uint32_t cbData = 0;
6576 uint32_t u32Width = 0;
6577 uint32_t u32Height = 0;
6578
6579 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6580
6581 if (RT_FAILURE(vrc))
6582 return setError(VBOX_E_IPRT_ERROR,
6583 tr("Saved screenshot data is not available (%Rrc)"),
6584 vrc);
6585
6586 *aWidth = u32Width;
6587 *aHeight = u32Height;
6588
6589 HRESULT rc = S_OK;
6590 uint8_t *pu8PNG = NULL;
6591 uint32_t cbPNG = 0;
6592 uint32_t cxPNG = 0;
6593 uint32_t cyPNG = 0;
6594
6595 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6596
6597 if (RT_SUCCESS(vrc))
6598 {
6599 com::SafeArray<BYTE> screenData(cbPNG);
6600 screenData.initFrom(pu8PNG, cbPNG);
6601 if (pu8PNG)
6602 RTMemFree(pu8PNG);
6603 screenData.detachTo(ComSafeArrayOutArg(aData));
6604 }
6605 else
6606 {
6607 if (pu8PNG)
6608 RTMemFree(pu8PNG);
6609 return setError(VBOX_E_IPRT_ERROR,
6610 tr("Could not convert screenshot to PNG (%Rrc)"),
6611 vrc);
6612 }
6613
6614 freeSavedDisplayScreenshot(pu8Data);
6615
6616 return rc;
6617}
6618
6619STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6620{
6621 LogFlowThisFunc(("\n"));
6622
6623 CheckComArgNotNull(aSize);
6624 CheckComArgNotNull(aWidth);
6625 CheckComArgNotNull(aHeight);
6626
6627 if (aScreenId != 0)
6628 return E_NOTIMPL;
6629
6630 AutoCaller autoCaller(this);
6631 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6632
6633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6634
6635 uint8_t *pu8Data = NULL;
6636 uint32_t cbData = 0;
6637 uint32_t u32Width = 0;
6638 uint32_t u32Height = 0;
6639
6640 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6641
6642 if (RT_FAILURE(vrc))
6643 return setError(VBOX_E_IPRT_ERROR,
6644 tr("Saved screenshot data is not available (%Rrc)"),
6645 vrc);
6646
6647 *aSize = cbData;
6648 *aWidth = u32Width;
6649 *aHeight = u32Height;
6650
6651 freeSavedDisplayScreenshot(pu8Data);
6652
6653 return S_OK;
6654}
6655
6656STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6657{
6658 LogFlowThisFunc(("\n"));
6659
6660 CheckComArgNotNull(aWidth);
6661 CheckComArgNotNull(aHeight);
6662 CheckComArgOutSafeArrayPointerValid(aData);
6663
6664 if (aScreenId != 0)
6665 return E_NOTIMPL;
6666
6667 AutoCaller autoCaller(this);
6668 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6669
6670 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6671
6672 uint8_t *pu8Data = NULL;
6673 uint32_t cbData = 0;
6674 uint32_t u32Width = 0;
6675 uint32_t u32Height = 0;
6676
6677 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6678
6679 if (RT_FAILURE(vrc))
6680 return setError(VBOX_E_IPRT_ERROR,
6681 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6682 vrc);
6683
6684 *aWidth = u32Width;
6685 *aHeight = u32Height;
6686
6687 com::SafeArray<BYTE> png(cbData);
6688 png.initFrom(pu8Data, cbData);
6689 png.detachTo(ComSafeArrayOutArg(aData));
6690
6691 freeSavedDisplayScreenshot(pu8Data);
6692
6693 return S_OK;
6694}
6695
6696STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6697{
6698 HRESULT rc = S_OK;
6699 LogFlowThisFunc(("\n"));
6700
6701 AutoCaller autoCaller(this);
6702 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6703
6704 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6705
6706 if (!mHWData->mCPUHotPlugEnabled)
6707 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6708
6709 if (aCpu >= mHWData->mCPUCount)
6710 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6711
6712 if (mHWData->mCPUAttached[aCpu])
6713 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6714
6715 alock.release();
6716 rc = onCPUChange(aCpu, false);
6717 alock.acquire();
6718 if (FAILED(rc)) return rc;
6719
6720 setModified(IsModified_MachineData);
6721 mHWData.backup();
6722 mHWData->mCPUAttached[aCpu] = true;
6723
6724 /* Save settings if online */
6725 if (Global::IsOnline(mData->mMachineState))
6726 saveSettings(NULL);
6727
6728 return S_OK;
6729}
6730
6731STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6732{
6733 HRESULT rc = S_OK;
6734 LogFlowThisFunc(("\n"));
6735
6736 AutoCaller autoCaller(this);
6737 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6738
6739 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6740
6741 if (!mHWData->mCPUHotPlugEnabled)
6742 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6743
6744 if (aCpu >= SchemaDefs::MaxCPUCount)
6745 return setError(E_INVALIDARG,
6746 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6747 SchemaDefs::MaxCPUCount);
6748
6749 if (!mHWData->mCPUAttached[aCpu])
6750 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6751
6752 /* CPU 0 can't be detached */
6753 if (aCpu == 0)
6754 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6755
6756 alock.release();
6757 rc = onCPUChange(aCpu, true);
6758 alock.acquire();
6759 if (FAILED(rc)) return rc;
6760
6761 setModified(IsModified_MachineData);
6762 mHWData.backup();
6763 mHWData->mCPUAttached[aCpu] = false;
6764
6765 /* Save settings if online */
6766 if (Global::IsOnline(mData->mMachineState))
6767 saveSettings(NULL);
6768
6769 return S_OK;
6770}
6771
6772STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6773{
6774 LogFlowThisFunc(("\n"));
6775
6776 CheckComArgNotNull(aCpuAttached);
6777
6778 *aCpuAttached = false;
6779
6780 AutoCaller autoCaller(this);
6781 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6782
6783 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6784
6785 /* If hotplug is enabled the CPU is always enabled. */
6786 if (!mHWData->mCPUHotPlugEnabled)
6787 {
6788 if (aCpu < mHWData->mCPUCount)
6789 *aCpuAttached = true;
6790 }
6791 else
6792 {
6793 if (aCpu < SchemaDefs::MaxCPUCount)
6794 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6795 }
6796
6797 return S_OK;
6798}
6799
6800STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6801{
6802 CheckComArgOutPointerValid(aName);
6803
6804 AutoCaller autoCaller(this);
6805 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6806
6807 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6808
6809 Utf8Str log = queryLogFilename(aIdx);
6810 if (!RTFileExists(log.c_str()))
6811 log.setNull();
6812 log.cloneTo(aName);
6813
6814 return S_OK;
6815}
6816
6817STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6818{
6819 LogFlowThisFunc(("\n"));
6820 CheckComArgOutSafeArrayPointerValid(aData);
6821 if (aSize < 0)
6822 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6823
6824 AutoCaller autoCaller(this);
6825 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6826
6827 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6828
6829 HRESULT rc = S_OK;
6830 Utf8Str log = queryLogFilename(aIdx);
6831
6832 /* do not unnecessarily hold the lock while doing something which does
6833 * not need the lock and potentially takes a long time. */
6834 alock.release();
6835
6836 /* Limit the chunk size to 32K for now, as that gives better performance
6837 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6838 * One byte expands to approx. 25 bytes of breathtaking XML. */
6839 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6840 com::SafeArray<BYTE> logData(cbData);
6841
6842 RTFILE LogFile;
6843 int vrc = RTFileOpen(&LogFile, log.c_str(),
6844 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6845 if (RT_SUCCESS(vrc))
6846 {
6847 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6848 if (RT_SUCCESS(vrc))
6849 logData.resize(cbData);
6850 else
6851 rc = setError(VBOX_E_IPRT_ERROR,
6852 tr("Could not read log file '%s' (%Rrc)"),
6853 log.c_str(), vrc);
6854 RTFileClose(LogFile);
6855 }
6856 else
6857 rc = setError(VBOX_E_IPRT_ERROR,
6858 tr("Could not open log file '%s' (%Rrc)"),
6859 log.c_str(), vrc);
6860
6861 if (FAILED(rc))
6862 logData.resize(0);
6863 logData.detachTo(ComSafeArrayOutArg(aData));
6864
6865 return rc;
6866}
6867
6868
6869/**
6870 * Currently this method doesn't attach device to the running VM,
6871 * just makes sure it's plugged on next VM start.
6872 */
6873STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6874{
6875 AutoCaller autoCaller(this);
6876 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6877
6878 // lock scope
6879 {
6880 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6881
6882 HRESULT rc = checkStateDependency(MutableStateDep);
6883 if (FAILED(rc)) return rc;
6884
6885 ChipsetType_T aChipset = ChipsetType_PIIX3;
6886 COMGETTER(ChipsetType)(&aChipset);
6887
6888 if (aChipset != ChipsetType_ICH9)
6889 {
6890 return setError(E_INVALIDARG,
6891 tr("Host PCI attachment only supported with ICH9 chipset"));
6892 }
6893
6894 // check if device with this host PCI address already attached
6895 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6896 it != mHWData->mPCIDeviceAssignments.end();
6897 ++it)
6898 {
6899 LONG iHostAddress = -1;
6900 ComPtr<PCIDeviceAttachment> pAttach;
6901 pAttach = *it;
6902 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6903 if (iHostAddress == hostAddress)
6904 return setError(E_INVALIDARG,
6905 tr("Device with host PCI address already attached to this VM"));
6906 }
6907
6908 ComObjPtr<PCIDeviceAttachment> pda;
6909 char name[32];
6910
6911 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6912 Bstr bname(name);
6913 pda.createObject();
6914 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
6915 setModified(IsModified_MachineData);
6916 mHWData.backup();
6917 mHWData->mPCIDeviceAssignments.push_back(pda);
6918 }
6919
6920 return S_OK;
6921}
6922
6923/**
6924 * Currently this method doesn't detach device from the running VM,
6925 * just makes sure it's not plugged on next VM start.
6926 */
6927STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
6928{
6929 AutoCaller autoCaller(this);
6930 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6931
6932 ComObjPtr<PCIDeviceAttachment> pAttach;
6933 bool fRemoved = false;
6934 HRESULT rc;
6935
6936 // lock scope
6937 {
6938 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6939
6940 rc = checkStateDependency(MutableStateDep);
6941 if (FAILED(rc)) return rc;
6942
6943 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6944 it != mHWData->mPCIDeviceAssignments.end();
6945 ++it)
6946 {
6947 LONG iHostAddress = -1;
6948 pAttach = *it;
6949 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6950 if (iHostAddress != -1 && iHostAddress == hostAddress)
6951 {
6952 setModified(IsModified_MachineData);
6953 mHWData.backup();
6954 mHWData->mPCIDeviceAssignments.remove(pAttach);
6955 fRemoved = true;
6956 break;
6957 }
6958 }
6959 }
6960
6961
6962 /* Fire event outside of the lock */
6963 if (fRemoved)
6964 {
6965 Assert(!pAttach.isNull());
6966 ComPtr<IEventSource> es;
6967 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6968 Assert(SUCCEEDED(rc));
6969 Bstr mid;
6970 rc = this->COMGETTER(Id)(mid.asOutParam());
6971 Assert(SUCCEEDED(rc));
6972 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6973 }
6974
6975 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6976 tr("No host PCI device %08x attached"),
6977 hostAddress
6978 );
6979}
6980
6981STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
6982{
6983 CheckComArgOutSafeArrayPointerValid(aAssignments);
6984
6985 AutoCaller autoCaller(this);
6986 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6987
6988 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6989
6990 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
6991 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
6992
6993 return S_OK;
6994}
6995
6996STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
6997{
6998 CheckComArgOutPointerValid(aBandwidthControl);
6999
7000 AutoCaller autoCaller(this);
7001 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7002
7003 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
7004
7005 return S_OK;
7006}
7007
7008STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
7009{
7010 CheckComArgOutPointerValid(pfEnabled);
7011 AutoCaller autoCaller(this);
7012 HRESULT hrc = autoCaller.rc();
7013 if (SUCCEEDED(hrc))
7014 {
7015 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7016 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
7017 }
7018 return hrc;
7019}
7020
7021STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
7022{
7023 AutoCaller autoCaller(this);
7024 HRESULT hrc = autoCaller.rc();
7025 if (SUCCEEDED(hrc))
7026 {
7027 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7028 hrc = checkStateDependency(MutableStateDep);
7029 if (SUCCEEDED(hrc))
7030 {
7031 hrc = mHWData.backupEx();
7032 if (SUCCEEDED(hrc))
7033 {
7034 setModified(IsModified_MachineData);
7035 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
7036 }
7037 }
7038 }
7039 return hrc;
7040}
7041
7042STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
7043{
7044 CheckComArgOutPointerValid(pbstrConfig);
7045 AutoCaller autoCaller(this);
7046 HRESULT hrc = autoCaller.rc();
7047 if (SUCCEEDED(hrc))
7048 {
7049 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7050 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
7051 }
7052 return hrc;
7053}
7054
7055STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
7056{
7057 CheckComArgStr(bstrConfig);
7058 AutoCaller autoCaller(this);
7059 HRESULT hrc = autoCaller.rc();
7060 if (SUCCEEDED(hrc))
7061 {
7062 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7063 hrc = checkStateDependency(MutableStateDep);
7064 if (SUCCEEDED(hrc))
7065 {
7066 hrc = mHWData.backupEx();
7067 if (SUCCEEDED(hrc))
7068 {
7069 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
7070 if (SUCCEEDED(hrc))
7071 setModified(IsModified_MachineData);
7072 }
7073 }
7074 }
7075 return hrc;
7076
7077}
7078
7079STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
7080{
7081 CheckComArgOutPointerValid(pfAllow);
7082 AutoCaller autoCaller(this);
7083 HRESULT hrc = autoCaller.rc();
7084 if (SUCCEEDED(hrc))
7085 {
7086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7087 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
7088 }
7089 return hrc;
7090}
7091
7092STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
7093{
7094 AutoCaller autoCaller(this);
7095 HRESULT hrc = autoCaller.rc();
7096 if (SUCCEEDED(hrc))
7097 {
7098 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7099 hrc = checkStateDependency(MutableStateDep);
7100 if (SUCCEEDED(hrc))
7101 {
7102 hrc = mHWData.backupEx();
7103 if (SUCCEEDED(hrc))
7104 {
7105 setModified(IsModified_MachineData);
7106 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
7107 }
7108 }
7109 }
7110 return hrc;
7111}
7112
7113STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
7114{
7115 CheckComArgOutPointerValid(pfEnabled);
7116 AutoCaller autoCaller(this);
7117 HRESULT hrc = autoCaller.rc();
7118 if (SUCCEEDED(hrc))
7119 {
7120 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7121 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
7122 }
7123 return hrc;
7124}
7125
7126STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
7127{
7128 AutoCaller autoCaller(this);
7129 HRESULT hrc = autoCaller.rc();
7130 if (SUCCEEDED(hrc))
7131 {
7132 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7133 hrc = checkStateDependency(MutableStateDep);
7134 if ( SUCCEEDED(hrc)
7135 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
7136 {
7137 AutostartDb *autostartDb = mParent->getAutostartDb();
7138 int vrc;
7139
7140 if (fEnabled)
7141 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7142 else
7143 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7144
7145 if (RT_SUCCESS(vrc))
7146 {
7147 hrc = mHWData.backupEx();
7148 if (SUCCEEDED(hrc))
7149 {
7150 setModified(IsModified_MachineData);
7151 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
7152 }
7153 }
7154 else if (vrc == VERR_NOT_SUPPORTED)
7155 hrc = setError(VBOX_E_NOT_SUPPORTED,
7156 tr("The VM autostart feature is not supported on this platform"));
7157 else if (vrc == VERR_PATH_NOT_FOUND)
7158 hrc = setError(E_FAIL,
7159 tr("The path to the autostart database is not set"));
7160 else
7161 hrc = setError(E_UNEXPECTED,
7162 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7163 fEnabled ? "Adding" : "Removing",
7164 mUserData->s.strName.c_str(), vrc);
7165 }
7166 }
7167 return hrc;
7168}
7169
7170STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
7171{
7172 CheckComArgOutPointerValid(puDelay);
7173 AutoCaller autoCaller(this);
7174 HRESULT hrc = autoCaller.rc();
7175 if (SUCCEEDED(hrc))
7176 {
7177 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7178 *puDelay = mHWData->mAutostart.uAutostartDelay;
7179 }
7180 return hrc;
7181}
7182
7183STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
7184{
7185 AutoCaller autoCaller(this);
7186 HRESULT hrc = autoCaller.rc();
7187 if (SUCCEEDED(hrc))
7188 {
7189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7190 hrc = checkStateDependency(MutableStateDep);
7191 if (SUCCEEDED(hrc))
7192 {
7193 hrc = mHWData.backupEx();
7194 if (SUCCEEDED(hrc))
7195 {
7196 setModified(IsModified_MachineData);
7197 mHWData->mAutostart.uAutostartDelay = uDelay;
7198 }
7199 }
7200 }
7201 return hrc;
7202}
7203
7204STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
7205{
7206 CheckComArgOutPointerValid(penmAutostopType);
7207 AutoCaller autoCaller(this);
7208 HRESULT hrc = autoCaller.rc();
7209 if (SUCCEEDED(hrc))
7210 {
7211 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7212 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
7213 }
7214 return hrc;
7215}
7216
7217STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
7218{
7219 AutoCaller autoCaller(this);
7220 HRESULT hrc = autoCaller.rc();
7221 if (SUCCEEDED(hrc))
7222 {
7223 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7224 hrc = checkStateDependency(MutableStateDep);
7225 if ( SUCCEEDED(hrc)
7226 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
7227 {
7228 AutostartDb *autostartDb = mParent->getAutostartDb();
7229 int vrc;
7230
7231 if (enmAutostopType != AutostopType_Disabled)
7232 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7233 else
7234 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7235
7236 if (RT_SUCCESS(vrc))
7237 {
7238 hrc = mHWData.backupEx();
7239 if (SUCCEEDED(hrc))
7240 {
7241 setModified(IsModified_MachineData);
7242 mHWData->mAutostart.enmAutostopType = enmAutostopType;
7243 }
7244 }
7245 else if (vrc == VERR_NOT_SUPPORTED)
7246 hrc = setError(VBOX_E_NOT_SUPPORTED,
7247 tr("The VM autostop feature is not supported on this platform"));
7248 else if (vrc == VERR_PATH_NOT_FOUND)
7249 hrc = setError(E_FAIL,
7250 tr("The path to the autostart database is not set"));
7251 else
7252 hrc = setError(E_UNEXPECTED,
7253 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7254 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7255 mUserData->s.strName.c_str(), vrc);
7256 }
7257 }
7258 return hrc;
7259}
7260
7261STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7262{
7263 CheckComArgOutPointerValid(aDefaultFrontend);
7264 AutoCaller autoCaller(this);
7265 HRESULT hrc = autoCaller.rc();
7266 if (SUCCEEDED(hrc))
7267 {
7268 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7269 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7270 }
7271 return hrc;
7272}
7273
7274STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7275{
7276 CheckComArgStr(aDefaultFrontend);
7277 AutoCaller autoCaller(this);
7278 HRESULT hrc = autoCaller.rc();
7279 if (SUCCEEDED(hrc))
7280 {
7281 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7282 hrc = checkStateDependency(MutableOrSavedStateDep);
7283 if (SUCCEEDED(hrc))
7284 {
7285 hrc = mHWData.backupEx();
7286 if (SUCCEEDED(hrc))
7287 {
7288 setModified(IsModified_MachineData);
7289 mHWData->mDefaultFrontend = aDefaultFrontend;
7290 }
7291 }
7292 }
7293 return hrc;
7294}
7295
7296
7297STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7298{
7299 LogFlowFuncEnter();
7300
7301 CheckComArgNotNull(pTarget);
7302 CheckComArgOutPointerValid(pProgress);
7303
7304 /* Convert the options. */
7305 RTCList<CloneOptions_T> optList;
7306 if (options != NULL)
7307 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7308
7309 if (optList.contains(CloneOptions_Link))
7310 {
7311 if (!isSnapshotMachine())
7312 return setError(E_INVALIDARG,
7313 tr("Linked clone can only be created from a snapshot"));
7314 if (mode != CloneMode_MachineState)
7315 return setError(E_INVALIDARG,
7316 tr("Linked clone can only be created for a single machine state"));
7317 }
7318 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7319
7320 AutoCaller autoCaller(this);
7321 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7322
7323
7324 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7325
7326 HRESULT rc = pWorker->start(pProgress);
7327
7328 LogFlowFuncLeave();
7329
7330 return rc;
7331}
7332
7333// public methods for internal purposes
7334/////////////////////////////////////////////////////////////////////////////
7335
7336/**
7337 * Adds the given IsModified_* flag to the dirty flags of the machine.
7338 * This must be called either during loadSettings or under the machine write lock.
7339 * @param fl
7340 */
7341void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7342{
7343 mData->flModifications |= fl;
7344 if (fAllowStateModification && isStateModificationAllowed())
7345 mData->mCurrentStateModified = true;
7346}
7347
7348/**
7349 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7350 * care of the write locking.
7351 *
7352 * @param fModifications The flag to add.
7353 */
7354void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7355{
7356 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7357 setModified(fModification, fAllowStateModification);
7358}
7359
7360/**
7361 * Saves the registry entry of this machine to the given configuration node.
7362 *
7363 * @param aEntryNode Node to save the registry entry to.
7364 *
7365 * @note locks this object for reading.
7366 */
7367HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7368{
7369 AutoLimitedCaller autoCaller(this);
7370 AssertComRCReturnRC(autoCaller.rc());
7371
7372 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7373
7374 data.uuid = mData->mUuid;
7375 data.strSettingsFile = mData->m_strConfigFile;
7376
7377 return S_OK;
7378}
7379
7380/**
7381 * Calculates the absolute path of the given path taking the directory of the
7382 * machine settings file as the current directory.
7383 *
7384 * @param aPath Path to calculate the absolute path for.
7385 * @param aResult Where to put the result (used only on success, can be the
7386 * same Utf8Str instance as passed in @a aPath).
7387 * @return IPRT result.
7388 *
7389 * @note Locks this object for reading.
7390 */
7391int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7392{
7393 AutoCaller autoCaller(this);
7394 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7395
7396 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7397
7398 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7399
7400 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7401
7402 strSettingsDir.stripFilename();
7403 char folder[RTPATH_MAX];
7404 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7405 if (RT_SUCCESS(vrc))
7406 aResult = folder;
7407
7408 return vrc;
7409}
7410
7411/**
7412 * Copies strSource to strTarget, making it relative to the machine folder
7413 * if it is a subdirectory thereof, or simply copying it otherwise.
7414 *
7415 * @param strSource Path to evaluate and copy.
7416 * @param strTarget Buffer to receive target path.
7417 *
7418 * @note Locks this object for reading.
7419 */
7420void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7421 Utf8Str &strTarget)
7422{
7423 AutoCaller autoCaller(this);
7424 AssertComRCReturn(autoCaller.rc(), (void)0);
7425
7426 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7427
7428 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7429 // use strTarget as a temporary buffer to hold the machine settings dir
7430 strTarget = mData->m_strConfigFileFull;
7431 strTarget.stripFilename();
7432 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7433 {
7434 // is relative: then append what's left
7435 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7436 // for empty paths (only possible for subdirs) use "." to avoid
7437 // triggering default settings for not present config attributes.
7438 if (strTarget.isEmpty())
7439 strTarget = ".";
7440 }
7441 else
7442 // is not relative: then overwrite
7443 strTarget = strSource;
7444}
7445
7446/**
7447 * Returns the full path to the machine's log folder in the
7448 * \a aLogFolder argument.
7449 */
7450void Machine::getLogFolder(Utf8Str &aLogFolder)
7451{
7452 AutoCaller autoCaller(this);
7453 AssertComRCReturnVoid(autoCaller.rc());
7454
7455 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7456
7457 char szTmp[RTPATH_MAX];
7458 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7459 if (RT_SUCCESS(vrc))
7460 {
7461 if (szTmp[0] && !mUserData.isNull())
7462 {
7463 char szTmp2[RTPATH_MAX];
7464 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7465 if (RT_SUCCESS(vrc))
7466 aLogFolder = BstrFmt("%s%c%s",
7467 szTmp2,
7468 RTPATH_DELIMITER,
7469 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7470 }
7471 else
7472 vrc = VERR_PATH_IS_RELATIVE;
7473 }
7474
7475 if (RT_FAILURE(vrc))
7476 {
7477 // fallback if VBOX_USER_LOGHOME is not set or invalid
7478 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7479 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7480 aLogFolder.append(RTPATH_DELIMITER);
7481 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7482 }
7483}
7484
7485/**
7486 * Returns the full path to the machine's log file for an given index.
7487 */
7488Utf8Str Machine::queryLogFilename(ULONG idx)
7489{
7490 Utf8Str logFolder;
7491 getLogFolder(logFolder);
7492 Assert(logFolder.length());
7493 Utf8Str log;
7494 if (idx == 0)
7495 log = Utf8StrFmt("%s%cVBox.log",
7496 logFolder.c_str(), RTPATH_DELIMITER);
7497 else
7498 log = Utf8StrFmt("%s%cVBox.log.%d",
7499 logFolder.c_str(), RTPATH_DELIMITER, idx);
7500 return log;
7501}
7502
7503/**
7504 * Composes a unique saved state filename based on the current system time. The filename is
7505 * granular to the second so this will work so long as no more than one snapshot is taken on
7506 * a machine per second.
7507 *
7508 * Before version 4.1, we used this formula for saved state files:
7509 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7510 * which no longer works because saved state files can now be shared between the saved state of the
7511 * "saved" machine and an online snapshot, and the following would cause problems:
7512 * 1) save machine
7513 * 2) create online snapshot from that machine state --> reusing saved state file
7514 * 3) save machine again --> filename would be reused, breaking the online snapshot
7515 *
7516 * So instead we now use a timestamp.
7517 *
7518 * @param str
7519 */
7520void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7521{
7522 AutoCaller autoCaller(this);
7523 AssertComRCReturnVoid(autoCaller.rc());
7524
7525 {
7526 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7527 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7528 }
7529
7530 RTTIMESPEC ts;
7531 RTTimeNow(&ts);
7532 RTTIME time;
7533 RTTimeExplode(&time, &ts);
7534
7535 strStateFilePath += RTPATH_DELIMITER;
7536 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7537 time.i32Year, time.u8Month, time.u8MonthDay,
7538 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7539}
7540
7541/**
7542 * @note Locks this object for writing, calls the client process
7543 * (inside the lock).
7544 */
7545HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7546 const Utf8Str &strFrontend,
7547 const Utf8Str &strEnvironment,
7548 ProgressProxy *aProgress)
7549{
7550 LogFlowThisFuncEnter();
7551
7552 AssertReturn(aControl, E_FAIL);
7553 AssertReturn(aProgress, E_FAIL);
7554
7555 AutoCaller autoCaller(this);
7556 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7557
7558 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7559
7560 if (!mData->mRegistered)
7561 return setError(E_UNEXPECTED,
7562 tr("The machine '%s' is not registered"),
7563 mUserData->s.strName.c_str());
7564
7565 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7566
7567 if ( mData->mSession.mState == SessionState_Locked
7568 || mData->mSession.mState == SessionState_Spawning
7569 || mData->mSession.mState == SessionState_Unlocking)
7570 return setError(VBOX_E_INVALID_OBJECT_STATE,
7571 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7572 mUserData->s.strName.c_str());
7573
7574 /* may not be busy */
7575 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7576
7577 /* get the path to the executable */
7578 char szPath[RTPATH_MAX];
7579 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7580 size_t sz = strlen(szPath);
7581 szPath[sz++] = RTPATH_DELIMITER;
7582 szPath[sz] = 0;
7583 char *cmd = szPath + sz;
7584 sz = RTPATH_MAX - sz;
7585
7586 int vrc = VINF_SUCCESS;
7587 RTPROCESS pid = NIL_RTPROCESS;
7588
7589 RTENV env = RTENV_DEFAULT;
7590
7591 if (!strEnvironment.isEmpty())
7592 {
7593 char *newEnvStr = NULL;
7594
7595 do
7596 {
7597 /* clone the current environment */
7598 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7599 AssertRCBreakStmt(vrc2, vrc = vrc2);
7600
7601 newEnvStr = RTStrDup(strEnvironment.c_str());
7602 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7603
7604 /* put new variables to the environment
7605 * (ignore empty variable names here since RTEnv API
7606 * intentionally doesn't do that) */
7607 char *var = newEnvStr;
7608 for (char *p = newEnvStr; *p; ++p)
7609 {
7610 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7611 {
7612 *p = '\0';
7613 if (*var)
7614 {
7615 char *val = strchr(var, '=');
7616 if (val)
7617 {
7618 *val++ = '\0';
7619 vrc2 = RTEnvSetEx(env, var, val);
7620 }
7621 else
7622 vrc2 = RTEnvUnsetEx(env, var);
7623 if (RT_FAILURE(vrc2))
7624 break;
7625 }
7626 var = p + 1;
7627 }
7628 }
7629 if (RT_SUCCESS(vrc2) && *var)
7630 vrc2 = RTEnvPutEx(env, var);
7631
7632 AssertRCBreakStmt(vrc2, vrc = vrc2);
7633 }
7634 while (0);
7635
7636 if (newEnvStr != NULL)
7637 RTStrFree(newEnvStr);
7638 }
7639
7640 /* Qt is default */
7641#ifdef VBOX_WITH_QTGUI
7642 if (strFrontend == "gui" || strFrontend == "GUI/Qt" || strFrontend == "")
7643 {
7644# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7645 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7646# else
7647 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7648# endif
7649 Assert(sz >= sizeof(VirtualBox_exe));
7650 strcpy(cmd, VirtualBox_exe);
7651
7652 Utf8Str idStr = mData->mUuid.toString();
7653 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7654 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7655 }
7656#else /* !VBOX_WITH_QTGUI */
7657 if (0)
7658 ;
7659#endif /* VBOX_WITH_QTGUI */
7660
7661 else
7662
7663#ifdef VBOX_WITH_VBOXSDL
7664 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7665 {
7666 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7667 Assert(sz >= sizeof(VBoxSDL_exe));
7668 strcpy(cmd, VBoxSDL_exe);
7669
7670 Utf8Str idStr = mData->mUuid.toString();
7671 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7672 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7673 }
7674#else /* !VBOX_WITH_VBOXSDL */
7675 if (0)
7676 ;
7677#endif /* !VBOX_WITH_VBOXSDL */
7678
7679 else
7680
7681#ifdef VBOX_WITH_HEADLESS
7682 if ( strFrontend == "headless"
7683 || strFrontend == "capture"
7684 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7685 )
7686 {
7687 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7688 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7689 * and a VM works even if the server has not been installed.
7690 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7691 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7692 * differently in 4.0 and 3.x.
7693 */
7694 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7695 Assert(sz >= sizeof(VBoxHeadless_exe));
7696 strcpy(cmd, VBoxHeadless_exe);
7697
7698 Utf8Str idStr = mData->mUuid.toString();
7699 /* Leave space for "--capture" arg. */
7700 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7701 "--startvm", idStr.c_str(),
7702 "--vrde", "config",
7703 0, /* For "--capture". */
7704 0 };
7705 if (strFrontend == "capture")
7706 {
7707 unsigned pos = RT_ELEMENTS(args) - 2;
7708 args[pos] = "--capture";
7709 }
7710 vrc = RTProcCreate(szPath, args, env,
7711#ifdef RT_OS_WINDOWS
7712 RTPROC_FLAGS_NO_WINDOW
7713#else
7714 0
7715#endif
7716 , &pid);
7717 }
7718#else /* !VBOX_WITH_HEADLESS */
7719 if (0)
7720 ;
7721#endif /* !VBOX_WITH_HEADLESS */
7722 else
7723 {
7724 RTEnvDestroy(env);
7725 return setError(E_INVALIDARG,
7726 tr("Invalid frontend name: '%s'"),
7727 strFrontend.c_str());
7728 }
7729
7730 RTEnvDestroy(env);
7731
7732 if (RT_FAILURE(vrc))
7733 return setError(VBOX_E_IPRT_ERROR,
7734 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7735 mUserData->s.strName.c_str(), vrc);
7736
7737 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7738
7739 /*
7740 * Note that we don't release the lock here before calling the client,
7741 * because it doesn't need to call us back if called with a NULL argument.
7742 * Releasing the lock here is dangerous because we didn't prepare the
7743 * launch data yet, but the client we've just started may happen to be
7744 * too fast and call openSession() that will fail (because of PID, etc.),
7745 * so that the Machine will never get out of the Spawning session state.
7746 */
7747
7748 /* inform the session that it will be a remote one */
7749 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7750 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
7751 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7752
7753 if (FAILED(rc))
7754 {
7755 /* restore the session state */
7756 mData->mSession.mState = SessionState_Unlocked;
7757 /* The failure may occur w/o any error info (from RPC), so provide one */
7758 return setError(VBOX_E_VM_ERROR,
7759 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7760 }
7761
7762 /* attach launch data to the machine */
7763 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7764 mData->mSession.mRemoteControls.push_back(aControl);
7765 mData->mSession.mProgress = aProgress;
7766 mData->mSession.mPID = pid;
7767 mData->mSession.mState = SessionState_Spawning;
7768 mData->mSession.mType = strFrontend;
7769
7770 LogFlowThisFuncLeave();
7771 return S_OK;
7772}
7773
7774/**
7775 * Returns @c true if the given machine has an open direct session and returns
7776 * the session machine instance and additional session data (on some platforms)
7777 * if so.
7778 *
7779 * Note that when the method returns @c false, the arguments remain unchanged.
7780 *
7781 * @param aMachine Session machine object.
7782 * @param aControl Direct session control object (optional).
7783 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7784 *
7785 * @note locks this object for reading.
7786 */
7787#if defined(RT_OS_WINDOWS)
7788bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7789 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7790 HANDLE *aIPCSem /*= NULL*/,
7791 bool aAllowClosing /*= false*/)
7792#elif defined(RT_OS_OS2)
7793bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7794 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7795 HMTX *aIPCSem /*= NULL*/,
7796 bool aAllowClosing /*= false*/)
7797#else
7798bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7799 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7800 bool aAllowClosing /*= false*/)
7801#endif
7802{
7803 AutoLimitedCaller autoCaller(this);
7804 AssertComRCReturn(autoCaller.rc(), false);
7805
7806 /* just return false for inaccessible machines */
7807 if (autoCaller.state() != Ready)
7808 return false;
7809
7810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7811
7812 if ( mData->mSession.mState == SessionState_Locked
7813 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7814 )
7815 {
7816 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7817
7818 aMachine = mData->mSession.mMachine;
7819
7820 if (aControl != NULL)
7821 *aControl = mData->mSession.mDirectControl;
7822
7823#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7824 /* Additional session data */
7825 if (aIPCSem != NULL)
7826 *aIPCSem = aMachine->mIPCSem;
7827#endif
7828 return true;
7829 }
7830
7831 return false;
7832}
7833
7834/**
7835 * Returns @c true if the given machine has an spawning direct session and
7836 * returns and additional session data (on some platforms) if so.
7837 *
7838 * Note that when the method returns @c false, the arguments remain unchanged.
7839 *
7840 * @param aPID PID of the spawned direct session process.
7841 *
7842 * @note locks this object for reading.
7843 */
7844#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7845bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7846#else
7847bool Machine::isSessionSpawning()
7848#endif
7849{
7850 AutoLimitedCaller autoCaller(this);
7851 AssertComRCReturn(autoCaller.rc(), false);
7852
7853 /* just return false for inaccessible machines */
7854 if (autoCaller.state() != Ready)
7855 return false;
7856
7857 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7858
7859 if (mData->mSession.mState == SessionState_Spawning)
7860 {
7861#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7862 /* Additional session data */
7863 if (aPID != NULL)
7864 {
7865 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
7866 *aPID = mData->mSession.mPID;
7867 }
7868#endif
7869 return true;
7870 }
7871
7872 return false;
7873}
7874
7875/**
7876 * Called from the client watcher thread to check for unexpected client process
7877 * death during Session_Spawning state (e.g. before it successfully opened a
7878 * direct session).
7879 *
7880 * On Win32 and on OS/2, this method is called only when we've got the
7881 * direct client's process termination notification, so it always returns @c
7882 * true.
7883 *
7884 * On other platforms, this method returns @c true if the client process is
7885 * terminated and @c false if it's still alive.
7886 *
7887 * @note Locks this object for writing.
7888 */
7889bool Machine::checkForSpawnFailure()
7890{
7891 AutoCaller autoCaller(this);
7892 if (!autoCaller.isOk())
7893 {
7894 /* nothing to do */
7895 LogFlowThisFunc(("Already uninitialized!\n"));
7896 return true;
7897 }
7898
7899 /* VirtualBox::addProcessToReap() needs a write lock */
7900 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
7901
7902 if (mData->mSession.mState != SessionState_Spawning)
7903 {
7904 /* nothing to do */
7905 LogFlowThisFunc(("Not spawning any more!\n"));
7906 return true;
7907 }
7908
7909 HRESULT rc = S_OK;
7910
7911#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7912
7913 /* the process was already unexpectedly terminated, we just need to set an
7914 * error and finalize session spawning */
7915 rc = setError(E_FAIL,
7916 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
7917 getName().c_str());
7918#else
7919
7920 /* PID not yet initialized, skip check. */
7921 if (mData->mSession.mPID == NIL_RTPROCESS)
7922 return false;
7923
7924 RTPROCSTATUS status;
7925 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
7926 &status);
7927
7928 if (vrc != VERR_PROCESS_RUNNING)
7929 {
7930 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7931 rc = setError(E_FAIL,
7932 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7933 getName().c_str(), status.iStatus);
7934 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7935 rc = setError(E_FAIL,
7936 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7937 getName().c_str(), status.iStatus);
7938 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7939 rc = setError(E_FAIL,
7940 tr("The virtual machine '%s' has terminated abnormally"),
7941 getName().c_str(), status.iStatus);
7942 else
7943 rc = setError(E_FAIL,
7944 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7945 getName().c_str(), rc);
7946 }
7947
7948#endif
7949
7950 if (FAILED(rc))
7951 {
7952 /* Close the remote session, remove the remote control from the list
7953 * and reset session state to Closed (@note keep the code in sync with
7954 * the relevant part in checkForSpawnFailure()). */
7955
7956 Assert(mData->mSession.mRemoteControls.size() == 1);
7957 if (mData->mSession.mRemoteControls.size() == 1)
7958 {
7959 ErrorInfoKeeper eik;
7960 mData->mSession.mRemoteControls.front()->Uninitialize();
7961 }
7962
7963 mData->mSession.mRemoteControls.clear();
7964 mData->mSession.mState = SessionState_Unlocked;
7965
7966 /* finalize the progress after setting the state */
7967 if (!mData->mSession.mProgress.isNull())
7968 {
7969 mData->mSession.mProgress->notifyComplete(rc);
7970 mData->mSession.mProgress.setNull();
7971 }
7972
7973 mParent->addProcessToReap(mData->mSession.mPID);
7974 mData->mSession.mPID = NIL_RTPROCESS;
7975
7976 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7977 return true;
7978 }
7979
7980 return false;
7981}
7982
7983/**
7984 * Checks whether the machine can be registered. If so, commits and saves
7985 * all settings.
7986 *
7987 * @note Must be called from mParent's write lock. Locks this object and
7988 * children for writing.
7989 */
7990HRESULT Machine::prepareRegister()
7991{
7992 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7993
7994 AutoLimitedCaller autoCaller(this);
7995 AssertComRCReturnRC(autoCaller.rc());
7996
7997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7998
7999 /* wait for state dependents to drop to zero */
8000 ensureNoStateDependencies();
8001
8002 if (!mData->mAccessible)
8003 return setError(VBOX_E_INVALID_OBJECT_STATE,
8004 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8005 mUserData->s.strName.c_str(),
8006 mData->mUuid.toString().c_str());
8007
8008 AssertReturn(autoCaller.state() == Ready, E_FAIL);
8009
8010 if (mData->mRegistered)
8011 return setError(VBOX_E_INVALID_OBJECT_STATE,
8012 tr("The machine '%s' with UUID {%s} is already registered"),
8013 mUserData->s.strName.c_str(),
8014 mData->mUuid.toString().c_str());
8015
8016 HRESULT rc = S_OK;
8017
8018 // Ensure the settings are saved. If we are going to be registered and
8019 // no config file exists yet, create it by calling saveSettings() too.
8020 if ( (mData->flModifications)
8021 || (!mData->pMachineConfigFile->fileExists())
8022 )
8023 {
8024 rc = saveSettings(NULL);
8025 // no need to check whether VirtualBox.xml needs saving too since
8026 // we can't have a machine XML file rename pending
8027 if (FAILED(rc)) return rc;
8028 }
8029
8030 /* more config checking goes here */
8031
8032 if (SUCCEEDED(rc))
8033 {
8034 /* we may have had implicit modifications we want to fix on success */
8035 commit();
8036
8037 mData->mRegistered = true;
8038 }
8039 else
8040 {
8041 /* we may have had implicit modifications we want to cancel on failure*/
8042 rollback(false /* aNotify */);
8043 }
8044
8045 return rc;
8046}
8047
8048/**
8049 * Increases the number of objects dependent on the machine state or on the
8050 * registered state. Guarantees that these two states will not change at least
8051 * until #releaseStateDependency() is called.
8052 *
8053 * Depending on the @a aDepType value, additional state checks may be made.
8054 * These checks will set extended error info on failure. See
8055 * #checkStateDependency() for more info.
8056 *
8057 * If this method returns a failure, the dependency is not added and the caller
8058 * is not allowed to rely on any particular machine state or registration state
8059 * value and may return the failed result code to the upper level.
8060 *
8061 * @param aDepType Dependency type to add.
8062 * @param aState Current machine state (NULL if not interested).
8063 * @param aRegistered Current registered state (NULL if not interested).
8064 *
8065 * @note Locks this object for writing.
8066 */
8067HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8068 MachineState_T *aState /* = NULL */,
8069 BOOL *aRegistered /* = NULL */)
8070{
8071 AutoCaller autoCaller(this);
8072 AssertComRCReturnRC(autoCaller.rc());
8073
8074 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8075
8076 HRESULT rc = checkStateDependency(aDepType);
8077 if (FAILED(rc)) return rc;
8078
8079 {
8080 if (mData->mMachineStateChangePending != 0)
8081 {
8082 /* ensureNoStateDependencies() is waiting for state dependencies to
8083 * drop to zero so don't add more. It may make sense to wait a bit
8084 * and retry before reporting an error (since the pending state
8085 * transition should be really quick) but let's just assert for
8086 * now to see if it ever happens on practice. */
8087
8088 AssertFailed();
8089
8090 return setError(E_ACCESSDENIED,
8091 tr("Machine state change is in progress. Please retry the operation later."));
8092 }
8093
8094 ++mData->mMachineStateDeps;
8095 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8096 }
8097
8098 if (aState)
8099 *aState = mData->mMachineState;
8100 if (aRegistered)
8101 *aRegistered = mData->mRegistered;
8102
8103 return S_OK;
8104}
8105
8106/**
8107 * Decreases the number of objects dependent on the machine state.
8108 * Must always complete the #addStateDependency() call after the state
8109 * dependency is no more necessary.
8110 */
8111void Machine::releaseStateDependency()
8112{
8113 AutoCaller autoCaller(this);
8114 AssertComRCReturnVoid(autoCaller.rc());
8115
8116 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8117
8118 /* releaseStateDependency() w/o addStateDependency()? */
8119 AssertReturnVoid(mData->mMachineStateDeps != 0);
8120 -- mData->mMachineStateDeps;
8121
8122 if (mData->mMachineStateDeps == 0)
8123 {
8124 /* inform ensureNoStateDependencies() that there are no more deps */
8125 if (mData->mMachineStateChangePending != 0)
8126 {
8127 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8128 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8129 }
8130 }
8131}
8132
8133Utf8Str Machine::getExtraData(const Utf8Str &strKey)
8134{
8135 /* start with nothing found */
8136 Utf8Str strResult("");
8137
8138 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8139
8140 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8141 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8142 // found:
8143 strResult = it->second; // source is a Utf8Str
8144
8145 return strResult;
8146}
8147
8148// protected methods
8149/////////////////////////////////////////////////////////////////////////////
8150
8151/**
8152 * Performs machine state checks based on the @a aDepType value. If a check
8153 * fails, this method will set extended error info, otherwise it will return
8154 * S_OK. It is supposed, that on failure, the caller will immediately return
8155 * the return value of this method to the upper level.
8156 *
8157 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8158 *
8159 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8160 * current state of this machine object allows to change settings of the
8161 * machine (i.e. the machine is not registered, or registered but not running
8162 * and not saved). It is useful to call this method from Machine setters
8163 * before performing any change.
8164 *
8165 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8166 * as for MutableStateDep except that if the machine is saved, S_OK is also
8167 * returned. This is useful in setters which allow changing machine
8168 * properties when it is in the saved state.
8169 *
8170 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
8171 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
8172 * Aborted).
8173 *
8174 * @param aDepType Dependency type to check.
8175 *
8176 * @note Non Machine based classes should use #addStateDependency() and
8177 * #releaseStateDependency() methods or the smart AutoStateDependency
8178 * template.
8179 *
8180 * @note This method must be called from under this object's read or write
8181 * lock.
8182 */
8183HRESULT Machine::checkStateDependency(StateDependency aDepType)
8184{
8185 switch (aDepType)
8186 {
8187 case AnyStateDep:
8188 {
8189 break;
8190 }
8191 case MutableStateDep:
8192 {
8193 if ( mData->mRegistered
8194 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8195 || ( mData->mMachineState != MachineState_Paused
8196 && mData->mMachineState != MachineState_Running
8197 && mData->mMachineState != MachineState_Aborted
8198 && mData->mMachineState != MachineState_Teleported
8199 && mData->mMachineState != MachineState_PoweredOff
8200 )
8201 )
8202 )
8203 return setError(VBOX_E_INVALID_VM_STATE,
8204 tr("The machine is not mutable (state is %s)"),
8205 Global::stringifyMachineState(mData->mMachineState));
8206 break;
8207 }
8208 case MutableOrSavedStateDep:
8209 {
8210 if ( mData->mRegistered
8211 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8212 || ( mData->mMachineState != MachineState_Paused
8213 && mData->mMachineState != MachineState_Running
8214 && mData->mMachineState != MachineState_Aborted
8215 && mData->mMachineState != MachineState_Teleported
8216 && mData->mMachineState != MachineState_Saved
8217 && mData->mMachineState != MachineState_PoweredOff
8218 )
8219 )
8220 )
8221 return setError(VBOX_E_INVALID_VM_STATE,
8222 tr("The machine is not mutable (state is %s)"),
8223 Global::stringifyMachineState(mData->mMachineState));
8224 break;
8225 }
8226 case OfflineStateDep:
8227 {
8228 if ( mData->mRegistered
8229 && ( !isSessionMachine()
8230 || ( mData->mMachineState != MachineState_PoweredOff
8231 && mData->mMachineState != MachineState_Saved
8232 && mData->mMachineState != MachineState_Aborted
8233 && mData->mMachineState != MachineState_Teleported
8234 )
8235 )
8236 )
8237 return setError(VBOX_E_INVALID_VM_STATE,
8238 tr("The machine is not offline (state is %s)"),
8239 Global::stringifyMachineState(mData->mMachineState));
8240 break;
8241 }
8242 }
8243
8244 return S_OK;
8245}
8246
8247/**
8248 * Helper to initialize all associated child objects and allocate data
8249 * structures.
8250 *
8251 * This method must be called as a part of the object's initialization procedure
8252 * (usually done in the #init() method).
8253 *
8254 * @note Must be called only from #init() or from #registeredInit().
8255 */
8256HRESULT Machine::initDataAndChildObjects()
8257{
8258 AutoCaller autoCaller(this);
8259 AssertComRCReturnRC(autoCaller.rc());
8260 AssertComRCReturn(autoCaller.state() == InInit ||
8261 autoCaller.state() == Limited, E_FAIL);
8262
8263 AssertReturn(!mData->mAccessible, E_FAIL);
8264
8265 /* allocate data structures */
8266 mSSData.allocate();
8267 mUserData.allocate();
8268 mHWData.allocate();
8269 mMediaData.allocate();
8270 mStorageControllers.allocate();
8271
8272 /* initialize mOSTypeId */
8273 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
8274
8275 /* create associated BIOS settings object */
8276 unconst(mBIOSSettings).createObject();
8277 mBIOSSettings->init(this);
8278
8279 /* create an associated VRDE object (default is disabled) */
8280 unconst(mVRDEServer).createObject();
8281 mVRDEServer->init(this);
8282
8283 /* create associated serial port objects */
8284 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8285 {
8286 unconst(mSerialPorts[slot]).createObject();
8287 mSerialPorts[slot]->init(this, slot);
8288 }
8289
8290 /* create associated parallel port objects */
8291 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8292 {
8293 unconst(mParallelPorts[slot]).createObject();
8294 mParallelPorts[slot]->init(this, slot);
8295 }
8296
8297 /* create the audio adapter object (always present, default is disabled) */
8298 unconst(mAudioAdapter).createObject();
8299 mAudioAdapter->init(this);
8300
8301 /* create the USB controller object (always present, default is disabled) */
8302 unconst(mUSBController).createObject();
8303 mUSBController->init(this);
8304
8305 /* create associated network adapter objects */
8306 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8307 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8308 {
8309 unconst(mNetworkAdapters[slot]).createObject();
8310 mNetworkAdapters[slot]->init(this, slot);
8311 }
8312
8313 /* create the bandwidth control */
8314 unconst(mBandwidthControl).createObject();
8315 mBandwidthControl->init(this);
8316
8317 return S_OK;
8318}
8319
8320/**
8321 * Helper to uninitialize all associated child objects and to free all data
8322 * structures.
8323 *
8324 * This method must be called as a part of the object's uninitialization
8325 * procedure (usually done in the #uninit() method).
8326 *
8327 * @note Must be called only from #uninit() or from #registeredInit().
8328 */
8329void Machine::uninitDataAndChildObjects()
8330{
8331 AutoCaller autoCaller(this);
8332 AssertComRCReturnVoid(autoCaller.rc());
8333 AssertComRCReturnVoid( autoCaller.state() == InUninit
8334 || autoCaller.state() == Limited);
8335
8336 /* tell all our other child objects we've been uninitialized */
8337 if (mBandwidthControl)
8338 {
8339 mBandwidthControl->uninit();
8340 unconst(mBandwidthControl).setNull();
8341 }
8342
8343 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8344 {
8345 if (mNetworkAdapters[slot])
8346 {
8347 mNetworkAdapters[slot]->uninit();
8348 unconst(mNetworkAdapters[slot]).setNull();
8349 }
8350 }
8351
8352 if (mUSBController)
8353 {
8354 mUSBController->uninit();
8355 unconst(mUSBController).setNull();
8356 }
8357
8358 if (mAudioAdapter)
8359 {
8360 mAudioAdapter->uninit();
8361 unconst(mAudioAdapter).setNull();
8362 }
8363
8364 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8365 {
8366 if (mParallelPorts[slot])
8367 {
8368 mParallelPorts[slot]->uninit();
8369 unconst(mParallelPorts[slot]).setNull();
8370 }
8371 }
8372
8373 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8374 {
8375 if (mSerialPorts[slot])
8376 {
8377 mSerialPorts[slot]->uninit();
8378 unconst(mSerialPorts[slot]).setNull();
8379 }
8380 }
8381
8382 if (mVRDEServer)
8383 {
8384 mVRDEServer->uninit();
8385 unconst(mVRDEServer).setNull();
8386 }
8387
8388 if (mBIOSSettings)
8389 {
8390 mBIOSSettings->uninit();
8391 unconst(mBIOSSettings).setNull();
8392 }
8393
8394 /* Deassociate media (only when a real Machine or a SnapshotMachine
8395 * instance is uninitialized; SessionMachine instances refer to real
8396 * Machine media). This is necessary for a clean re-initialization of
8397 * the VM after successfully re-checking the accessibility state. Note
8398 * that in case of normal Machine or SnapshotMachine uninitialization (as
8399 * a result of unregistering or deleting the snapshot), outdated media
8400 * attachments will already be uninitialized and deleted, so this
8401 * code will not affect them. */
8402 if ( !!mMediaData
8403 && (!isSessionMachine())
8404 )
8405 {
8406 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8407 it != mMediaData->mAttachments.end();
8408 ++it)
8409 {
8410 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8411 if (pMedium.isNull())
8412 continue;
8413 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8414 AssertComRC(rc);
8415 }
8416 }
8417
8418 if (!isSessionMachine() && !isSnapshotMachine())
8419 {
8420 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8421 if (mData->mFirstSnapshot)
8422 {
8423 // snapshots tree is protected by machine write lock; strictly
8424 // this isn't necessary here since we're deleting the entire
8425 // machine, but otherwise we assert in Snapshot::uninit()
8426 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8427 mData->mFirstSnapshot->uninit();
8428 mData->mFirstSnapshot.setNull();
8429 }
8430
8431 mData->mCurrentSnapshot.setNull();
8432 }
8433
8434 /* free data structures (the essential mData structure is not freed here
8435 * since it may be still in use) */
8436 mMediaData.free();
8437 mStorageControllers.free();
8438 mHWData.free();
8439 mUserData.free();
8440 mSSData.free();
8441}
8442
8443/**
8444 * Returns a pointer to the Machine object for this machine that acts like a
8445 * parent for complex machine data objects such as shared folders, etc.
8446 *
8447 * For primary Machine objects and for SnapshotMachine objects, returns this
8448 * object's pointer itself. For SessionMachine objects, returns the peer
8449 * (primary) machine pointer.
8450 */
8451Machine* Machine::getMachine()
8452{
8453 if (isSessionMachine())
8454 return (Machine*)mPeer;
8455 return this;
8456}
8457
8458/**
8459 * Makes sure that there are no machine state dependents. If necessary, waits
8460 * for the number of dependents to drop to zero.
8461 *
8462 * Make sure this method is called from under this object's write lock to
8463 * guarantee that no new dependents may be added when this method returns
8464 * control to the caller.
8465 *
8466 * @note Locks this object for writing. The lock will be released while waiting
8467 * (if necessary).
8468 *
8469 * @warning To be used only in methods that change the machine state!
8470 */
8471void Machine::ensureNoStateDependencies()
8472{
8473 AssertReturnVoid(isWriteLockOnCurrentThread());
8474
8475 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8476
8477 /* Wait for all state dependents if necessary */
8478 if (mData->mMachineStateDeps != 0)
8479 {
8480 /* lazy semaphore creation */
8481 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8482 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8483
8484 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8485 mData->mMachineStateDeps));
8486
8487 ++mData->mMachineStateChangePending;
8488
8489 /* reset the semaphore before waiting, the last dependent will signal
8490 * it */
8491 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8492
8493 alock.release();
8494
8495 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8496
8497 alock.acquire();
8498
8499 -- mData->mMachineStateChangePending;
8500 }
8501}
8502
8503/**
8504 * Changes the machine state and informs callbacks.
8505 *
8506 * This method is not intended to fail so it either returns S_OK or asserts (and
8507 * returns a failure).
8508 *
8509 * @note Locks this object for writing.
8510 */
8511HRESULT Machine::setMachineState(MachineState_T aMachineState)
8512{
8513 LogFlowThisFuncEnter();
8514 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8515
8516 AutoCaller autoCaller(this);
8517 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8518
8519 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8520
8521 /* wait for state dependents to drop to zero */
8522 ensureNoStateDependencies();
8523
8524 if (mData->mMachineState != aMachineState)
8525 {
8526 mData->mMachineState = aMachineState;
8527
8528 RTTimeNow(&mData->mLastStateChange);
8529
8530 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8531 }
8532
8533 LogFlowThisFuncLeave();
8534 return S_OK;
8535}
8536
8537/**
8538 * Searches for a shared folder with the given logical name
8539 * in the collection of shared folders.
8540 *
8541 * @param aName logical name of the shared folder
8542 * @param aSharedFolder where to return the found object
8543 * @param aSetError whether to set the error info if the folder is
8544 * not found
8545 * @return
8546 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8547 *
8548 * @note
8549 * must be called from under the object's lock!
8550 */
8551HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8552 ComObjPtr<SharedFolder> &aSharedFolder,
8553 bool aSetError /* = false */)
8554{
8555 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8556 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8557 it != mHWData->mSharedFolders.end();
8558 ++it)
8559 {
8560 SharedFolder *pSF = *it;
8561 AutoCaller autoCaller(pSF);
8562 if (pSF->getName() == aName)
8563 {
8564 aSharedFolder = pSF;
8565 rc = S_OK;
8566 break;
8567 }
8568 }
8569
8570 if (aSetError && FAILED(rc))
8571 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8572
8573 return rc;
8574}
8575
8576/**
8577 * Initializes all machine instance data from the given settings structures
8578 * from XML. The exception is the machine UUID which needs special handling
8579 * depending on the caller's use case, so the caller needs to set that herself.
8580 *
8581 * This gets called in several contexts during machine initialization:
8582 *
8583 * -- When machine XML exists on disk already and needs to be loaded into memory,
8584 * for example, from registeredInit() to load all registered machines on
8585 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8586 * attached to the machine should be part of some media registry already.
8587 *
8588 * -- During OVF import, when a machine config has been constructed from an
8589 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8590 * ensure that the media listed as attachments in the config (which have
8591 * been imported from the OVF) receive the correct registry ID.
8592 *
8593 * -- During VM cloning.
8594 *
8595 * @param config Machine settings from XML.
8596 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8597 * @return
8598 */
8599HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8600 const Guid *puuidRegistry)
8601{
8602 // copy name, description, OS type, teleporter, UTC etc.
8603 mUserData->s = config.machineUserData;
8604
8605 // look up the object by Id to check it is valid
8606 ComPtr<IGuestOSType> guestOSType;
8607 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8608 guestOSType.asOutParam());
8609 if (FAILED(rc)) return rc;
8610
8611 // stateFile (optional)
8612 if (config.strStateFile.isEmpty())
8613 mSSData->strStateFilePath.setNull();
8614 else
8615 {
8616 Utf8Str stateFilePathFull(config.strStateFile);
8617 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8618 if (RT_FAILURE(vrc))
8619 return setError(E_FAIL,
8620 tr("Invalid saved state file path '%s' (%Rrc)"),
8621 config.strStateFile.c_str(),
8622 vrc);
8623 mSSData->strStateFilePath = stateFilePathFull;
8624 }
8625
8626 // snapshot folder needs special processing so set it again
8627 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8628 if (FAILED(rc)) return rc;
8629
8630 /* Copy the extra data items (Not in any case config is already the same as
8631 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8632 * make sure the extra data map is copied). */
8633 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8634
8635 /* currentStateModified (optional, default is true) */
8636 mData->mCurrentStateModified = config.fCurrentStateModified;
8637
8638 mData->mLastStateChange = config.timeLastStateChange;
8639
8640 /*
8641 * note: all mUserData members must be assigned prior this point because
8642 * we need to commit changes in order to let mUserData be shared by all
8643 * snapshot machine instances.
8644 */
8645 mUserData.commitCopy();
8646
8647 // machine registry, if present (must be loaded before snapshots)
8648 if (config.canHaveOwnMediaRegistry())
8649 {
8650 // determine machine folder
8651 Utf8Str strMachineFolder = getSettingsFileFull();
8652 strMachineFolder.stripFilename();
8653 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8654 config.mediaRegistry,
8655 strMachineFolder);
8656 if (FAILED(rc)) return rc;
8657 }
8658
8659 /* Snapshot node (optional) */
8660 size_t cRootSnapshots;
8661 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8662 {
8663 // there must be only one root snapshot
8664 Assert(cRootSnapshots == 1);
8665
8666 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8667
8668 rc = loadSnapshot(snap,
8669 config.uuidCurrentSnapshot,
8670 NULL); // no parent == first snapshot
8671 if (FAILED(rc)) return rc;
8672 }
8673
8674 // hardware data
8675 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8676 if (FAILED(rc)) return rc;
8677
8678 // load storage controllers
8679 rc = loadStorageControllers(config.storageMachine,
8680 puuidRegistry,
8681 NULL /* puuidSnapshot */);
8682 if (FAILED(rc)) return rc;
8683
8684 /*
8685 * NOTE: the assignment below must be the last thing to do,
8686 * otherwise it will be not possible to change the settings
8687 * somewhere in the code above because all setters will be
8688 * blocked by checkStateDependency(MutableStateDep).
8689 */
8690
8691 /* set the machine state to Aborted or Saved when appropriate */
8692 if (config.fAborted)
8693 {
8694 mSSData->strStateFilePath.setNull();
8695
8696 /* no need to use setMachineState() during init() */
8697 mData->mMachineState = MachineState_Aborted;
8698 }
8699 else if (!mSSData->strStateFilePath.isEmpty())
8700 {
8701 /* no need to use setMachineState() during init() */
8702 mData->mMachineState = MachineState_Saved;
8703 }
8704
8705 // after loading settings, we are no longer different from the XML on disk
8706 mData->flModifications = 0;
8707
8708 return S_OK;
8709}
8710
8711/**
8712 * Recursively loads all snapshots starting from the given.
8713 *
8714 * @param aNode <Snapshot> node.
8715 * @param aCurSnapshotId Current snapshot ID from the settings file.
8716 * @param aParentSnapshot Parent snapshot.
8717 */
8718HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8719 const Guid &aCurSnapshotId,
8720 Snapshot *aParentSnapshot)
8721{
8722 AssertReturn(!isSnapshotMachine(), E_FAIL);
8723 AssertReturn(!isSessionMachine(), E_FAIL);
8724
8725 HRESULT rc = S_OK;
8726
8727 Utf8Str strStateFile;
8728 if (!data.strStateFile.isEmpty())
8729 {
8730 /* optional */
8731 strStateFile = data.strStateFile;
8732 int vrc = calculateFullPath(strStateFile, strStateFile);
8733 if (RT_FAILURE(vrc))
8734 return setError(E_FAIL,
8735 tr("Invalid saved state file path '%s' (%Rrc)"),
8736 strStateFile.c_str(),
8737 vrc);
8738 }
8739
8740 /* create a snapshot machine object */
8741 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8742 pSnapshotMachine.createObject();
8743 rc = pSnapshotMachine->initFromSettings(this,
8744 data.hardware,
8745 &data.debugging,
8746 &data.autostart,
8747 data.storage,
8748 data.uuid.ref(),
8749 strStateFile);
8750 if (FAILED(rc)) return rc;
8751
8752 /* create a snapshot object */
8753 ComObjPtr<Snapshot> pSnapshot;
8754 pSnapshot.createObject();
8755 /* initialize the snapshot */
8756 rc = pSnapshot->init(mParent, // VirtualBox object
8757 data.uuid,
8758 data.strName,
8759 data.strDescription,
8760 data.timestamp,
8761 pSnapshotMachine,
8762 aParentSnapshot);
8763 if (FAILED(rc)) return rc;
8764
8765 /* memorize the first snapshot if necessary */
8766 if (!mData->mFirstSnapshot)
8767 mData->mFirstSnapshot = pSnapshot;
8768
8769 /* memorize the current snapshot when appropriate */
8770 if ( !mData->mCurrentSnapshot
8771 && pSnapshot->getId() == aCurSnapshotId
8772 )
8773 mData->mCurrentSnapshot = pSnapshot;
8774
8775 // now create the children
8776 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8777 it != data.llChildSnapshots.end();
8778 ++it)
8779 {
8780 const settings::Snapshot &childData = *it;
8781 // recurse
8782 rc = loadSnapshot(childData,
8783 aCurSnapshotId,
8784 pSnapshot); // parent = the one we created above
8785 if (FAILED(rc)) return rc;
8786 }
8787
8788 return rc;
8789}
8790
8791/**
8792 * Loads settings into mHWData.
8793 *
8794 * @param data Reference to the hardware settings.
8795 * @param pDbg Pointer to the debugging settings.
8796 * @param pAutostart Pointer to the autostart settings.
8797 */
8798HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8799 const settings::Autostart *pAutostart)
8800{
8801 AssertReturn(!isSessionMachine(), E_FAIL);
8802
8803 HRESULT rc = S_OK;
8804
8805 try
8806 {
8807 /* The hardware version attribute (optional). */
8808 mHWData->mHWVersion = data.strVersion;
8809 mHWData->mHardwareUUID = data.uuid;
8810
8811 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8812 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8813 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8814 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8815 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8816 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8817 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8818 mHWData->mPAEEnabled = data.fPAE;
8819 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8820 mHWData->mLongMode = data.enmLongMode;
8821 mHWData->mCPUCount = data.cCPUs;
8822 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8823 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8824
8825 // cpu
8826 if (mHWData->mCPUHotPlugEnabled)
8827 {
8828 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8829 it != data.llCpus.end();
8830 ++it)
8831 {
8832 const settings::Cpu &cpu = *it;
8833
8834 mHWData->mCPUAttached[cpu.ulId] = true;
8835 }
8836 }
8837
8838 // cpuid leafs
8839 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8840 it != data.llCpuIdLeafs.end();
8841 ++it)
8842 {
8843 const settings::CpuIdLeaf &leaf = *it;
8844
8845 switch (leaf.ulId)
8846 {
8847 case 0x0:
8848 case 0x1:
8849 case 0x2:
8850 case 0x3:
8851 case 0x4:
8852 case 0x5:
8853 case 0x6:
8854 case 0x7:
8855 case 0x8:
8856 case 0x9:
8857 case 0xA:
8858 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8859 break;
8860
8861 case 0x80000000:
8862 case 0x80000001:
8863 case 0x80000002:
8864 case 0x80000003:
8865 case 0x80000004:
8866 case 0x80000005:
8867 case 0x80000006:
8868 case 0x80000007:
8869 case 0x80000008:
8870 case 0x80000009:
8871 case 0x8000000A:
8872 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8873 break;
8874
8875 default:
8876 /* just ignore */
8877 break;
8878 }
8879 }
8880
8881 mHWData->mMemorySize = data.ulMemorySizeMB;
8882 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8883
8884 // boot order
8885 for (size_t i = 0;
8886 i < RT_ELEMENTS(mHWData->mBootOrder);
8887 i++)
8888 {
8889 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8890 if (it == data.mapBootOrder.end())
8891 mHWData->mBootOrder[i] = DeviceType_Null;
8892 else
8893 mHWData->mBootOrder[i] = it->second;
8894 }
8895
8896 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8897 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8898 mHWData->mMonitorCount = data.cMonitors;
8899 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8900 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8901 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8902 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8903 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8904 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8905 mHWData->mVideoCaptureFps = data.ulVideoCaptureFps;
8906 mHWData->mVideoCaptureFile = data.strVideoCaptureFile;
8907 mHWData->mFirmwareType = data.firmwareType;
8908 mHWData->mPointingHIDType = data.pointingHIDType;
8909 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8910 mHWData->mChipsetType = data.chipsetType;
8911 mHWData->mEmulatedUSBWebcamEnabled = data.fEmulatedUSBWebcam;
8912 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8913 mHWData->mHPETEnabled = data.fHPETEnabled;
8914
8915 /* VRDEServer */
8916 rc = mVRDEServer->loadSettings(data.vrdeSettings);
8917 if (FAILED(rc)) return rc;
8918
8919 /* BIOS */
8920 rc = mBIOSSettings->loadSettings(data.biosSettings);
8921 if (FAILED(rc)) return rc;
8922
8923 // Bandwidth control (must come before network adapters)
8924 rc = mBandwidthControl->loadSettings(data.ioSettings);
8925 if (FAILED(rc)) return rc;
8926
8927 /* USB Controller */
8928 rc = mUSBController->loadSettings(data.usbController);
8929 if (FAILED(rc)) return rc;
8930
8931 // network adapters
8932 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8933 uint32_t oldCount = mNetworkAdapters.size();
8934 if (newCount > oldCount)
8935 {
8936 mNetworkAdapters.resize(newCount);
8937 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
8938 {
8939 unconst(mNetworkAdapters[slot]).createObject();
8940 mNetworkAdapters[slot]->init(this, slot);
8941 }
8942 }
8943 else if (newCount < oldCount)
8944 mNetworkAdapters.resize(newCount);
8945 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8946 it != data.llNetworkAdapters.end();
8947 ++it)
8948 {
8949 const settings::NetworkAdapter &nic = *it;
8950
8951 /* slot unicity is guaranteed by XML Schema */
8952 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8953 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
8954 if (FAILED(rc)) return rc;
8955 }
8956
8957 // serial ports
8958 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8959 it != data.llSerialPorts.end();
8960 ++it)
8961 {
8962 const settings::SerialPort &s = *it;
8963
8964 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8965 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
8966 if (FAILED(rc)) return rc;
8967 }
8968
8969 // parallel ports (optional)
8970 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8971 it != data.llParallelPorts.end();
8972 ++it)
8973 {
8974 const settings::ParallelPort &p = *it;
8975
8976 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8977 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
8978 if (FAILED(rc)) return rc;
8979 }
8980
8981 /* AudioAdapter */
8982 rc = mAudioAdapter->loadSettings(data.audioAdapter);
8983 if (FAILED(rc)) return rc;
8984
8985 /* Shared folders */
8986 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8987 it != data.llSharedFolders.end();
8988 ++it)
8989 {
8990 const settings::SharedFolder &sf = *it;
8991
8992 ComObjPtr<SharedFolder> sharedFolder;
8993 /* Check for double entries. Not allowed! */
8994 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8995 if (SUCCEEDED(rc))
8996 return setError(VBOX_E_OBJECT_IN_USE,
8997 tr("Shared folder named '%s' already exists"),
8998 sf.strName.c_str());
8999
9000 /* Create the new shared folder. Don't break on error. This will be
9001 * reported when the machine starts. */
9002 sharedFolder.createObject();
9003 rc = sharedFolder->init(getMachine(),
9004 sf.strName,
9005 sf.strHostPath,
9006 RT_BOOL(sf.fWritable),
9007 RT_BOOL(sf.fAutoMount),
9008 false /* fFailOnError */);
9009 if (FAILED(rc)) return rc;
9010 mHWData->mSharedFolders.push_back(sharedFolder);
9011 }
9012
9013 // Clipboard
9014 mHWData->mClipboardMode = data.clipboardMode;
9015
9016 // drag'n'drop
9017 mHWData->mDragAndDropMode = data.dragAndDropMode;
9018
9019 // guest settings
9020 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9021
9022 // IO settings
9023 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9024 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9025
9026 // Host PCI devices
9027 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9028 it != data.pciAttachments.end();
9029 ++it)
9030 {
9031 const settings::HostPCIDeviceAttachment &hpda = *it;
9032 ComObjPtr<PCIDeviceAttachment> pda;
9033
9034 pda.createObject();
9035 pda->loadSettings(this, hpda);
9036 mHWData->mPCIDeviceAssignments.push_back(pda);
9037 }
9038
9039 /*
9040 * (The following isn't really real hardware, but it lives in HWData
9041 * for reasons of convenience.)
9042 */
9043
9044#ifdef VBOX_WITH_GUEST_PROPS
9045 /* Guest properties (optional) */
9046 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9047 it != data.llGuestProperties.end();
9048 ++it)
9049 {
9050 const settings::GuestProperty &prop = *it;
9051 uint32_t fFlags = guestProp::NILFLAG;
9052 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9053 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9054 mHWData->mGuestProperties[prop.strName] = property;
9055 }
9056
9057 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9058#endif /* VBOX_WITH_GUEST_PROPS defined */
9059
9060 rc = loadDebugging(pDbg);
9061 if (FAILED(rc))
9062 return rc;
9063
9064 mHWData->mAutostart = *pAutostart;
9065
9066 /* default frontend */
9067 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9068 }
9069 catch(std::bad_alloc &)
9070 {
9071 return E_OUTOFMEMORY;
9072 }
9073
9074 AssertComRC(rc);
9075 return rc;
9076}
9077
9078/**
9079 * Called from Machine::loadHardware() to load the debugging settings of the
9080 * machine.
9081 *
9082 * @param pDbg Pointer to the settings.
9083 */
9084HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
9085{
9086 mHWData->mDebugging = *pDbg;
9087 /* no more processing currently required, this will probably change. */
9088 return S_OK;
9089}
9090
9091/**
9092 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
9093 *
9094 * @param data
9095 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9096 * @param puuidSnapshot
9097 * @return
9098 */
9099HRESULT Machine::loadStorageControllers(const settings::Storage &data,
9100 const Guid *puuidRegistry,
9101 const Guid *puuidSnapshot)
9102{
9103 AssertReturn(!isSessionMachine(), E_FAIL);
9104
9105 HRESULT rc = S_OK;
9106
9107 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9108 it != data.llStorageControllers.end();
9109 ++it)
9110 {
9111 const settings::StorageController &ctlData = *it;
9112
9113 ComObjPtr<StorageController> pCtl;
9114 /* Try to find one with the name first. */
9115 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9116 if (SUCCEEDED(rc))
9117 return setError(VBOX_E_OBJECT_IN_USE,
9118 tr("Storage controller named '%s' already exists"),
9119 ctlData.strName.c_str());
9120
9121 pCtl.createObject();
9122 rc = pCtl->init(this,
9123 ctlData.strName,
9124 ctlData.storageBus,
9125 ctlData.ulInstance,
9126 ctlData.fBootable);
9127 if (FAILED(rc)) return rc;
9128
9129 mStorageControllers->push_back(pCtl);
9130
9131 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9132 if (FAILED(rc)) return rc;
9133
9134 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9135 if (FAILED(rc)) return rc;
9136
9137 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9138 if (FAILED(rc)) return rc;
9139
9140 /* Set IDE emulation settings (only for AHCI controller). */
9141 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9142 {
9143 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9144 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9145 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9146 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9147 )
9148 return rc;
9149 }
9150
9151 /* Load the attached devices now. */
9152 rc = loadStorageDevices(pCtl,
9153 ctlData,
9154 puuidRegistry,
9155 puuidSnapshot);
9156 if (FAILED(rc)) return rc;
9157 }
9158
9159 return S_OK;
9160}
9161
9162/**
9163 * Called from loadStorageControllers for a controller's devices.
9164 *
9165 * @param aStorageController
9166 * @param data
9167 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9168 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9169 * @return
9170 */
9171HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
9172 const settings::StorageController &data,
9173 const Guid *puuidRegistry,
9174 const Guid *puuidSnapshot)
9175{
9176 HRESULT rc = S_OK;
9177
9178 /* paranoia: detect duplicate attachments */
9179 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9180 it != data.llAttachedDevices.end();
9181 ++it)
9182 {
9183 const settings::AttachedDevice &ad = *it;
9184
9185 for (settings::AttachedDevicesList::const_iterator it2 = it;
9186 it2 != data.llAttachedDevices.end();
9187 ++it2)
9188 {
9189 if (it == it2)
9190 continue;
9191
9192 const settings::AttachedDevice &ad2 = *it2;
9193
9194 if ( ad.lPort == ad2.lPort
9195 && ad.lDevice == ad2.lDevice)
9196 {
9197 return setError(E_FAIL,
9198 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9199 aStorageController->getName().c_str(),
9200 ad.lPort,
9201 ad.lDevice,
9202 mUserData->s.strName.c_str());
9203 }
9204 }
9205 }
9206
9207 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9208 it != data.llAttachedDevices.end();
9209 ++it)
9210 {
9211 const settings::AttachedDevice &dev = *it;
9212 ComObjPtr<Medium> medium;
9213
9214 switch (dev.deviceType)
9215 {
9216 case DeviceType_Floppy:
9217 case DeviceType_DVD:
9218 if (dev.strHostDriveSrc.isNotEmpty())
9219 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9220 else
9221 rc = mParent->findRemoveableMedium(dev.deviceType,
9222 dev.uuid,
9223 false /* fRefresh */,
9224 false /* aSetError */,
9225 medium);
9226 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9227 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9228 rc = S_OK;
9229 break;
9230
9231 case DeviceType_HardDisk:
9232 {
9233 /* find a hard disk by UUID */
9234 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9235 if (FAILED(rc))
9236 {
9237 if (isSnapshotMachine())
9238 {
9239 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9240 // so the user knows that the bad disk is in a snapshot somewhere
9241 com::ErrorInfo info;
9242 return setError(E_FAIL,
9243 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9244 puuidSnapshot->raw(),
9245 info.getText().raw());
9246 }
9247 else
9248 return rc;
9249 }
9250
9251 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9252
9253 if (medium->getType() == MediumType_Immutable)
9254 {
9255 if (isSnapshotMachine())
9256 return setError(E_FAIL,
9257 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9258 "of the virtual machine '%s' ('%s')"),
9259 medium->getLocationFull().c_str(),
9260 dev.uuid.raw(),
9261 puuidSnapshot->raw(),
9262 mUserData->s.strName.c_str(),
9263 mData->m_strConfigFileFull.c_str());
9264
9265 return setError(E_FAIL,
9266 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9267 medium->getLocationFull().c_str(),
9268 dev.uuid.raw(),
9269 mUserData->s.strName.c_str(),
9270 mData->m_strConfigFileFull.c_str());
9271 }
9272
9273 if (medium->getType() == MediumType_MultiAttach)
9274 {
9275 if (isSnapshotMachine())
9276 return setError(E_FAIL,
9277 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9278 "of the virtual machine '%s' ('%s')"),
9279 medium->getLocationFull().c_str(),
9280 dev.uuid.raw(),
9281 puuidSnapshot->raw(),
9282 mUserData->s.strName.c_str(),
9283 mData->m_strConfigFileFull.c_str());
9284
9285 return setError(E_FAIL,
9286 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9287 medium->getLocationFull().c_str(),
9288 dev.uuid.raw(),
9289 mUserData->s.strName.c_str(),
9290 mData->m_strConfigFileFull.c_str());
9291 }
9292
9293 if ( !isSnapshotMachine()
9294 && medium->getChildren().size() != 0
9295 )
9296 return setError(E_FAIL,
9297 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9298 "because it has %d differencing child hard disks"),
9299 medium->getLocationFull().c_str(),
9300 dev.uuid.raw(),
9301 mUserData->s.strName.c_str(),
9302 mData->m_strConfigFileFull.c_str(),
9303 medium->getChildren().size());
9304
9305 if (findAttachment(mMediaData->mAttachments,
9306 medium))
9307 return setError(E_FAIL,
9308 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9309 medium->getLocationFull().c_str(),
9310 dev.uuid.raw(),
9311 mUserData->s.strName.c_str(),
9312 mData->m_strConfigFileFull.c_str());
9313
9314 break;
9315 }
9316
9317 default:
9318 return setError(E_FAIL,
9319 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9320 medium->getLocationFull().c_str(),
9321 mUserData->s.strName.c_str(),
9322 mData->m_strConfigFileFull.c_str());
9323 }
9324
9325 if (FAILED(rc))
9326 break;
9327
9328 /* Bandwidth groups are loaded at this point. */
9329 ComObjPtr<BandwidthGroup> pBwGroup;
9330
9331 if (!dev.strBwGroup.isEmpty())
9332 {
9333 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9334 if (FAILED(rc))
9335 return setError(E_FAIL,
9336 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9337 medium->getLocationFull().c_str(),
9338 dev.strBwGroup.c_str(),
9339 mUserData->s.strName.c_str(),
9340 mData->m_strConfigFileFull.c_str());
9341 pBwGroup->reference();
9342 }
9343
9344 const Bstr controllerName = aStorageController->getName();
9345 ComObjPtr<MediumAttachment> pAttachment;
9346 pAttachment.createObject();
9347 rc = pAttachment->init(this,
9348 medium,
9349 controllerName,
9350 dev.lPort,
9351 dev.lDevice,
9352 dev.deviceType,
9353 false,
9354 dev.fPassThrough,
9355 dev.fTempEject,
9356 dev.fNonRotational,
9357 dev.fDiscard,
9358 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
9359 if (FAILED(rc)) break;
9360
9361 /* associate the medium with this machine and snapshot */
9362 if (!medium.isNull())
9363 {
9364 AutoCaller medCaller(medium);
9365 if (FAILED(medCaller.rc())) return medCaller.rc();
9366 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9367
9368 if (isSnapshotMachine())
9369 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
9370 else
9371 rc = medium->addBackReference(mData->mUuid);
9372 /* If the medium->addBackReference fails it sets an appropriate
9373 * error message, so no need to do any guesswork here. */
9374
9375 if (puuidRegistry)
9376 // caller wants registry ID to be set on all attached media (OVF import case)
9377 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9378 }
9379
9380 if (FAILED(rc))
9381 break;
9382
9383 /* back up mMediaData to let registeredInit() properly rollback on failure
9384 * (= limited accessibility) */
9385 setModified(IsModified_Storage);
9386 mMediaData.backup();
9387 mMediaData->mAttachments.push_back(pAttachment);
9388 }
9389
9390 return rc;
9391}
9392
9393/**
9394 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9395 *
9396 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9397 * @param aSnapshot where to return the found snapshot
9398 * @param aSetError true to set extended error info on failure
9399 */
9400HRESULT Machine::findSnapshotById(const Guid &aId,
9401 ComObjPtr<Snapshot> &aSnapshot,
9402 bool aSetError /* = false */)
9403{
9404 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9405
9406 if (!mData->mFirstSnapshot)
9407 {
9408 if (aSetError)
9409 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9410 return E_FAIL;
9411 }
9412
9413 if (aId.isZero())
9414 aSnapshot = mData->mFirstSnapshot;
9415 else
9416 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9417
9418 if (!aSnapshot)
9419 {
9420 if (aSetError)
9421 return setError(E_FAIL,
9422 tr("Could not find a snapshot with UUID {%s}"),
9423 aId.toString().c_str());
9424 return E_FAIL;
9425 }
9426
9427 return S_OK;
9428}
9429
9430/**
9431 * Returns the snapshot with the given name or fails of no such snapshot.
9432 *
9433 * @param aName snapshot name to find
9434 * @param aSnapshot where to return the found snapshot
9435 * @param aSetError true to set extended error info on failure
9436 */
9437HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9438 ComObjPtr<Snapshot> &aSnapshot,
9439 bool aSetError /* = false */)
9440{
9441 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9442
9443 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9444
9445 if (!mData->mFirstSnapshot)
9446 {
9447 if (aSetError)
9448 return setError(VBOX_E_OBJECT_NOT_FOUND,
9449 tr("This machine does not have any snapshots"));
9450 return VBOX_E_OBJECT_NOT_FOUND;
9451 }
9452
9453 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9454
9455 if (!aSnapshot)
9456 {
9457 if (aSetError)
9458 return setError(VBOX_E_OBJECT_NOT_FOUND,
9459 tr("Could not find a snapshot named '%s'"), strName.c_str());
9460 return VBOX_E_OBJECT_NOT_FOUND;
9461 }
9462
9463 return S_OK;
9464}
9465
9466/**
9467 * Returns a storage controller object with the given name.
9468 *
9469 * @param aName storage controller name to find
9470 * @param aStorageController where to return the found storage controller
9471 * @param aSetError true to set extended error info on failure
9472 */
9473HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9474 ComObjPtr<StorageController> &aStorageController,
9475 bool aSetError /* = false */)
9476{
9477 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9478
9479 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9480 it != mStorageControllers->end();
9481 ++it)
9482 {
9483 if ((*it)->getName() == aName)
9484 {
9485 aStorageController = (*it);
9486 return S_OK;
9487 }
9488 }
9489
9490 if (aSetError)
9491 return setError(VBOX_E_OBJECT_NOT_FOUND,
9492 tr("Could not find a storage controller named '%s'"),
9493 aName.c_str());
9494 return VBOX_E_OBJECT_NOT_FOUND;
9495}
9496
9497HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9498 MediaData::AttachmentList &atts)
9499{
9500 AutoCaller autoCaller(this);
9501 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9502
9503 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9504
9505 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9506 it != mMediaData->mAttachments.end();
9507 ++it)
9508 {
9509 const ComObjPtr<MediumAttachment> &pAtt = *it;
9510
9511 // should never happen, but deal with NULL pointers in the list.
9512 AssertStmt(!pAtt.isNull(), continue);
9513
9514 // getControllerName() needs caller+read lock
9515 AutoCaller autoAttCaller(pAtt);
9516 if (FAILED(autoAttCaller.rc()))
9517 {
9518 atts.clear();
9519 return autoAttCaller.rc();
9520 }
9521 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9522
9523 if (pAtt->getControllerName() == aName)
9524 atts.push_back(pAtt);
9525 }
9526
9527 return S_OK;
9528}
9529
9530/**
9531 * Helper for #saveSettings. Cares about renaming the settings directory and
9532 * file if the machine name was changed and about creating a new settings file
9533 * if this is a new machine.
9534 *
9535 * @note Must be never called directly but only from #saveSettings().
9536 */
9537HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9538{
9539 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9540
9541 HRESULT rc = S_OK;
9542
9543 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9544
9545 /// @todo need to handle primary group change, too
9546
9547 /* attempt to rename the settings file if machine name is changed */
9548 if ( mUserData->s.fNameSync
9549 && mUserData.isBackedUp()
9550 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9551 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9552 )
9553 {
9554 bool dirRenamed = false;
9555 bool fileRenamed = false;
9556
9557 Utf8Str configFile, newConfigFile;
9558 Utf8Str configFilePrev, newConfigFilePrev;
9559 Utf8Str configDir, newConfigDir;
9560
9561 do
9562 {
9563 int vrc = VINF_SUCCESS;
9564
9565 Utf8Str name = mUserData.backedUpData()->s.strName;
9566 Utf8Str newName = mUserData->s.strName;
9567 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9568 if (group == "/")
9569 group.setNull();
9570 Utf8Str newGroup = mUserData->s.llGroups.front();
9571 if (newGroup == "/")
9572 newGroup.setNull();
9573
9574 configFile = mData->m_strConfigFileFull;
9575
9576 /* first, rename the directory if it matches the group and machine name */
9577 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9578 group.c_str(), RTPATH_DELIMITER, name.c_str());
9579 /** @todo hack, make somehow use of ComposeMachineFilename */
9580 if (mUserData->s.fDirectoryIncludesUUID)
9581 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9582 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9583 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9584 /** @todo hack, make somehow use of ComposeMachineFilename */
9585 if (mUserData->s.fDirectoryIncludesUUID)
9586 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9587 configDir = configFile;
9588 configDir.stripFilename();
9589 newConfigDir = configDir;
9590 if ( configDir.length() >= groupPlusName.length()
9591 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9592 {
9593 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9594 Utf8Str newConfigBaseDir(newConfigDir);
9595 newConfigDir.append(newGroupPlusName);
9596 /* consistency: use \ if appropriate on the platform */
9597 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9598 /* new dir and old dir cannot be equal here because of 'if'
9599 * above and because name != newName */
9600 Assert(configDir != newConfigDir);
9601 if (!fSettingsFileIsNew)
9602 {
9603 /* perform real rename only if the machine is not new */
9604 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9605 if ( vrc == VERR_FILE_NOT_FOUND
9606 || vrc == VERR_PATH_NOT_FOUND)
9607 {
9608 /* create the parent directory, then retry renaming */
9609 Utf8Str parent(newConfigDir);
9610 parent.stripFilename();
9611 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9612 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9613 }
9614 if (RT_FAILURE(vrc))
9615 {
9616 rc = setError(E_FAIL,
9617 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9618 configDir.c_str(),
9619 newConfigDir.c_str(),
9620 vrc);
9621 break;
9622 }
9623 /* delete subdirectories which are no longer needed */
9624 Utf8Str dir(configDir);
9625 dir.stripFilename();
9626 while (dir != newConfigBaseDir && dir != ".")
9627 {
9628 vrc = RTDirRemove(dir.c_str());
9629 if (RT_FAILURE(vrc))
9630 break;
9631 dir.stripFilename();
9632 }
9633 dirRenamed = true;
9634 }
9635 }
9636
9637 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9638 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9639
9640 /* then try to rename the settings file itself */
9641 if (newConfigFile != configFile)
9642 {
9643 /* get the path to old settings file in renamed directory */
9644 configFile = Utf8StrFmt("%s%c%s",
9645 newConfigDir.c_str(),
9646 RTPATH_DELIMITER,
9647 RTPathFilename(configFile.c_str()));
9648 if (!fSettingsFileIsNew)
9649 {
9650 /* perform real rename only if the machine is not new */
9651 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9652 if (RT_FAILURE(vrc))
9653 {
9654 rc = setError(E_FAIL,
9655 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9656 configFile.c_str(),
9657 newConfigFile.c_str(),
9658 vrc);
9659 break;
9660 }
9661 fileRenamed = true;
9662 configFilePrev = configFile;
9663 configFilePrev += "-prev";
9664 newConfigFilePrev = newConfigFile;
9665 newConfigFilePrev += "-prev";
9666 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9667 }
9668 }
9669
9670 // update m_strConfigFileFull amd mConfigFile
9671 mData->m_strConfigFileFull = newConfigFile;
9672 // compute the relative path too
9673 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9674
9675 // store the old and new so that VirtualBox::saveSettings() can update
9676 // the media registry
9677 if ( mData->mRegistered
9678 && configDir != newConfigDir)
9679 {
9680 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9681
9682 if (pfNeedsGlobalSaveSettings)
9683 *pfNeedsGlobalSaveSettings = true;
9684 }
9685
9686 // in the saved state file path, replace the old directory with the new directory
9687 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9688 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9689
9690 // and do the same thing for the saved state file paths of all the online snapshots
9691 if (mData->mFirstSnapshot)
9692 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9693 newConfigDir.c_str());
9694 }
9695 while (0);
9696
9697 if (FAILED(rc))
9698 {
9699 /* silently try to rename everything back */
9700 if (fileRenamed)
9701 {
9702 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9703 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9704 }
9705 if (dirRenamed)
9706 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9707 }
9708
9709 if (FAILED(rc)) return rc;
9710 }
9711
9712 if (fSettingsFileIsNew)
9713 {
9714 /* create a virgin config file */
9715 int vrc = VINF_SUCCESS;
9716
9717 /* ensure the settings directory exists */
9718 Utf8Str path(mData->m_strConfigFileFull);
9719 path.stripFilename();
9720 if (!RTDirExists(path.c_str()))
9721 {
9722 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9723 if (RT_FAILURE(vrc))
9724 {
9725 return setError(E_FAIL,
9726 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9727 path.c_str(),
9728 vrc);
9729 }
9730 }
9731
9732 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9733 path = Utf8Str(mData->m_strConfigFileFull);
9734 RTFILE f = NIL_RTFILE;
9735 vrc = RTFileOpen(&f, path.c_str(),
9736 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9737 if (RT_FAILURE(vrc))
9738 return setError(E_FAIL,
9739 tr("Could not create the settings file '%s' (%Rrc)"),
9740 path.c_str(),
9741 vrc);
9742 RTFileClose(f);
9743 }
9744
9745 return rc;
9746}
9747
9748/**
9749 * Saves and commits machine data, user data and hardware data.
9750 *
9751 * Note that on failure, the data remains uncommitted.
9752 *
9753 * @a aFlags may combine the following flags:
9754 *
9755 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9756 * Used when saving settings after an operation that makes them 100%
9757 * correspond to the settings from the current snapshot.
9758 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9759 * #isReallyModified() returns false. This is necessary for cases when we
9760 * change machine data directly, not through the backup()/commit() mechanism.
9761 * - SaveS_Force: settings will be saved without doing a deep compare of the
9762 * settings structures. This is used when this is called because snapshots
9763 * have changed to avoid the overhead of the deep compare.
9764 *
9765 * @note Must be called from under this object's write lock. Locks children for
9766 * writing.
9767 *
9768 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9769 * initialized to false and that will be set to true by this function if
9770 * the caller must invoke VirtualBox::saveSettings() because the global
9771 * settings have changed. This will happen if a machine rename has been
9772 * saved and the global machine and media registries will therefore need
9773 * updating.
9774 */
9775HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9776 int aFlags /*= 0*/)
9777{
9778 LogFlowThisFuncEnter();
9779
9780 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9781
9782 /* make sure child objects are unable to modify the settings while we are
9783 * saving them */
9784 ensureNoStateDependencies();
9785
9786 AssertReturn(!isSnapshotMachine(),
9787 E_FAIL);
9788
9789 HRESULT rc = S_OK;
9790 bool fNeedsWrite = false;
9791
9792 /* First, prepare to save settings. It will care about renaming the
9793 * settings directory and file if the machine name was changed and about
9794 * creating a new settings file if this is a new machine. */
9795 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9796 if (FAILED(rc)) return rc;
9797
9798 // keep a pointer to the current settings structures
9799 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9800 settings::MachineConfigFile *pNewConfig = NULL;
9801
9802 try
9803 {
9804 // make a fresh one to have everyone write stuff into
9805 pNewConfig = new settings::MachineConfigFile(NULL);
9806 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9807
9808 // now go and copy all the settings data from COM to the settings structures
9809 // (this calles saveSettings() on all the COM objects in the machine)
9810 copyMachineDataToSettings(*pNewConfig);
9811
9812 if (aFlags & SaveS_ResetCurStateModified)
9813 {
9814 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9815 mData->mCurrentStateModified = FALSE;
9816 fNeedsWrite = true; // always, no need to compare
9817 }
9818 else if (aFlags & SaveS_Force)
9819 {
9820 fNeedsWrite = true; // always, no need to compare
9821 }
9822 else
9823 {
9824 if (!mData->mCurrentStateModified)
9825 {
9826 // do a deep compare of the settings that we just saved with the settings
9827 // previously stored in the config file; this invokes MachineConfigFile::operator==
9828 // which does a deep compare of all the settings, which is expensive but less expensive
9829 // than writing out XML in vain
9830 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9831
9832 // could still be modified if any settings changed
9833 mData->mCurrentStateModified = fAnySettingsChanged;
9834
9835 fNeedsWrite = fAnySettingsChanged;
9836 }
9837 else
9838 fNeedsWrite = true;
9839 }
9840
9841 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9842
9843 if (fNeedsWrite)
9844 // now spit it all out!
9845 pNewConfig->write(mData->m_strConfigFileFull);
9846
9847 mData->pMachineConfigFile = pNewConfig;
9848 delete pOldConfig;
9849 commit();
9850
9851 // after saving settings, we are no longer different from the XML on disk
9852 mData->flModifications = 0;
9853 }
9854 catch (HRESULT err)
9855 {
9856 // we assume that error info is set by the thrower
9857 rc = err;
9858
9859 // restore old config
9860 delete pNewConfig;
9861 mData->pMachineConfigFile = pOldConfig;
9862 }
9863 catch (...)
9864 {
9865 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9866 }
9867
9868 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9869 {
9870 /* Fire the data change event, even on failure (since we've already
9871 * committed all data). This is done only for SessionMachines because
9872 * mutable Machine instances are always not registered (i.e. private
9873 * to the client process that creates them) and thus don't need to
9874 * inform callbacks. */
9875 if (isSessionMachine())
9876 mParent->onMachineDataChange(mData->mUuid);
9877 }
9878
9879 LogFlowThisFunc(("rc=%08X\n", rc));
9880 LogFlowThisFuncLeave();
9881 return rc;
9882}
9883
9884/**
9885 * Implementation for saving the machine settings into the given
9886 * settings::MachineConfigFile instance. This copies machine extradata
9887 * from the previous machine config file in the instance data, if any.
9888 *
9889 * This gets called from two locations:
9890 *
9891 * -- Machine::saveSettings(), during the regular XML writing;
9892 *
9893 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9894 * exported to OVF and we write the VirtualBox proprietary XML
9895 * into a <vbox:Machine> tag.
9896 *
9897 * This routine fills all the fields in there, including snapshots, *except*
9898 * for the following:
9899 *
9900 * -- fCurrentStateModified. There is some special logic associated with that.
9901 *
9902 * The caller can then call MachineConfigFile::write() or do something else
9903 * with it.
9904 *
9905 * Caller must hold the machine lock!
9906 *
9907 * This throws XML errors and HRESULT, so the caller must have a catch block!
9908 */
9909void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
9910{
9911 // deep copy extradata
9912 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9913
9914 config.uuid = mData->mUuid;
9915
9916 // copy name, description, OS type, teleport, UTC etc.
9917 config.machineUserData = mUserData->s;
9918
9919 if ( mData->mMachineState == MachineState_Saved
9920 || mData->mMachineState == MachineState_Restoring
9921 // when deleting a snapshot we may or may not have a saved state in the current state,
9922 // so let's not assert here please
9923 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9924 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9925 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9926 && (!mSSData->strStateFilePath.isEmpty())
9927 )
9928 )
9929 {
9930 Assert(!mSSData->strStateFilePath.isEmpty());
9931 /* try to make the file name relative to the settings file dir */
9932 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9933 }
9934 else
9935 {
9936 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9937 config.strStateFile.setNull();
9938 }
9939
9940 if (mData->mCurrentSnapshot)
9941 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
9942 else
9943 config.uuidCurrentSnapshot.clear();
9944
9945 config.timeLastStateChange = mData->mLastStateChange;
9946 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9947 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9948
9949 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9950 if (FAILED(rc)) throw rc;
9951
9952 rc = saveStorageControllers(config.storageMachine);
9953 if (FAILED(rc)) throw rc;
9954
9955 // save machine's media registry if this is VirtualBox 4.0 or later
9956 if (config.canHaveOwnMediaRegistry())
9957 {
9958 // determine machine folder
9959 Utf8Str strMachineFolder = getSettingsFileFull();
9960 strMachineFolder.stripFilename();
9961 mParent->saveMediaRegistry(config.mediaRegistry,
9962 getId(), // only media with registry ID == machine UUID
9963 strMachineFolder);
9964 // this throws HRESULT
9965 }
9966
9967 // save snapshots
9968 rc = saveAllSnapshots(config);
9969 if (FAILED(rc)) throw rc;
9970}
9971
9972/**
9973 * Saves all snapshots of the machine into the given machine config file. Called
9974 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9975 * @param config
9976 * @return
9977 */
9978HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
9979{
9980 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9981
9982 HRESULT rc = S_OK;
9983
9984 try
9985 {
9986 config.llFirstSnapshot.clear();
9987
9988 if (mData->mFirstSnapshot)
9989 {
9990 settings::Snapshot snapNew;
9991 config.llFirstSnapshot.push_back(snapNew);
9992
9993 // get reference to the fresh copy of the snapshot on the list and
9994 // work on that copy directly to avoid excessive copying later
9995 settings::Snapshot &snap = config.llFirstSnapshot.front();
9996
9997 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
9998 if (FAILED(rc)) throw rc;
9999 }
10000
10001// if (mType == IsSessionMachine)
10002// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10003
10004 }
10005 catch (HRESULT err)
10006 {
10007 /* we assume that error info is set by the thrower */
10008 rc = err;
10009 }
10010 catch (...)
10011 {
10012 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10013 }
10014
10015 return rc;
10016}
10017
10018/**
10019 * Saves the VM hardware configuration. It is assumed that the
10020 * given node is empty.
10021 *
10022 * @param data Reference to the settings object for the hardware config.
10023 * @param pDbg Pointer to the settings object for the debugging config
10024 * which happens to live in mHWData.
10025 * @param pAutostart Pointer to the settings object for the autostart config
10026 * which happens to live in mHWData.
10027 */
10028HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10029 settings::Autostart *pAutostart)
10030{
10031 HRESULT rc = S_OK;
10032
10033 try
10034 {
10035 /* The hardware version attribute (optional).
10036 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10037 if ( mHWData->mHWVersion == "1"
10038 && mSSData->strStateFilePath.isEmpty()
10039 )
10040 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. */
10041
10042 data.strVersion = mHWData->mHWVersion;
10043 data.uuid = mHWData->mHardwareUUID;
10044
10045 // CPU
10046 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10047 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
10048 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10049 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10050 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10051 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10052 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10053 data.fPAE = !!mHWData->mPAEEnabled;
10054 data.enmLongMode = mHWData->mLongMode;
10055 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10056
10057 /* Standard and Extended CPUID leafs. */
10058 data.llCpuIdLeafs.clear();
10059 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
10060 {
10061 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10062 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10063 }
10064 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
10065 {
10066 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10067 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10068 }
10069
10070 data.cCPUs = mHWData->mCPUCount;
10071 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10072 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10073
10074 data.llCpus.clear();
10075 if (data.fCpuHotPlug)
10076 {
10077 for (unsigned idx = 0; idx < data.cCPUs; idx++)
10078 {
10079 if (mHWData->mCPUAttached[idx])
10080 {
10081 settings::Cpu cpu;
10082 cpu.ulId = idx;
10083 data.llCpus.push_back(cpu);
10084 }
10085 }
10086 }
10087
10088 // memory
10089 data.ulMemorySizeMB = mHWData->mMemorySize;
10090 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10091
10092 // firmware
10093 data.firmwareType = mHWData->mFirmwareType;
10094
10095 // HID
10096 data.pointingHIDType = mHWData->mPointingHIDType;
10097 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10098
10099 // chipset
10100 data.chipsetType = mHWData->mChipsetType;
10101
10102 data.fEmulatedUSBWebcam = !!mHWData->mEmulatedUSBWebcamEnabled;
10103 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10104
10105 // HPET
10106 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10107
10108 // boot order
10109 data.mapBootOrder.clear();
10110 for (size_t i = 0;
10111 i < RT_ELEMENTS(mHWData->mBootOrder);
10112 ++i)
10113 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10114
10115 // display
10116 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10117 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10118 data.cMonitors = mHWData->mMonitorCount;
10119 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10120 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10121 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10122 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10123 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10124 data.ulVideoCaptureFps = mHWData->mVideoCaptureFps;
10125 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10126 data.strVideoCaptureFile = mHWData->mVideoCaptureFile;
10127
10128 /* VRDEServer settings (optional) */
10129 rc = mVRDEServer->saveSettings(data.vrdeSettings);
10130 if (FAILED(rc)) throw rc;
10131
10132 /* BIOS (required) */
10133 rc = mBIOSSettings->saveSettings(data.biosSettings);
10134 if (FAILED(rc)) throw rc;
10135
10136 /* USB Controller (required) */
10137 rc = mUSBController->saveSettings(data.usbController);
10138 if (FAILED(rc)) throw rc;
10139
10140 /* Network adapters (required) */
10141 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10142 data.llNetworkAdapters.clear();
10143 /* Write out only the nominal number of network adapters for this
10144 * chipset type. Since Machine::commit() hasn't been called there
10145 * may be extra NIC settings in the vector. */
10146 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10147 {
10148 settings::NetworkAdapter nic;
10149 nic.ulSlot = slot;
10150 /* paranoia check... must not be NULL, but must not crash either. */
10151 if (mNetworkAdapters[slot])
10152 {
10153 rc = mNetworkAdapters[slot]->saveSettings(nic);
10154 if (FAILED(rc)) throw rc;
10155
10156 data.llNetworkAdapters.push_back(nic);
10157 }
10158 }
10159
10160 /* Serial ports */
10161 data.llSerialPorts.clear();
10162 for (ULONG slot = 0;
10163 slot < RT_ELEMENTS(mSerialPorts);
10164 ++slot)
10165 {
10166 settings::SerialPort s;
10167 s.ulSlot = slot;
10168 rc = mSerialPorts[slot]->saveSettings(s);
10169 if (FAILED(rc)) return rc;
10170
10171 data.llSerialPorts.push_back(s);
10172 }
10173
10174 /* Parallel ports */
10175 data.llParallelPorts.clear();
10176 for (ULONG slot = 0;
10177 slot < RT_ELEMENTS(mParallelPorts);
10178 ++slot)
10179 {
10180 settings::ParallelPort p;
10181 p.ulSlot = slot;
10182 rc = mParallelPorts[slot]->saveSettings(p);
10183 if (FAILED(rc)) return rc;
10184
10185 data.llParallelPorts.push_back(p);
10186 }
10187
10188 /* Audio adapter */
10189 rc = mAudioAdapter->saveSettings(data.audioAdapter);
10190 if (FAILED(rc)) return rc;
10191
10192 /* Shared folders */
10193 data.llSharedFolders.clear();
10194 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10195 it != mHWData->mSharedFolders.end();
10196 ++it)
10197 {
10198 SharedFolder *pSF = *it;
10199 AutoCaller sfCaller(pSF);
10200 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10201 settings::SharedFolder sf;
10202 sf.strName = pSF->getName();
10203 sf.strHostPath = pSF->getHostPath();
10204 sf.fWritable = !!pSF->isWritable();
10205 sf.fAutoMount = !!pSF->isAutoMounted();
10206
10207 data.llSharedFolders.push_back(sf);
10208 }
10209
10210 // clipboard
10211 data.clipboardMode = mHWData->mClipboardMode;
10212
10213 // drag'n'drop
10214 data.dragAndDropMode = mHWData->mDragAndDropMode;
10215
10216 /* Guest */
10217 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10218
10219 // IO settings
10220 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10221 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10222
10223 /* BandwidthControl (required) */
10224 rc = mBandwidthControl->saveSettings(data.ioSettings);
10225 if (FAILED(rc)) throw rc;
10226
10227 /* Host PCI devices */
10228 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10229 it != mHWData->mPCIDeviceAssignments.end();
10230 ++it)
10231 {
10232 ComObjPtr<PCIDeviceAttachment> pda = *it;
10233 settings::HostPCIDeviceAttachment hpda;
10234
10235 rc = pda->saveSettings(hpda);
10236 if (FAILED(rc)) throw rc;
10237
10238 data.pciAttachments.push_back(hpda);
10239 }
10240
10241
10242 // guest properties
10243 data.llGuestProperties.clear();
10244#ifdef VBOX_WITH_GUEST_PROPS
10245 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10246 it != mHWData->mGuestProperties.end();
10247 ++it)
10248 {
10249 HWData::GuestProperty property = it->second;
10250
10251 /* Remove transient guest properties at shutdown unless we
10252 * are saving state */
10253 if ( ( mData->mMachineState == MachineState_PoweredOff
10254 || mData->mMachineState == MachineState_Aborted
10255 || mData->mMachineState == MachineState_Teleported)
10256 && ( property.mFlags & guestProp::TRANSIENT
10257 || property.mFlags & guestProp::TRANSRESET))
10258 continue;
10259 settings::GuestProperty prop;
10260 prop.strName = it->first;
10261 prop.strValue = property.strValue;
10262 prop.timestamp = property.mTimestamp;
10263 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10264 guestProp::writeFlags(property.mFlags, szFlags);
10265 prop.strFlags = szFlags;
10266
10267 data.llGuestProperties.push_back(prop);
10268 }
10269
10270 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10271 /* I presume this doesn't require a backup(). */
10272 mData->mGuestPropertiesModified = FALSE;
10273#endif /* VBOX_WITH_GUEST_PROPS defined */
10274
10275 *pDbg = mHWData->mDebugging;
10276 *pAutostart = mHWData->mAutostart;
10277
10278 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10279 }
10280 catch(std::bad_alloc &)
10281 {
10282 return E_OUTOFMEMORY;
10283 }
10284
10285 AssertComRC(rc);
10286 return rc;
10287}
10288
10289/**
10290 * Saves the storage controller configuration.
10291 *
10292 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10293 */
10294HRESULT Machine::saveStorageControllers(settings::Storage &data)
10295{
10296 data.llStorageControllers.clear();
10297
10298 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10299 it != mStorageControllers->end();
10300 ++it)
10301 {
10302 HRESULT rc;
10303 ComObjPtr<StorageController> pCtl = *it;
10304
10305 settings::StorageController ctl;
10306 ctl.strName = pCtl->getName();
10307 ctl.controllerType = pCtl->getControllerType();
10308 ctl.storageBus = pCtl->getStorageBus();
10309 ctl.ulInstance = pCtl->getInstance();
10310 ctl.fBootable = pCtl->getBootable();
10311
10312 /* Save the port count. */
10313 ULONG portCount;
10314 rc = pCtl->COMGETTER(PortCount)(&portCount);
10315 ComAssertComRCRet(rc, rc);
10316 ctl.ulPortCount = portCount;
10317
10318 /* Save fUseHostIOCache */
10319 BOOL fUseHostIOCache;
10320 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10321 ComAssertComRCRet(rc, rc);
10322 ctl.fUseHostIOCache = !!fUseHostIOCache;
10323
10324 /* Save IDE emulation settings. */
10325 if (ctl.controllerType == StorageControllerType_IntelAhci)
10326 {
10327 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10328 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10329 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10330 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10331 )
10332 ComAssertComRCRet(rc, rc);
10333 }
10334
10335 /* save the devices now. */
10336 rc = saveStorageDevices(pCtl, ctl);
10337 ComAssertComRCRet(rc, rc);
10338
10339 data.llStorageControllers.push_back(ctl);
10340 }
10341
10342 return S_OK;
10343}
10344
10345/**
10346 * Saves the hard disk configuration.
10347 */
10348HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10349 settings::StorageController &data)
10350{
10351 MediaData::AttachmentList atts;
10352
10353 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
10354 if (FAILED(rc)) return rc;
10355
10356 data.llAttachedDevices.clear();
10357 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10358 it != atts.end();
10359 ++it)
10360 {
10361 settings::AttachedDevice dev;
10362
10363 MediumAttachment *pAttach = *it;
10364 Medium *pMedium = pAttach->getMedium();
10365
10366 dev.deviceType = pAttach->getType();
10367 dev.lPort = pAttach->getPort();
10368 dev.lDevice = pAttach->getDevice();
10369 if (pMedium)
10370 {
10371 if (pMedium->isHostDrive())
10372 dev.strHostDriveSrc = pMedium->getLocationFull();
10373 else
10374 dev.uuid = pMedium->getId();
10375 dev.fPassThrough = pAttach->getPassthrough();
10376 dev.fTempEject = pAttach->getTempEject();
10377 dev.fNonRotational = pAttach->getNonRotational();
10378 dev.fDiscard = pAttach->getDiscard();
10379 }
10380
10381 dev.strBwGroup = pAttach->getBandwidthGroup();
10382
10383 data.llAttachedDevices.push_back(dev);
10384 }
10385
10386 return S_OK;
10387}
10388
10389/**
10390 * Saves machine state settings as defined by aFlags
10391 * (SaveSTS_* values).
10392 *
10393 * @param aFlags Combination of SaveSTS_* flags.
10394 *
10395 * @note Locks objects for writing.
10396 */
10397HRESULT Machine::saveStateSettings(int aFlags)
10398{
10399 if (aFlags == 0)
10400 return S_OK;
10401
10402 AutoCaller autoCaller(this);
10403 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10404
10405 /* This object's write lock is also necessary to serialize file access
10406 * (prevent concurrent reads and writes) */
10407 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10408
10409 HRESULT rc = S_OK;
10410
10411 Assert(mData->pMachineConfigFile);
10412
10413 try
10414 {
10415 if (aFlags & SaveSTS_CurStateModified)
10416 mData->pMachineConfigFile->fCurrentStateModified = true;
10417
10418 if (aFlags & SaveSTS_StateFilePath)
10419 {
10420 if (!mSSData->strStateFilePath.isEmpty())
10421 /* try to make the file name relative to the settings file dir */
10422 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10423 else
10424 mData->pMachineConfigFile->strStateFile.setNull();
10425 }
10426
10427 if (aFlags & SaveSTS_StateTimeStamp)
10428 {
10429 Assert( mData->mMachineState != MachineState_Aborted
10430 || mSSData->strStateFilePath.isEmpty());
10431
10432 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10433
10434 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10435//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10436 }
10437
10438 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10439 }
10440 catch (...)
10441 {
10442 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10443 }
10444
10445 return rc;
10446}
10447
10448/**
10449 * Ensures that the given medium is added to a media registry. If this machine
10450 * was created with 4.0 or later, then the machine registry is used. Otherwise
10451 * the global VirtualBox media registry is used.
10452 *
10453 * Caller must NOT hold machine lock, media tree or any medium locks!
10454 *
10455 * @param pMedium
10456 */
10457void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10458{
10459 /* Paranoia checks: do not hold machine or media tree locks. */
10460 AssertReturnVoid(!isWriteLockOnCurrentThread());
10461 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10462
10463 ComObjPtr<Medium> pBase;
10464 {
10465 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10466 pBase = pMedium->getBase();
10467 }
10468
10469 /* Paranoia checks: do not hold medium locks. */
10470 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10471 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10472
10473 // decide which medium registry to use now that the medium is attached:
10474 Guid uuid;
10475 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10476 // machine XML is VirtualBox 4.0 or higher:
10477 uuid = getId(); // machine UUID
10478 else
10479 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10480
10481 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10482 mParent->markRegistryModified(uuid);
10483
10484 /* For more complex hard disk structures it can happen that the base
10485 * medium isn't yet associated with any medium registry. Do that now. */
10486 if (pMedium != pBase)
10487 {
10488 if (pBase->addRegistry(uuid, true /* fRecurse */))
10489 mParent->markRegistryModified(uuid);
10490 }
10491}
10492
10493/**
10494 * Creates differencing hard disks for all normal hard disks attached to this
10495 * machine and a new set of attachments to refer to created disks.
10496 *
10497 * Used when taking a snapshot or when deleting the current state. Gets called
10498 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10499 *
10500 * This method assumes that mMediaData contains the original hard disk attachments
10501 * it needs to create diffs for. On success, these attachments will be replaced
10502 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10503 * called to delete created diffs which will also rollback mMediaData and restore
10504 * whatever was backed up before calling this method.
10505 *
10506 * Attachments with non-normal hard disks are left as is.
10507 *
10508 * If @a aOnline is @c false then the original hard disks that require implicit
10509 * diffs will be locked for reading. Otherwise it is assumed that they are
10510 * already locked for writing (when the VM was started). Note that in the latter
10511 * case it is responsibility of the caller to lock the newly created diffs for
10512 * writing if this method succeeds.
10513 *
10514 * @param aProgress Progress object to run (must contain at least as
10515 * many operations left as the number of hard disks
10516 * attached).
10517 * @param aOnline Whether the VM was online prior to this operation.
10518 *
10519 * @note The progress object is not marked as completed, neither on success nor
10520 * on failure. This is a responsibility of the caller.
10521 *
10522 * @note Locks this object and the media tree for writing.
10523 */
10524HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10525 ULONG aWeight,
10526 bool aOnline)
10527{
10528 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10529
10530 AutoCaller autoCaller(this);
10531 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10532
10533 AutoMultiWriteLock2 alock(this->lockHandle(),
10534 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10535
10536 /* must be in a protective state because we release the lock below */
10537 AssertReturn( mData->mMachineState == MachineState_Saving
10538 || mData->mMachineState == MachineState_LiveSnapshotting
10539 || mData->mMachineState == MachineState_RestoringSnapshot
10540 || mData->mMachineState == MachineState_DeletingSnapshot
10541 , E_FAIL);
10542
10543 HRESULT rc = S_OK;
10544
10545 // use appropriate locked media map (online or offline)
10546 MediumLockListMap lockedMediaOffline;
10547 MediumLockListMap *lockedMediaMap;
10548 if (aOnline)
10549 lockedMediaMap = &mData->mSession.mLockedMedia;
10550 else
10551 lockedMediaMap = &lockedMediaOffline;
10552
10553 try
10554 {
10555 if (!aOnline)
10556 {
10557 /* lock all attached hard disks early to detect "in use"
10558 * situations before creating actual diffs */
10559 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10560 it != mMediaData->mAttachments.end();
10561 ++it)
10562 {
10563 MediumAttachment* pAtt = *it;
10564 if (pAtt->getType() == DeviceType_HardDisk)
10565 {
10566 Medium* pMedium = pAtt->getMedium();
10567 Assert(pMedium);
10568
10569 MediumLockList *pMediumLockList(new MediumLockList());
10570 alock.release();
10571 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10572 false /* fMediumLockWrite */,
10573 NULL,
10574 *pMediumLockList);
10575 alock.acquire();
10576 if (FAILED(rc))
10577 {
10578 delete pMediumLockList;
10579 throw rc;
10580 }
10581 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10582 if (FAILED(rc))
10583 {
10584 throw setError(rc,
10585 tr("Collecting locking information for all attached media failed"));
10586 }
10587 }
10588 }
10589
10590 /* Now lock all media. If this fails, nothing is locked. */
10591 alock.release();
10592 rc = lockedMediaMap->Lock();
10593 alock.acquire();
10594 if (FAILED(rc))
10595 {
10596 throw setError(rc,
10597 tr("Locking of attached media failed"));
10598 }
10599 }
10600
10601 /* remember the current list (note that we don't use backup() since
10602 * mMediaData may be already backed up) */
10603 MediaData::AttachmentList atts = mMediaData->mAttachments;
10604
10605 /* start from scratch */
10606 mMediaData->mAttachments.clear();
10607
10608 /* go through remembered attachments and create diffs for normal hard
10609 * disks and attach them */
10610 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10611 it != atts.end();
10612 ++it)
10613 {
10614 MediumAttachment* pAtt = *it;
10615
10616 DeviceType_T devType = pAtt->getType();
10617 Medium* pMedium = pAtt->getMedium();
10618
10619 if ( devType != DeviceType_HardDisk
10620 || pMedium == NULL
10621 || pMedium->getType() != MediumType_Normal)
10622 {
10623 /* copy the attachment as is */
10624
10625 /** @todo the progress object created in Console::TakeSnaphot
10626 * only expects operations for hard disks. Later other
10627 * device types need to show up in the progress as well. */
10628 if (devType == DeviceType_HardDisk)
10629 {
10630 if (pMedium == NULL)
10631 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10632 aWeight); // weight
10633 else
10634 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10635 pMedium->getBase()->getName().c_str()).raw(),
10636 aWeight); // weight
10637 }
10638
10639 mMediaData->mAttachments.push_back(pAtt);
10640 continue;
10641 }
10642
10643 /* need a diff */
10644 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10645 pMedium->getBase()->getName().c_str()).raw(),
10646 aWeight); // weight
10647
10648 Utf8Str strFullSnapshotFolder;
10649 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10650
10651 ComObjPtr<Medium> diff;
10652 diff.createObject();
10653 // store the diff in the same registry as the parent
10654 // (this cannot fail here because we can't create implicit diffs for
10655 // unregistered images)
10656 Guid uuidRegistryParent;
10657 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10658 Assert(fInRegistry); NOREF(fInRegistry);
10659 rc = diff->init(mParent,
10660 pMedium->getPreferredDiffFormat(),
10661 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10662 uuidRegistryParent);
10663 if (FAILED(rc)) throw rc;
10664
10665 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10666 * the push_back? Looks like we're going to release medium with the
10667 * wrong kind of lock (general issue with if we fail anywhere at all)
10668 * and an orphaned VDI in the snapshots folder. */
10669
10670 /* update the appropriate lock list */
10671 MediumLockList *pMediumLockList;
10672 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10673 AssertComRCThrowRC(rc);
10674 if (aOnline)
10675 {
10676 alock.release();
10677 /* The currently attached medium will be read-only, change
10678 * the lock type to read. */
10679 rc = pMediumLockList->Update(pMedium, false);
10680 alock.acquire();
10681 AssertComRCThrowRC(rc);
10682 }
10683
10684 /* release the locks before the potentially lengthy operation */
10685 alock.release();
10686 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10687 pMediumLockList,
10688 NULL /* aProgress */,
10689 true /* aWait */);
10690 alock.acquire();
10691 if (FAILED(rc)) throw rc;
10692
10693 rc = lockedMediaMap->Unlock();
10694 AssertComRCThrowRC(rc);
10695 alock.release();
10696 rc = pMediumLockList->Append(diff, true);
10697 alock.acquire();
10698 AssertComRCThrowRC(rc);
10699 alock.release();
10700 rc = lockedMediaMap->Lock();
10701 alock.acquire();
10702 AssertComRCThrowRC(rc);
10703
10704 rc = diff->addBackReference(mData->mUuid);
10705 AssertComRCThrowRC(rc);
10706
10707 /* add a new attachment */
10708 ComObjPtr<MediumAttachment> attachment;
10709 attachment.createObject();
10710 rc = attachment->init(this,
10711 diff,
10712 pAtt->getControllerName(),
10713 pAtt->getPort(),
10714 pAtt->getDevice(),
10715 DeviceType_HardDisk,
10716 true /* aImplicit */,
10717 false /* aPassthrough */,
10718 false /* aTempEject */,
10719 pAtt->getNonRotational(),
10720 pAtt->getDiscard(),
10721 pAtt->getBandwidthGroup());
10722 if (FAILED(rc)) throw rc;
10723
10724 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10725 AssertComRCThrowRC(rc);
10726 mMediaData->mAttachments.push_back(attachment);
10727 }
10728 }
10729 catch (HRESULT aRC) { rc = aRC; }
10730
10731 /* unlock all hard disks we locked when there is no VM */
10732 if (!aOnline)
10733 {
10734 ErrorInfoKeeper eik;
10735
10736 HRESULT rc1 = lockedMediaMap->Clear();
10737 AssertComRC(rc1);
10738 }
10739
10740 return rc;
10741}
10742
10743/**
10744 * Deletes implicit differencing hard disks created either by
10745 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10746 *
10747 * Note that to delete hard disks created by #AttachDevice() this method is
10748 * called from #fixupMedia() when the changes are rolled back.
10749 *
10750 * @note Locks this object and the media tree for writing.
10751 */
10752HRESULT Machine::deleteImplicitDiffs(bool aOnline)
10753{
10754 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10755
10756 AutoCaller autoCaller(this);
10757 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10758
10759 AutoMultiWriteLock2 alock(this->lockHandle(),
10760 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10761
10762 /* We absolutely must have backed up state. */
10763 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10764
10765 /* Check if there are any implicitly created diff images. */
10766 bool fImplicitDiffs = false;
10767 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10768 it != mMediaData->mAttachments.end();
10769 ++it)
10770 {
10771 const ComObjPtr<MediumAttachment> &pAtt = *it;
10772 if (pAtt->isImplicit())
10773 {
10774 fImplicitDiffs = true;
10775 break;
10776 }
10777 }
10778 /* If there is nothing to do, leave early. This saves lots of image locking
10779 * effort. It also avoids a MachineStateChanged event without real reason.
10780 * This is important e.g. when loading a VM config, because there should be
10781 * no events. Otherwise API clients can become thoroughly confused for
10782 * inaccessible VMs (the code for loading VM configs uses this method for
10783 * cleanup if the config makes no sense), as they take such events as an
10784 * indication that the VM is alive, and they would force the VM config to
10785 * be reread, leading to an endless loop. */
10786 if (!fImplicitDiffs)
10787 return S_OK;
10788
10789 HRESULT rc = S_OK;
10790 MachineState_T oldState = mData->mMachineState;
10791
10792 /* will release the lock before the potentially lengthy operation,
10793 * so protect with the special state (unless already protected) */
10794 if ( oldState != MachineState_Saving
10795 && oldState != MachineState_LiveSnapshotting
10796 && oldState != MachineState_RestoringSnapshot
10797 && oldState != MachineState_DeletingSnapshot
10798 && oldState != MachineState_DeletingSnapshotOnline
10799 && oldState != MachineState_DeletingSnapshotPaused
10800 )
10801 setMachineState(MachineState_SettingUp);
10802
10803 // use appropriate locked media map (online or offline)
10804 MediumLockListMap lockedMediaOffline;
10805 MediumLockListMap *lockedMediaMap;
10806 if (aOnline)
10807 lockedMediaMap = &mData->mSession.mLockedMedia;
10808 else
10809 lockedMediaMap = &lockedMediaOffline;
10810
10811 try
10812 {
10813 if (!aOnline)
10814 {
10815 /* lock all attached hard disks early to detect "in use"
10816 * situations before deleting actual diffs */
10817 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10818 it != mMediaData->mAttachments.end();
10819 ++it)
10820 {
10821 MediumAttachment* pAtt = *it;
10822 if (pAtt->getType() == DeviceType_HardDisk)
10823 {
10824 Medium* pMedium = pAtt->getMedium();
10825 Assert(pMedium);
10826
10827 MediumLockList *pMediumLockList(new MediumLockList());
10828 alock.release();
10829 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10830 false /* fMediumLockWrite */,
10831 NULL,
10832 *pMediumLockList);
10833 alock.acquire();
10834
10835 if (FAILED(rc))
10836 {
10837 delete pMediumLockList;
10838 throw rc;
10839 }
10840
10841 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10842 if (FAILED(rc))
10843 throw rc;
10844 }
10845 }
10846
10847 if (FAILED(rc))
10848 throw rc;
10849 } // end of offline
10850
10851 /* Lock lists are now up to date and include implicitly created media */
10852
10853 /* Go through remembered attachments and delete all implicitly created
10854 * diffs and fix up the attachment information */
10855 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10856 MediaData::AttachmentList implicitAtts;
10857 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10858 it != mMediaData->mAttachments.end();
10859 ++it)
10860 {
10861 ComObjPtr<MediumAttachment> pAtt = *it;
10862 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10863 if (pMedium.isNull())
10864 continue;
10865
10866 // Implicit attachments go on the list for deletion and back references are removed.
10867 if (pAtt->isImplicit())
10868 {
10869 /* Deassociate and mark for deletion */
10870 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
10871 rc = pMedium->removeBackReference(mData->mUuid);
10872 if (FAILED(rc))
10873 throw rc;
10874 implicitAtts.push_back(pAtt);
10875 continue;
10876 }
10877
10878 /* Was this medium attached before? */
10879 if (!findAttachment(oldAtts, pMedium))
10880 {
10881 /* no: de-associate */
10882 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
10883 rc = pMedium->removeBackReference(mData->mUuid);
10884 if (FAILED(rc))
10885 throw rc;
10886 continue;
10887 }
10888 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
10889 }
10890
10891 /* If there are implicit attachments to delete, throw away the lock
10892 * map contents (which will unlock all media) since the medium
10893 * attachments will be rolled back. Below we need to completely
10894 * recreate the lock map anyway since it is infinitely complex to
10895 * do this incrementally (would need reconstructing each attachment
10896 * change, which would be extremely hairy). */
10897 if (implicitAtts.size() != 0)
10898 {
10899 ErrorInfoKeeper eik;
10900
10901 HRESULT rc1 = lockedMediaMap->Clear();
10902 AssertComRC(rc1);
10903 }
10904
10905 /* rollback hard disk changes */
10906 mMediaData.rollback();
10907
10908 MultiResult mrc(S_OK);
10909
10910 // Delete unused implicit diffs.
10911 if (implicitAtts.size() != 0)
10912 {
10913 alock.release();
10914
10915 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
10916 it != implicitAtts.end();
10917 ++it)
10918 {
10919 // Remove medium associated with this attachment.
10920 ComObjPtr<MediumAttachment> pAtt = *it;
10921 Assert(pAtt);
10922 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
10923 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10924 Assert(pMedium);
10925
10926 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10927 // continue on delete failure, just collect error messages
10928 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
10929 mrc = rc;
10930 }
10931
10932 alock.acquire();
10933
10934 /* if there is a VM recreate media lock map as mentioned above,
10935 * otherwise it is a waste of time and we leave things unlocked */
10936 if (aOnline)
10937 {
10938 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10939 /* must never be NULL, but better safe than sorry */
10940 if (!pMachine.isNull())
10941 {
10942 alock.release();
10943 rc = mData->mSession.mMachine->lockMedia();
10944 alock.acquire();
10945 if (FAILED(rc))
10946 throw rc;
10947 }
10948 }
10949 }
10950 }
10951 catch (HRESULT aRC) {rc = aRC;}
10952
10953 if (mData->mMachineState == MachineState_SettingUp)
10954 setMachineState(oldState);
10955
10956 /* unlock all hard disks we locked when there is no VM */
10957 if (!aOnline)
10958 {
10959 ErrorInfoKeeper eik;
10960
10961 HRESULT rc1 = lockedMediaMap->Clear();
10962 AssertComRC(rc1);
10963 }
10964
10965 return rc;
10966}
10967
10968
10969/**
10970 * Looks through the given list of media attachments for one with the given parameters
10971 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10972 * can be searched as well if needed.
10973 *
10974 * @param list
10975 * @param aControllerName
10976 * @param aControllerPort
10977 * @param aDevice
10978 * @return
10979 */
10980MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10981 IN_BSTR aControllerName,
10982 LONG aControllerPort,
10983 LONG aDevice)
10984{
10985 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10986 it != ll.end();
10987 ++it)
10988 {
10989 MediumAttachment *pAttach = *it;
10990 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
10991 return pAttach;
10992 }
10993
10994 return NULL;
10995}
10996
10997/**
10998 * Looks through the given list of media attachments for one with the given parameters
10999 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11000 * can be searched as well if needed.
11001 *
11002 * @param list
11003 * @param aControllerName
11004 * @param aControllerPort
11005 * @param aDevice
11006 * @return
11007 */
11008MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11009 ComObjPtr<Medium> pMedium)
11010{
11011 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11012 it != ll.end();
11013 ++it)
11014 {
11015 MediumAttachment *pAttach = *it;
11016 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11017 if (pMediumThis == pMedium)
11018 return pAttach;
11019 }
11020
11021 return NULL;
11022}
11023
11024/**
11025 * Looks through the given list of media attachments for one with the given parameters
11026 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11027 * can be searched as well if needed.
11028 *
11029 * @param list
11030 * @param aControllerName
11031 * @param aControllerPort
11032 * @param aDevice
11033 * @return
11034 */
11035MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11036 Guid &id)
11037{
11038 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11039 it != ll.end();
11040 ++it)
11041 {
11042 MediumAttachment *pAttach = *it;
11043 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11044 if (pMediumThis->getId() == id)
11045 return pAttach;
11046 }
11047
11048 return NULL;
11049}
11050
11051/**
11052 * Main implementation for Machine::DetachDevice. This also gets called
11053 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11054 *
11055 * @param pAttach Medium attachment to detach.
11056 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11057 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
11058 * @return
11059 */
11060HRESULT Machine::detachDevice(MediumAttachment *pAttach,
11061 AutoWriteLock &writeLock,
11062 Snapshot *pSnapshot)
11063{
11064 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
11065 DeviceType_T mediumType = pAttach->getType();
11066
11067 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
11068
11069 if (pAttach->isImplicit())
11070 {
11071 /* attempt to implicitly delete the implicitly created diff */
11072
11073 /// @todo move the implicit flag from MediumAttachment to Medium
11074 /// and forbid any hard disk operation when it is implicit. Or maybe
11075 /// a special media state for it to make it even more simple.
11076
11077 Assert(mMediaData.isBackedUp());
11078
11079 /* will release the lock before the potentially lengthy operation, so
11080 * protect with the special state */
11081 MachineState_T oldState = mData->mMachineState;
11082 setMachineState(MachineState_SettingUp);
11083
11084 writeLock.release();
11085
11086 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
11087 true /*aWait*/);
11088
11089 writeLock.acquire();
11090
11091 setMachineState(oldState);
11092
11093 if (FAILED(rc)) return rc;
11094 }
11095
11096 setModified(IsModified_Storage);
11097 mMediaData.backup();
11098 mMediaData->mAttachments.remove(pAttach);
11099
11100 if (!oldmedium.isNull())
11101 {
11102 // if this is from a snapshot, do not defer detachment to commitMedia()
11103 if (pSnapshot)
11104 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
11105 // else if non-hard disk media, do not defer detachment to commitMedia() either
11106 else if (mediumType != DeviceType_HardDisk)
11107 oldmedium->removeBackReference(mData->mUuid);
11108 }
11109
11110 return S_OK;
11111}
11112
11113/**
11114 * Goes thru all media of the given list and
11115 *
11116 * 1) calls detachDevice() on each of them for this machine and
11117 * 2) adds all Medium objects found in the process to the given list,
11118 * depending on cleanupMode.
11119 *
11120 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11121 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11122 * media to the list.
11123 *
11124 * This gets called from Machine::Unregister, both for the actual Machine and
11125 * the SnapshotMachine objects that might be found in the snapshots.
11126 *
11127 * Requires caller and locking. The machine lock must be passed in because it
11128 * will be passed on to detachDevice which needs it for temporary unlocking.
11129 *
11130 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
11131 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11132 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
11133 * otherwise no media get added.
11134 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11135 * @return
11136 */
11137HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
11138 Snapshot *pSnapshot,
11139 CleanupMode_T cleanupMode,
11140 MediaList &llMedia)
11141{
11142 Assert(isWriteLockOnCurrentThread());
11143
11144 HRESULT rc;
11145
11146 // make a temporary list because detachDevice invalidates iterators into
11147 // mMediaData->mAttachments
11148 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11149
11150 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
11151 it != llAttachments2.end();
11152 ++it)
11153 {
11154 ComObjPtr<MediumAttachment> &pAttach = *it;
11155 ComObjPtr<Medium> pMedium = pAttach->getMedium();
11156
11157 if (!pMedium.isNull())
11158 {
11159 AutoCaller mac(pMedium);
11160 if (FAILED(mac.rc())) return mac.rc();
11161 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11162 DeviceType_T devType = pMedium->getDeviceType();
11163 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11164 && devType == DeviceType_HardDisk)
11165 || (cleanupMode == CleanupMode_Full)
11166 )
11167 {
11168 llMedia.push_back(pMedium);
11169 ComObjPtr<Medium> pParent = pMedium->getParent();
11170 /*
11171 * Search for medias which are not attached to any machine, but
11172 * in the chain to an attached disk. Mediums are only consided
11173 * if they are:
11174 * - have only one child
11175 * - no references to any machines
11176 * - are of normal medium type
11177 */
11178 while (!pParent.isNull())
11179 {
11180 AutoCaller mac1(pParent);
11181 if (FAILED(mac1.rc())) return mac1.rc();
11182 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11183 if (pParent->getChildren().size() == 1)
11184 {
11185 if ( pParent->getMachineBackRefCount() == 0
11186 && pParent->getType() == MediumType_Normal
11187 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11188 llMedia.push_back(pParent);
11189 }
11190 else
11191 break;
11192 pParent = pParent->getParent();
11193 }
11194 }
11195 }
11196
11197 // real machine: then we need to use the proper method
11198 rc = detachDevice(pAttach, writeLock, pSnapshot);
11199
11200 if (FAILED(rc))
11201 return rc;
11202 }
11203
11204 return S_OK;
11205}
11206
11207/**
11208 * Perform deferred hard disk detachments.
11209 *
11210 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11211 * backed up).
11212 *
11213 * If @a aOnline is @c true then this method will also unlock the old hard disks
11214 * for which the new implicit diffs were created and will lock these new diffs for
11215 * writing.
11216 *
11217 * @param aOnline Whether the VM was online prior to this operation.
11218 *
11219 * @note Locks this object for writing!
11220 */
11221void Machine::commitMedia(bool aOnline /*= false*/)
11222{
11223 AutoCaller autoCaller(this);
11224 AssertComRCReturnVoid(autoCaller.rc());
11225
11226 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11227
11228 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11229
11230 HRESULT rc = S_OK;
11231
11232 /* no attach/detach operations -- nothing to do */
11233 if (!mMediaData.isBackedUp())
11234 return;
11235
11236 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11237 bool fMediaNeedsLocking = false;
11238
11239 /* enumerate new attachments */
11240 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11241 it != mMediaData->mAttachments.end();
11242 ++it)
11243 {
11244 MediumAttachment *pAttach = *it;
11245
11246 pAttach->commit();
11247
11248 Medium* pMedium = pAttach->getMedium();
11249 bool fImplicit = pAttach->isImplicit();
11250
11251 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11252 (pMedium) ? pMedium->getName().c_str() : "NULL",
11253 fImplicit));
11254
11255 /** @todo convert all this Machine-based voodoo to MediumAttachment
11256 * based commit logic. */
11257 if (fImplicit)
11258 {
11259 /* convert implicit attachment to normal */
11260 pAttach->setImplicit(false);
11261
11262 if ( aOnline
11263 && pMedium
11264 && pAttach->getType() == DeviceType_HardDisk
11265 )
11266 {
11267 ComObjPtr<Medium> parent = pMedium->getParent();
11268 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11269
11270 /* update the appropriate lock list */
11271 MediumLockList *pMediumLockList;
11272 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11273 AssertComRC(rc);
11274 if (pMediumLockList)
11275 {
11276 /* unlock if there's a need to change the locking */
11277 if (!fMediaNeedsLocking)
11278 {
11279 rc = mData->mSession.mLockedMedia.Unlock();
11280 AssertComRC(rc);
11281 fMediaNeedsLocking = true;
11282 }
11283 rc = pMediumLockList->Update(parent, false);
11284 AssertComRC(rc);
11285 rc = pMediumLockList->Append(pMedium, true);
11286 AssertComRC(rc);
11287 }
11288 }
11289
11290 continue;
11291 }
11292
11293 if (pMedium)
11294 {
11295 /* was this medium attached before? */
11296 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11297 oldIt != oldAtts.end();
11298 ++oldIt)
11299 {
11300 MediumAttachment *pOldAttach = *oldIt;
11301 if (pOldAttach->getMedium() == pMedium)
11302 {
11303 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
11304
11305 /* yes: remove from old to avoid de-association */
11306 oldAtts.erase(oldIt);
11307 break;
11308 }
11309 }
11310 }
11311 }
11312
11313 /* enumerate remaining old attachments and de-associate from the
11314 * current machine state */
11315 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11316 it != oldAtts.end();
11317 ++it)
11318 {
11319 MediumAttachment *pAttach = *it;
11320 Medium* pMedium = pAttach->getMedium();
11321
11322 /* Detach only hard disks, since DVD/floppy media is detached
11323 * instantly in MountMedium. */
11324 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
11325 {
11326 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
11327
11328 /* now de-associate from the current machine state */
11329 rc = pMedium->removeBackReference(mData->mUuid);
11330 AssertComRC(rc);
11331
11332 if (aOnline)
11333 {
11334 /* unlock since medium is not used anymore */
11335 MediumLockList *pMediumLockList;
11336 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11337 AssertComRC(rc);
11338 if (pMediumLockList)
11339 {
11340 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11341 AssertComRC(rc);
11342 }
11343 }
11344 }
11345 }
11346
11347 /* take media locks again so that the locking state is consistent */
11348 if (fMediaNeedsLocking)
11349 {
11350 Assert(aOnline);
11351 rc = mData->mSession.mLockedMedia.Lock();
11352 AssertComRC(rc);
11353 }
11354
11355 /* commit the hard disk changes */
11356 mMediaData.commit();
11357
11358 if (isSessionMachine())
11359 {
11360 /*
11361 * Update the parent machine to point to the new owner.
11362 * This is necessary because the stored parent will point to the
11363 * session machine otherwise and cause crashes or errors later
11364 * when the session machine gets invalid.
11365 */
11366 /** @todo Change the MediumAttachment class to behave like any other
11367 * class in this regard by creating peer MediumAttachment
11368 * objects for session machines and share the data with the peer
11369 * machine.
11370 */
11371 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11372 it != mMediaData->mAttachments.end();
11373 ++it)
11374 {
11375 (*it)->updateParentMachine(mPeer);
11376 }
11377
11378 /* attach new data to the primary machine and reshare it */
11379 mPeer->mMediaData.attach(mMediaData);
11380 }
11381
11382 return;
11383}
11384
11385/**
11386 * Perform deferred deletion of implicitly created diffs.
11387 *
11388 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11389 * backed up).
11390 *
11391 * @note Locks this object for writing!
11392 */
11393void Machine::rollbackMedia()
11394{
11395 AutoCaller autoCaller(this);
11396 AssertComRCReturnVoid(autoCaller.rc());
11397
11398 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11399 LogFlowThisFunc(("Entering rollbackMedia\n"));
11400
11401 HRESULT rc = S_OK;
11402
11403 /* no attach/detach operations -- nothing to do */
11404 if (!mMediaData.isBackedUp())
11405 return;
11406
11407 /* enumerate new attachments */
11408 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11409 it != mMediaData->mAttachments.end();
11410 ++it)
11411 {
11412 MediumAttachment *pAttach = *it;
11413 /* Fix up the backrefs for DVD/floppy media. */
11414 if (pAttach->getType() != DeviceType_HardDisk)
11415 {
11416 Medium* pMedium = pAttach->getMedium();
11417 if (pMedium)
11418 {
11419 rc = pMedium->removeBackReference(mData->mUuid);
11420 AssertComRC(rc);
11421 }
11422 }
11423
11424 (*it)->rollback();
11425
11426 pAttach = *it;
11427 /* Fix up the backrefs for DVD/floppy media. */
11428 if (pAttach->getType() != DeviceType_HardDisk)
11429 {
11430 Medium* pMedium = pAttach->getMedium();
11431 if (pMedium)
11432 {
11433 rc = pMedium->addBackReference(mData->mUuid);
11434 AssertComRC(rc);
11435 }
11436 }
11437 }
11438
11439 /** @todo convert all this Machine-based voodoo to MediumAttachment
11440 * based rollback logic. */
11441 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11442
11443 return;
11444}
11445
11446/**
11447 * Returns true if the settings file is located in the directory named exactly
11448 * as the machine; this means, among other things, that the machine directory
11449 * should be auto-renamed.
11450 *
11451 * @param aSettingsDir if not NULL, the full machine settings file directory
11452 * name will be assigned there.
11453 *
11454 * @note Doesn't lock anything.
11455 * @note Not thread safe (must be called from this object's lock).
11456 */
11457bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11458{
11459 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11460 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11461 if (aSettingsDir)
11462 *aSettingsDir = strMachineDirName;
11463 strMachineDirName.stripPath(); // vmname
11464 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11465 strConfigFileOnly.stripPath() // vmname.vbox
11466 .stripExt(); // vmname
11467 /** @todo hack, make somehow use of ComposeMachineFilename */
11468 if (mUserData->s.fDirectoryIncludesUUID)
11469 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11470
11471 AssertReturn(!strMachineDirName.isEmpty(), false);
11472 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11473
11474 return strMachineDirName == strConfigFileOnly;
11475}
11476
11477/**
11478 * Discards all changes to machine settings.
11479 *
11480 * @param aNotify Whether to notify the direct session about changes or not.
11481 *
11482 * @note Locks objects for writing!
11483 */
11484void Machine::rollback(bool aNotify)
11485{
11486 AutoCaller autoCaller(this);
11487 AssertComRCReturn(autoCaller.rc(), (void)0);
11488
11489 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11490
11491 if (!mStorageControllers.isNull())
11492 {
11493 if (mStorageControllers.isBackedUp())
11494 {
11495 /* unitialize all new devices (absent in the backed up list). */
11496 StorageControllerList::const_iterator it = mStorageControllers->begin();
11497 StorageControllerList *backedList = mStorageControllers.backedUpData();
11498 while (it != mStorageControllers->end())
11499 {
11500 if ( std::find(backedList->begin(), backedList->end(), *it)
11501 == backedList->end()
11502 )
11503 {
11504 (*it)->uninit();
11505 }
11506 ++it;
11507 }
11508
11509 /* restore the list */
11510 mStorageControllers.rollback();
11511 }
11512
11513 /* rollback any changes to devices after restoring the list */
11514 if (mData->flModifications & IsModified_Storage)
11515 {
11516 StorageControllerList::const_iterator it = mStorageControllers->begin();
11517 while (it != mStorageControllers->end())
11518 {
11519 (*it)->rollback();
11520 ++it;
11521 }
11522 }
11523 }
11524
11525 mUserData.rollback();
11526
11527 mHWData.rollback();
11528
11529 if (mData->flModifications & IsModified_Storage)
11530 rollbackMedia();
11531
11532 if (mBIOSSettings)
11533 mBIOSSettings->rollback();
11534
11535 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11536 mVRDEServer->rollback();
11537
11538 if (mAudioAdapter)
11539 mAudioAdapter->rollback();
11540
11541 if (mUSBController && (mData->flModifications & IsModified_USB))
11542 mUSBController->rollback();
11543
11544 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11545 mBandwidthControl->rollback();
11546
11547 if (!mHWData.isNull())
11548 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11549 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11550 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11551 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11552
11553 if (mData->flModifications & IsModified_NetworkAdapters)
11554 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11555 if ( mNetworkAdapters[slot]
11556 && mNetworkAdapters[slot]->isModified())
11557 {
11558 mNetworkAdapters[slot]->rollback();
11559 networkAdapters[slot] = mNetworkAdapters[slot];
11560 }
11561
11562 if (mData->flModifications & IsModified_SerialPorts)
11563 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11564 if ( mSerialPorts[slot]
11565 && mSerialPorts[slot]->isModified())
11566 {
11567 mSerialPorts[slot]->rollback();
11568 serialPorts[slot] = mSerialPorts[slot];
11569 }
11570
11571 if (mData->flModifications & IsModified_ParallelPorts)
11572 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11573 if ( mParallelPorts[slot]
11574 && mParallelPorts[slot]->isModified())
11575 {
11576 mParallelPorts[slot]->rollback();
11577 parallelPorts[slot] = mParallelPorts[slot];
11578 }
11579
11580 if (aNotify)
11581 {
11582 /* inform the direct session about changes */
11583
11584 ComObjPtr<Machine> that = this;
11585 uint32_t flModifications = mData->flModifications;
11586 alock.release();
11587
11588 if (flModifications & IsModified_SharedFolders)
11589 that->onSharedFolderChange();
11590
11591 if (flModifications & IsModified_VRDEServer)
11592 that->onVRDEServerChange(/* aRestart */ TRUE);
11593 if (flModifications & IsModified_USB)
11594 that->onUSBControllerChange();
11595
11596 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
11597 if (networkAdapters[slot])
11598 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
11599 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
11600 if (serialPorts[slot])
11601 that->onSerialPortChange(serialPorts[slot]);
11602 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
11603 if (parallelPorts[slot])
11604 that->onParallelPortChange(parallelPorts[slot]);
11605
11606 if (flModifications & IsModified_Storage)
11607 that->onStorageControllerChange();
11608
11609#if 0
11610 if (flModifications & IsModified_BandwidthControl)
11611 that->onBandwidthControlChange();
11612#endif
11613 }
11614}
11615
11616/**
11617 * Commits all the changes to machine settings.
11618 *
11619 * Note that this operation is supposed to never fail.
11620 *
11621 * @note Locks this object and children for writing.
11622 */
11623void Machine::commit()
11624{
11625 AutoCaller autoCaller(this);
11626 AssertComRCReturnVoid(autoCaller.rc());
11627
11628 AutoCaller peerCaller(mPeer);
11629 AssertComRCReturnVoid(peerCaller.rc());
11630
11631 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11632
11633 /*
11634 * use safe commit to ensure Snapshot machines (that share mUserData)
11635 * will still refer to a valid memory location
11636 */
11637 mUserData.commitCopy();
11638
11639 mHWData.commit();
11640
11641 if (mMediaData.isBackedUp())
11642 commitMedia(Global::IsOnline(mData->mMachineState));
11643
11644 mBIOSSettings->commit();
11645 mVRDEServer->commit();
11646 mAudioAdapter->commit();
11647 mUSBController->commit();
11648 mBandwidthControl->commit();
11649
11650 /* Since mNetworkAdapters is a list which might have been changed (resized)
11651 * without using the Backupable<> template we need to handle the copying
11652 * of the list entries manually, including the creation of peers for the
11653 * new objects. */
11654 bool commitNetworkAdapters = false;
11655 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11656 if (mPeer)
11657 {
11658 /* commit everything, even the ones which will go away */
11659 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11660 mNetworkAdapters[slot]->commit();
11661 /* copy over the new entries, creating a peer and uninit the original */
11662 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11663 for (size_t slot = 0; slot < newSize; slot++)
11664 {
11665 /* look if this adapter has a peer device */
11666 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
11667 if (!peer)
11668 {
11669 /* no peer means the adapter is a newly created one;
11670 * create a peer owning data this data share it with */
11671 peer.createObject();
11672 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11673 }
11674 mPeer->mNetworkAdapters[slot] = peer;
11675 }
11676 /* uninit any no longer needed network adapters */
11677 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
11678 mNetworkAdapters[slot]->uninit();
11679 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
11680 {
11681 if (mPeer->mNetworkAdapters[slot])
11682 mPeer->mNetworkAdapters[slot]->uninit();
11683 }
11684 /* Keep the original network adapter count until this point, so that
11685 * discarding a chipset type change will not lose settings. */
11686 mNetworkAdapters.resize(newSize);
11687 mPeer->mNetworkAdapters.resize(newSize);
11688 }
11689 else
11690 {
11691 /* we have no peer (our parent is the newly created machine);
11692 * just commit changes to the network adapters */
11693 commitNetworkAdapters = true;
11694 }
11695 if (commitNetworkAdapters)
11696 {
11697 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11698 mNetworkAdapters[slot]->commit();
11699 }
11700
11701 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11702 mSerialPorts[slot]->commit();
11703 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11704 mParallelPorts[slot]->commit();
11705
11706 bool commitStorageControllers = false;
11707
11708 if (mStorageControllers.isBackedUp())
11709 {
11710 mStorageControllers.commit();
11711
11712 if (mPeer)
11713 {
11714 /* Commit all changes to new controllers (this will reshare data with
11715 * peers for those who have peers) */
11716 StorageControllerList *newList = new StorageControllerList();
11717 StorageControllerList::const_iterator it = mStorageControllers->begin();
11718 while (it != mStorageControllers->end())
11719 {
11720 (*it)->commit();
11721
11722 /* look if this controller has a peer device */
11723 ComObjPtr<StorageController> peer = (*it)->getPeer();
11724 if (!peer)
11725 {
11726 /* no peer means the device is a newly created one;
11727 * create a peer owning data this device share it with */
11728 peer.createObject();
11729 peer->init(mPeer, *it, true /* aReshare */);
11730 }
11731 else
11732 {
11733 /* remove peer from the old list */
11734 mPeer->mStorageControllers->remove(peer);
11735 }
11736 /* and add it to the new list */
11737 newList->push_back(peer);
11738
11739 ++it;
11740 }
11741
11742 /* uninit old peer's controllers that are left */
11743 it = mPeer->mStorageControllers->begin();
11744 while (it != mPeer->mStorageControllers->end())
11745 {
11746 (*it)->uninit();
11747 ++it;
11748 }
11749
11750 /* attach new list of controllers to our peer */
11751 mPeer->mStorageControllers.attach(newList);
11752 }
11753 else
11754 {
11755 /* we have no peer (our parent is the newly created machine);
11756 * just commit changes to devices */
11757 commitStorageControllers = true;
11758 }
11759 }
11760 else
11761 {
11762 /* the list of controllers itself is not changed,
11763 * just commit changes to controllers themselves */
11764 commitStorageControllers = true;
11765 }
11766
11767 if (commitStorageControllers)
11768 {
11769 StorageControllerList::const_iterator it = mStorageControllers->begin();
11770 while (it != mStorageControllers->end())
11771 {
11772 (*it)->commit();
11773 ++it;
11774 }
11775 }
11776
11777 if (isSessionMachine())
11778 {
11779 /* attach new data to the primary machine and reshare it */
11780 mPeer->mUserData.attach(mUserData);
11781 mPeer->mHWData.attach(mHWData);
11782 /* mMediaData is reshared by fixupMedia */
11783 // mPeer->mMediaData.attach(mMediaData);
11784 Assert(mPeer->mMediaData.data() == mMediaData.data());
11785 }
11786}
11787
11788/**
11789 * Copies all the hardware data from the given machine.
11790 *
11791 * Currently, only called when the VM is being restored from a snapshot. In
11792 * particular, this implies that the VM is not running during this method's
11793 * call.
11794 *
11795 * @note This method must be called from under this object's lock.
11796 *
11797 * @note This method doesn't call #commit(), so all data remains backed up and
11798 * unsaved.
11799 */
11800void Machine::copyFrom(Machine *aThat)
11801{
11802 AssertReturnVoid(!isSnapshotMachine());
11803 AssertReturnVoid(aThat->isSnapshotMachine());
11804
11805 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11806
11807 mHWData.assignCopy(aThat->mHWData);
11808
11809 // create copies of all shared folders (mHWData after attaching a copy
11810 // contains just references to original objects)
11811 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11812 it != mHWData->mSharedFolders.end();
11813 ++it)
11814 {
11815 ComObjPtr<SharedFolder> folder;
11816 folder.createObject();
11817 HRESULT rc = folder->initCopy(getMachine(), *it);
11818 AssertComRC(rc);
11819 *it = folder;
11820 }
11821
11822 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
11823 mVRDEServer->copyFrom(aThat->mVRDEServer);
11824 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
11825 mUSBController->copyFrom(aThat->mUSBController);
11826 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
11827
11828 /* create private copies of all controllers */
11829 mStorageControllers.backup();
11830 mStorageControllers->clear();
11831 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11832 it != aThat->mStorageControllers->end();
11833 ++it)
11834 {
11835 ComObjPtr<StorageController> ctrl;
11836 ctrl.createObject();
11837 ctrl->initCopy(this, *it);
11838 mStorageControllers->push_back(ctrl);
11839 }
11840
11841 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11842 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11843 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
11844 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11845 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
11846 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11847 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
11848}
11849
11850/**
11851 * Returns whether the given storage controller is hotplug capable.
11852 *
11853 * @returns true if the controller supports hotplugging
11854 * false otherwise.
11855 * @param enmCtrlType The controller type to check for.
11856 */
11857bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11858{
11859 switch (enmCtrlType)
11860 {
11861 case StorageControllerType_IntelAhci:
11862 return true;
11863 case StorageControllerType_LsiLogic:
11864 case StorageControllerType_LsiLogicSas:
11865 case StorageControllerType_BusLogic:
11866 case StorageControllerType_PIIX3:
11867 case StorageControllerType_PIIX4:
11868 case StorageControllerType_ICH6:
11869 case StorageControllerType_I82078:
11870 default:
11871 return false;
11872 }
11873}
11874
11875#ifdef VBOX_WITH_RESOURCE_USAGE_API
11876
11877void Machine::getDiskList(MediaList &list)
11878{
11879 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11880 it != mMediaData->mAttachments.end();
11881 ++it)
11882 {
11883 MediumAttachment* pAttach = *it;
11884 /* just in case */
11885 AssertStmt(pAttach, continue);
11886
11887 AutoCaller localAutoCallerA(pAttach);
11888 if (FAILED(localAutoCallerA.rc())) continue;
11889
11890 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11891
11892 if (pAttach->getType() == DeviceType_HardDisk)
11893 list.push_back(pAttach->getMedium());
11894 }
11895}
11896
11897void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11898{
11899 AssertReturnVoid(isWriteLockOnCurrentThread());
11900 AssertPtrReturnVoid(aCollector);
11901
11902 pm::CollectorHAL *hal = aCollector->getHAL();
11903 /* Create sub metrics */
11904 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11905 "Percentage of processor time spent in user mode by the VM process.");
11906 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11907 "Percentage of processor time spent in kernel mode by the VM process.");
11908 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11909 "Size of resident portion of VM process in memory.");
11910 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11911 "Actual size of all VM disks combined.");
11912 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11913 "Network receive rate.");
11914 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11915 "Network transmit rate.");
11916 /* Create and register base metrics */
11917 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11918 cpuLoadUser, cpuLoadKernel);
11919 aCollector->registerBaseMetric(cpuLoad);
11920 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11921 ramUsageUsed);
11922 aCollector->registerBaseMetric(ramUsage);
11923 MediaList disks;
11924 getDiskList(disks);
11925 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11926 diskUsageUsed);
11927 aCollector->registerBaseMetric(diskUsage);
11928
11929 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11930 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11931 new pm::AggregateAvg()));
11932 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11933 new pm::AggregateMin()));
11934 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11935 new pm::AggregateMax()));
11936 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11937 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11938 new pm::AggregateAvg()));
11939 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11940 new pm::AggregateMin()));
11941 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11942 new pm::AggregateMax()));
11943
11944 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11945 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11946 new pm::AggregateAvg()));
11947 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11948 new pm::AggregateMin()));
11949 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11950 new pm::AggregateMax()));
11951
11952 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11953 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11954 new pm::AggregateAvg()));
11955 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11956 new pm::AggregateMin()));
11957 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11958 new pm::AggregateMax()));
11959
11960
11961 /* Guest metrics collector */
11962 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11963 aCollector->registerGuest(mCollectorGuest);
11964 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11965 this, __PRETTY_FUNCTION__, mCollectorGuest));
11966
11967 /* Create sub metrics */
11968 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11969 "Percentage of processor time spent in user mode as seen by the guest.");
11970 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11971 "Percentage of processor time spent in kernel mode as seen by the guest.");
11972 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11973 "Percentage of processor time spent idling as seen by the guest.");
11974
11975 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11976 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11977 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11978 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11979 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11980 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11981
11982 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11983
11984 /* Create and register base metrics */
11985 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
11986 machineNetRx, machineNetTx);
11987 aCollector->registerBaseMetric(machineNetRate);
11988
11989 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11990 guestLoadUser, guestLoadKernel, guestLoadIdle);
11991 aCollector->registerBaseMetric(guestCpuLoad);
11992
11993 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11994 guestMemTotal, guestMemFree,
11995 guestMemBalloon, guestMemShared,
11996 guestMemCache, guestPagedTotal);
11997 aCollector->registerBaseMetric(guestCpuMem);
11998
11999 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12000 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12001 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12002 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12003
12004 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12005 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12006 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12007 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12008
12009 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12010 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12011 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12012 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12013
12014 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12015 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12016 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12017 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12018
12019 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12020 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12021 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12022 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12023
12024 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12025 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12026 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12027 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12028
12029 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12030 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12031 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12032 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12033
12034 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12035 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12036 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12037 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12038
12039 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12040 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12041 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12042 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12043
12044 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12045 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12046 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12047 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12048
12049 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12050 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12051 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12052 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12053}
12054
12055void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12056{
12057 AssertReturnVoid(isWriteLockOnCurrentThread());
12058
12059 if (aCollector)
12060 {
12061 aCollector->unregisterMetricsFor(aMachine);
12062 aCollector->unregisterBaseMetricsFor(aMachine);
12063 }
12064}
12065
12066#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12067
12068
12069////////////////////////////////////////////////////////////////////////////////
12070
12071DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12072
12073HRESULT SessionMachine::FinalConstruct()
12074{
12075 LogFlowThisFunc(("\n"));
12076
12077#if defined(RT_OS_WINDOWS)
12078 mIPCSem = NULL;
12079#elif defined(RT_OS_OS2)
12080 mIPCSem = NULLHANDLE;
12081#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12082 mIPCSem = -1;
12083#else
12084# error "Port me!"
12085#endif
12086
12087 return BaseFinalConstruct();
12088}
12089
12090void SessionMachine::FinalRelease()
12091{
12092 LogFlowThisFunc(("\n"));
12093
12094 uninit(Uninit::Unexpected);
12095
12096 BaseFinalRelease();
12097}
12098
12099/**
12100 * @note Must be called only by Machine::openSession() from its own write lock.
12101 */
12102HRESULT SessionMachine::init(Machine *aMachine)
12103{
12104 LogFlowThisFuncEnter();
12105 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12106
12107 AssertReturn(aMachine, E_INVALIDARG);
12108
12109 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12110
12111 /* Enclose the state transition NotReady->InInit->Ready */
12112 AutoInitSpan autoInitSpan(this);
12113 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12114
12115 /* create the interprocess semaphore */
12116#if defined(RT_OS_WINDOWS)
12117 mIPCSemName = aMachine->mData->m_strConfigFileFull;
12118 for (size_t i = 0; i < mIPCSemName.length(); i++)
12119 if (mIPCSemName.raw()[i] == '\\')
12120 mIPCSemName.raw()[i] = '/';
12121 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
12122 ComAssertMsgRet(mIPCSem,
12123 ("Cannot create IPC mutex '%ls', err=%d",
12124 mIPCSemName.raw(), ::GetLastError()),
12125 E_FAIL);
12126#elif defined(RT_OS_OS2)
12127 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
12128 aMachine->mData->mUuid.raw());
12129 mIPCSemName = ipcSem;
12130 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
12131 ComAssertMsgRet(arc == NO_ERROR,
12132 ("Cannot create IPC mutex '%s', arc=%ld",
12133 ipcSem.c_str(), arc),
12134 E_FAIL);
12135#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12136# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12137# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
12138 /** @todo Check that this still works correctly. */
12139 AssertCompileSize(key_t, 8);
12140# else
12141 AssertCompileSize(key_t, 4);
12142# endif
12143 key_t key;
12144 mIPCSem = -1;
12145 mIPCKey = "0";
12146 for (uint32_t i = 0; i < 1 << 24; i++)
12147 {
12148 key = ((uint32_t)'V' << 24) | i;
12149 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
12150 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
12151 {
12152 mIPCSem = sem;
12153 if (sem >= 0)
12154 mIPCKey = BstrFmt("%u", key);
12155 break;
12156 }
12157 }
12158# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12159 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
12160 char *pszSemName = NULL;
12161 RTStrUtf8ToCurrentCP(&pszSemName, semName);
12162 key_t key = ::ftok(pszSemName, 'V');
12163 RTStrFree(pszSemName);
12164
12165 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
12166# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12167
12168 int errnoSave = errno;
12169 if (mIPCSem < 0 && errnoSave == ENOSYS)
12170 {
12171 setError(E_FAIL,
12172 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
12173 "support for SysV IPC. Check the host kernel configuration for "
12174 "CONFIG_SYSVIPC=y"));
12175 return E_FAIL;
12176 }
12177 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
12178 * the IPC semaphores */
12179 if (mIPCSem < 0 && errnoSave == ENOSPC)
12180 {
12181#ifdef RT_OS_LINUX
12182 setError(E_FAIL,
12183 tr("Cannot create IPC semaphore because the system limit for the "
12184 "maximum number of semaphore sets (SEMMNI), or the system wide "
12185 "maximum number of semaphores (SEMMNS) would be exceeded. The "
12186 "current set of SysV IPC semaphores can be determined from "
12187 "the file /proc/sysvipc/sem"));
12188#else
12189 setError(E_FAIL,
12190 tr("Cannot create IPC semaphore because the system-imposed limit "
12191 "on the maximum number of allowed semaphores or semaphore "
12192 "identifiers system-wide would be exceeded"));
12193#endif
12194 return E_FAIL;
12195 }
12196 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
12197 E_FAIL);
12198 /* set the initial value to 1 */
12199 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
12200 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
12201 E_FAIL);
12202#else
12203# error "Port me!"
12204#endif
12205
12206 /* memorize the peer Machine */
12207 unconst(mPeer) = aMachine;
12208 /* share the parent pointer */
12209 unconst(mParent) = aMachine->mParent;
12210
12211 /* take the pointers to data to share */
12212 mData.share(aMachine->mData);
12213 mSSData.share(aMachine->mSSData);
12214
12215 mUserData.share(aMachine->mUserData);
12216 mHWData.share(aMachine->mHWData);
12217 mMediaData.share(aMachine->mMediaData);
12218
12219 mStorageControllers.allocate();
12220 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12221 it != aMachine->mStorageControllers->end();
12222 ++it)
12223 {
12224 ComObjPtr<StorageController> ctl;
12225 ctl.createObject();
12226 ctl->init(this, *it);
12227 mStorageControllers->push_back(ctl);
12228 }
12229
12230 unconst(mBIOSSettings).createObject();
12231 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12232 /* create another VRDEServer object that will be mutable */
12233 unconst(mVRDEServer).createObject();
12234 mVRDEServer->init(this, aMachine->mVRDEServer);
12235 /* create another audio adapter object that will be mutable */
12236 unconst(mAudioAdapter).createObject();
12237 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12238 /* create a list of serial ports that will be mutable */
12239 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12240 {
12241 unconst(mSerialPorts[slot]).createObject();
12242 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12243 }
12244 /* create a list of parallel ports that will be mutable */
12245 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12246 {
12247 unconst(mParallelPorts[slot]).createObject();
12248 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12249 }
12250 /* create another USB controller object that will be mutable */
12251 unconst(mUSBController).createObject();
12252 mUSBController->init(this, aMachine->mUSBController);
12253
12254 /* create a list of network adapters that will be mutable */
12255 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12256 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12257 {
12258 unconst(mNetworkAdapters[slot]).createObject();
12259 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12260 }
12261
12262 /* create another bandwidth control object that will be mutable */
12263 unconst(mBandwidthControl).createObject();
12264 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12265
12266 /* default is to delete saved state on Saved -> PoweredOff transition */
12267 mRemoveSavedState = true;
12268
12269 /* Confirm a successful initialization when it's the case */
12270 autoInitSpan.setSucceeded();
12271
12272 LogFlowThisFuncLeave();
12273 return S_OK;
12274}
12275
12276/**
12277 * Uninitializes this session object. If the reason is other than
12278 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
12279 *
12280 * @param aReason uninitialization reason
12281 *
12282 * @note Locks mParent + this object for writing.
12283 */
12284void SessionMachine::uninit(Uninit::Reason aReason)
12285{
12286 LogFlowThisFuncEnter();
12287 LogFlowThisFunc(("reason=%d\n", aReason));
12288
12289 /*
12290 * Strongly reference ourselves to prevent this object deletion after
12291 * mData->mSession.mMachine.setNull() below (which can release the last
12292 * reference and call the destructor). Important: this must be done before
12293 * accessing any members (and before AutoUninitSpan that does it as well).
12294 * This self reference will be released as the very last step on return.
12295 */
12296 ComObjPtr<SessionMachine> selfRef = this;
12297
12298 /* Enclose the state transition Ready->InUninit->NotReady */
12299 AutoUninitSpan autoUninitSpan(this);
12300 if (autoUninitSpan.uninitDone())
12301 {
12302 LogFlowThisFunc(("Already uninitialized\n"));
12303 LogFlowThisFuncLeave();
12304 return;
12305 }
12306
12307 if (autoUninitSpan.initFailed())
12308 {
12309 /* We've been called by init() because it's failed. It's not really
12310 * necessary (nor it's safe) to perform the regular uninit sequence
12311 * below, the following is enough.
12312 */
12313 LogFlowThisFunc(("Initialization failed.\n"));
12314#if defined(RT_OS_WINDOWS)
12315 if (mIPCSem)
12316 ::CloseHandle(mIPCSem);
12317 mIPCSem = NULL;
12318#elif defined(RT_OS_OS2)
12319 if (mIPCSem != NULLHANDLE)
12320 ::DosCloseMutexSem(mIPCSem);
12321 mIPCSem = NULLHANDLE;
12322#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12323 if (mIPCSem >= 0)
12324 ::semctl(mIPCSem, 0, IPC_RMID);
12325 mIPCSem = -1;
12326# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12327 mIPCKey = "0";
12328# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12329#else
12330# error "Port me!"
12331#endif
12332 uninitDataAndChildObjects();
12333 mData.free();
12334 unconst(mParent) = NULL;
12335 unconst(mPeer) = NULL;
12336 LogFlowThisFuncLeave();
12337 return;
12338 }
12339
12340 MachineState_T lastState;
12341 {
12342 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12343 lastState = mData->mMachineState;
12344 }
12345 NOREF(lastState);
12346
12347#ifdef VBOX_WITH_USB
12348 // release all captured USB devices, but do this before requesting the locks below
12349 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12350 {
12351 /* Console::captureUSBDevices() is called in the VM process only after
12352 * setting the machine state to Starting or Restoring.
12353 * Console::detachAllUSBDevices() will be called upon successful
12354 * termination. So, we need to release USB devices only if there was
12355 * an abnormal termination of a running VM.
12356 *
12357 * This is identical to SessionMachine::DetachAllUSBDevices except
12358 * for the aAbnormal argument. */
12359 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12360 AssertComRC(rc);
12361 NOREF(rc);
12362
12363 USBProxyService *service = mParent->host()->usbProxyService();
12364 if (service)
12365 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12366 }
12367#endif /* VBOX_WITH_USB */
12368
12369 // we need to lock this object in uninit() because the lock is shared
12370 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12371 // and others need mParent lock, and USB needs host lock.
12372 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12373
12374#if 0
12375 // Trigger async cleanup tasks, avoid doing things here which are not
12376 // vital to be done immediately and maybe need more locks. This calls
12377 // Machine::unregisterMetrics().
12378 mParent->onMachineUninit(mPeer);
12379#else
12380 /*
12381 * It is safe to call Machine::unregisterMetrics() here because
12382 * PerformanceCollector::samplerCallback no longer accesses guest methods
12383 * holding the lock.
12384 */
12385 unregisterMetrics(mParent->performanceCollector(), mPeer);
12386#endif
12387 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12388 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12389 this, __PRETTY_FUNCTION__, mCollectorGuest));
12390 if (mCollectorGuest)
12391 {
12392 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
12393 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12394 mCollectorGuest = NULL;
12395 }
12396
12397 if (aReason == Uninit::Abnormal)
12398 {
12399 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12400 Global::IsOnlineOrTransient(lastState)));
12401
12402 /* reset the state to Aborted */
12403 if (mData->mMachineState != MachineState_Aborted)
12404 setMachineState(MachineState_Aborted);
12405 }
12406
12407 // any machine settings modified?
12408 if (mData->flModifications)
12409 {
12410 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12411 rollback(false /* aNotify */);
12412 }
12413
12414 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12415 || !mConsoleTaskData.mSnapshot);
12416 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12417 {
12418 LogWarningThisFunc(("canceling failed save state request!\n"));
12419 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12420 }
12421 else if (!mConsoleTaskData.mSnapshot.isNull())
12422 {
12423 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12424
12425 /* delete all differencing hard disks created (this will also attach
12426 * their parents back by rolling back mMediaData) */
12427 rollbackMedia();
12428
12429 // delete the saved state file (it might have been already created)
12430 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12431 // think it's still in use
12432 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12433 mConsoleTaskData.mSnapshot->uninit();
12434 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12435 }
12436
12437 if (!mData->mSession.mType.isEmpty())
12438 {
12439 /* mType is not null when this machine's process has been started by
12440 * Machine::LaunchVMProcess(), therefore it is our child. We
12441 * need to queue the PID to reap the process (and avoid zombies on
12442 * Linux). */
12443 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12444 mParent->addProcessToReap(mData->mSession.mPID);
12445 }
12446
12447 mData->mSession.mPID = NIL_RTPROCESS;
12448
12449 if (aReason == Uninit::Unexpected)
12450 {
12451 /* Uninitialization didn't come from #checkForDeath(), so tell the
12452 * client watcher thread to update the set of machines that have open
12453 * sessions. */
12454 mParent->updateClientWatcher();
12455 }
12456
12457 /* uninitialize all remote controls */
12458 if (mData->mSession.mRemoteControls.size())
12459 {
12460 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12461 mData->mSession.mRemoteControls.size()));
12462
12463 Data::Session::RemoteControlList::iterator it =
12464 mData->mSession.mRemoteControls.begin();
12465 while (it != mData->mSession.mRemoteControls.end())
12466 {
12467 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12468 HRESULT rc = (*it)->Uninitialize();
12469 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12470 if (FAILED(rc))
12471 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12472 ++it;
12473 }
12474 mData->mSession.mRemoteControls.clear();
12475 }
12476
12477 /*
12478 * An expected uninitialization can come only from #checkForDeath().
12479 * Otherwise it means that something's gone really wrong (for example,
12480 * the Session implementation has released the VirtualBox reference
12481 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12482 * etc). However, it's also possible, that the client releases the IPC
12483 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12484 * but the VirtualBox release event comes first to the server process.
12485 * This case is practically possible, so we should not assert on an
12486 * unexpected uninit, just log a warning.
12487 */
12488
12489 if ((aReason == Uninit::Unexpected))
12490 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12491
12492 if (aReason != Uninit::Normal)
12493 {
12494 mData->mSession.mDirectControl.setNull();
12495 }
12496 else
12497 {
12498 /* this must be null here (see #OnSessionEnd()) */
12499 Assert(mData->mSession.mDirectControl.isNull());
12500 Assert(mData->mSession.mState == SessionState_Unlocking);
12501 Assert(!mData->mSession.mProgress.isNull());
12502 }
12503 if (mData->mSession.mProgress)
12504 {
12505 if (aReason == Uninit::Normal)
12506 mData->mSession.mProgress->notifyComplete(S_OK);
12507 else
12508 mData->mSession.mProgress->notifyComplete(E_FAIL,
12509 COM_IIDOF(ISession),
12510 getComponentName(),
12511 tr("The VM session was aborted"));
12512 mData->mSession.mProgress.setNull();
12513 }
12514
12515 /* remove the association between the peer machine and this session machine */
12516 Assert( (SessionMachine*)mData->mSession.mMachine == this
12517 || aReason == Uninit::Unexpected);
12518
12519 /* reset the rest of session data */
12520 mData->mSession.mMachine.setNull();
12521 mData->mSession.mState = SessionState_Unlocked;
12522 mData->mSession.mType.setNull();
12523
12524 /* close the interprocess semaphore before leaving the exclusive lock */
12525#if defined(RT_OS_WINDOWS)
12526 if (mIPCSem)
12527 ::CloseHandle(mIPCSem);
12528 mIPCSem = NULL;
12529#elif defined(RT_OS_OS2)
12530 if (mIPCSem != NULLHANDLE)
12531 ::DosCloseMutexSem(mIPCSem);
12532 mIPCSem = NULLHANDLE;
12533#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12534 if (mIPCSem >= 0)
12535 ::semctl(mIPCSem, 0, IPC_RMID);
12536 mIPCSem = -1;
12537# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12538 mIPCKey = "0";
12539# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12540#else
12541# error "Port me!"
12542#endif
12543
12544 /* fire an event */
12545 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12546
12547 uninitDataAndChildObjects();
12548
12549 /* free the essential data structure last */
12550 mData.free();
12551
12552 /* release the exclusive lock before setting the below two to NULL */
12553 multilock.release();
12554
12555 unconst(mParent) = NULL;
12556 unconst(mPeer) = NULL;
12557
12558 LogFlowThisFuncLeave();
12559}
12560
12561// util::Lockable interface
12562////////////////////////////////////////////////////////////////////////////////
12563
12564/**
12565 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12566 * with the primary Machine instance (mPeer).
12567 */
12568RWLockHandle *SessionMachine::lockHandle() const
12569{
12570 AssertReturn(mPeer != NULL, NULL);
12571 return mPeer->lockHandle();
12572}
12573
12574// IInternalMachineControl methods
12575////////////////////////////////////////////////////////////////////////////////
12576
12577/**
12578 * Passes collected guest statistics to performance collector object
12579 */
12580STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12581 ULONG aCpuKernel, ULONG aCpuIdle,
12582 ULONG aMemTotal, ULONG aMemFree,
12583 ULONG aMemBalloon, ULONG aMemShared,
12584 ULONG aMemCache, ULONG aPageTotal,
12585 ULONG aAllocVMM, ULONG aFreeVMM,
12586 ULONG aBalloonedVMM, ULONG aSharedVMM,
12587 ULONG aVmNetRx, ULONG aVmNetTx)
12588{
12589 if (mCollectorGuest)
12590 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12591 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12592 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12593 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12594
12595 return S_OK;
12596}
12597
12598/**
12599 * @note Locks this object for writing.
12600 */
12601STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12602{
12603 AutoCaller autoCaller(this);
12604 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12605
12606 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12607
12608 mRemoveSavedState = aRemove;
12609
12610 return S_OK;
12611}
12612
12613/**
12614 * @note Locks the same as #setMachineState() does.
12615 */
12616STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12617{
12618 return setMachineState(aMachineState);
12619}
12620
12621/**
12622 * @note Locks this object for reading.
12623 */
12624STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
12625{
12626 AutoCaller autoCaller(this);
12627 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12628
12629 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12630
12631#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
12632 mIPCSemName.cloneTo(aId);
12633 return S_OK;
12634#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12635# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12636 mIPCKey.cloneTo(aId);
12637# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12638 mData->m_strConfigFileFull.cloneTo(aId);
12639# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12640 return S_OK;
12641#else
12642# error "Port me!"
12643#endif
12644}
12645
12646/**
12647 * @note Locks this object for writing.
12648 */
12649STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12650{
12651 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12652 AutoCaller autoCaller(this);
12653 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12654
12655 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12656
12657 if (mData->mSession.mState != SessionState_Locked)
12658 return VBOX_E_INVALID_OBJECT_STATE;
12659
12660 if (!mData->mSession.mProgress.isNull())
12661 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12662
12663 LogFlowThisFunc(("returns S_OK.\n"));
12664 return S_OK;
12665}
12666
12667/**
12668 * @note Locks this object for writing.
12669 */
12670STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12671{
12672 AutoCaller autoCaller(this);
12673 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12674
12675 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12676
12677 if (mData->mSession.mState != SessionState_Locked)
12678 return VBOX_E_INVALID_OBJECT_STATE;
12679
12680 /* Finalize the LaunchVMProcess progress object. */
12681 if (mData->mSession.mProgress)
12682 {
12683 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12684 mData->mSession.mProgress.setNull();
12685 }
12686
12687 if (SUCCEEDED((HRESULT)iResult))
12688 {
12689#ifdef VBOX_WITH_RESOURCE_USAGE_API
12690 /* The VM has been powered up successfully, so it makes sense
12691 * now to offer the performance metrics for a running machine
12692 * object. Doing it earlier wouldn't be safe. */
12693 registerMetrics(mParent->performanceCollector(), mPeer,
12694 mData->mSession.mPID);
12695#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12696 }
12697
12698 return S_OK;
12699}
12700
12701/**
12702 * @note Locks this object for writing.
12703 */
12704STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12705{
12706 LogFlowThisFuncEnter();
12707
12708 CheckComArgOutPointerValid(aProgress);
12709
12710 AutoCaller autoCaller(this);
12711 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12712
12713 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12714
12715 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12716 E_FAIL);
12717
12718 /* create a progress object to track operation completion */
12719 ComObjPtr<Progress> pProgress;
12720 pProgress.createObject();
12721 pProgress->init(getVirtualBox(),
12722 static_cast<IMachine *>(this) /* aInitiator */,
12723 Bstr(tr("Stopping the virtual machine")).raw(),
12724 FALSE /* aCancelable */);
12725
12726 /* fill in the console task data */
12727 mConsoleTaskData.mLastState = mData->mMachineState;
12728 mConsoleTaskData.mProgress = pProgress;
12729
12730 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12731 setMachineState(MachineState_Stopping);
12732
12733 pProgress.queryInterfaceTo(aProgress);
12734
12735 return S_OK;
12736}
12737
12738/**
12739 * @note Locks this object for writing.
12740 */
12741STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12742{
12743 LogFlowThisFuncEnter();
12744
12745 AutoCaller autoCaller(this);
12746 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12747
12748 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12749
12750 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12751 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12752 && mConsoleTaskData.mLastState != MachineState_Null,
12753 E_FAIL);
12754
12755 /*
12756 * On failure, set the state to the state we had when BeginPoweringDown()
12757 * was called (this is expected by Console::PowerDown() and the associated
12758 * task). On success the VM process already changed the state to
12759 * MachineState_PoweredOff, so no need to do anything.
12760 */
12761 if (FAILED(iResult))
12762 setMachineState(mConsoleTaskData.mLastState);
12763
12764 /* notify the progress object about operation completion */
12765 Assert(mConsoleTaskData.mProgress);
12766 if (SUCCEEDED(iResult))
12767 mConsoleTaskData.mProgress->notifyComplete(S_OK);
12768 else
12769 {
12770 Utf8Str strErrMsg(aErrMsg);
12771 if (strErrMsg.length())
12772 mConsoleTaskData.mProgress->notifyComplete(iResult,
12773 COM_IIDOF(ISession),
12774 getComponentName(),
12775 strErrMsg.c_str());
12776 else
12777 mConsoleTaskData.mProgress->notifyComplete(iResult);
12778 }
12779
12780 /* clear out the temporary saved state data */
12781 mConsoleTaskData.mLastState = MachineState_Null;
12782 mConsoleTaskData.mProgress.setNull();
12783
12784 LogFlowThisFuncLeave();
12785 return S_OK;
12786}
12787
12788
12789/**
12790 * Goes through the USB filters of the given machine to see if the given
12791 * device matches any filter or not.
12792 *
12793 * @note Locks the same as USBController::hasMatchingFilter() does.
12794 */
12795STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12796 BOOL *aMatched,
12797 ULONG *aMaskedIfs)
12798{
12799 LogFlowThisFunc(("\n"));
12800
12801 CheckComArgNotNull(aUSBDevice);
12802 CheckComArgOutPointerValid(aMatched);
12803
12804 AutoCaller autoCaller(this);
12805 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12806
12807#ifdef VBOX_WITH_USB
12808 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
12809#else
12810 NOREF(aUSBDevice);
12811 NOREF(aMaskedIfs);
12812 *aMatched = FALSE;
12813#endif
12814
12815 return S_OK;
12816}
12817
12818/**
12819 * @note Locks the same as Host::captureUSBDevice() does.
12820 */
12821STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12822{
12823 LogFlowThisFunc(("\n"));
12824
12825 AutoCaller autoCaller(this);
12826 AssertComRCReturnRC(autoCaller.rc());
12827
12828#ifdef VBOX_WITH_USB
12829 /* if captureDeviceForVM() fails, it must have set extended error info */
12830 clearError();
12831 MultiResult rc = mParent->host()->checkUSBProxyService();
12832 if (FAILED(rc)) return rc;
12833
12834 USBProxyService *service = mParent->host()->usbProxyService();
12835 AssertReturn(service, E_FAIL);
12836 return service->captureDeviceForVM(this, Guid(aId).ref());
12837#else
12838 NOREF(aId);
12839 return E_NOTIMPL;
12840#endif
12841}
12842
12843/**
12844 * @note Locks the same as Host::detachUSBDevice() does.
12845 */
12846STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12847{
12848 LogFlowThisFunc(("\n"));
12849
12850 AutoCaller autoCaller(this);
12851 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12852
12853#ifdef VBOX_WITH_USB
12854 USBProxyService *service = mParent->host()->usbProxyService();
12855 AssertReturn(service, E_FAIL);
12856 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12857#else
12858 NOREF(aId);
12859 NOREF(aDone);
12860 return E_NOTIMPL;
12861#endif
12862}
12863
12864/**
12865 * Inserts all machine filters to the USB proxy service and then calls
12866 * Host::autoCaptureUSBDevices().
12867 *
12868 * Called by Console from the VM process upon VM startup.
12869 *
12870 * @note Locks what called methods lock.
12871 */
12872STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12873{
12874 LogFlowThisFunc(("\n"));
12875
12876 AutoCaller autoCaller(this);
12877 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12878
12879#ifdef VBOX_WITH_USB
12880 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
12881 AssertComRC(rc);
12882 NOREF(rc);
12883
12884 USBProxyService *service = mParent->host()->usbProxyService();
12885 AssertReturn(service, E_FAIL);
12886 return service->autoCaptureDevicesForVM(this);
12887#else
12888 return S_OK;
12889#endif
12890}
12891
12892/**
12893 * Removes all machine filters from the USB proxy service and then calls
12894 * Host::detachAllUSBDevices().
12895 *
12896 * Called by Console from the VM process upon normal VM termination or by
12897 * SessionMachine::uninit() upon abnormal VM termination (from under the
12898 * Machine/SessionMachine lock).
12899 *
12900 * @note Locks what called methods lock.
12901 */
12902STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12903{
12904 LogFlowThisFunc(("\n"));
12905
12906 AutoCaller autoCaller(this);
12907 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12908
12909#ifdef VBOX_WITH_USB
12910 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12911 AssertComRC(rc);
12912 NOREF(rc);
12913
12914 USBProxyService *service = mParent->host()->usbProxyService();
12915 AssertReturn(service, E_FAIL);
12916 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12917#else
12918 NOREF(aDone);
12919 return S_OK;
12920#endif
12921}
12922
12923/**
12924 * @note Locks this object for writing.
12925 */
12926STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12927 IProgress **aProgress)
12928{
12929 LogFlowThisFuncEnter();
12930
12931 AssertReturn(aSession, E_INVALIDARG);
12932 AssertReturn(aProgress, E_INVALIDARG);
12933
12934 AutoCaller autoCaller(this);
12935
12936 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
12937 /*
12938 * We don't assert below because it might happen that a non-direct session
12939 * informs us it is closed right after we've been uninitialized -- it's ok.
12940 */
12941 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12942
12943 /* get IInternalSessionControl interface */
12944 ComPtr<IInternalSessionControl> control(aSession);
12945
12946 ComAssertRet(!control.isNull(), E_INVALIDARG);
12947
12948 /* Creating a Progress object requires the VirtualBox lock, and
12949 * thus locking it here is required by the lock order rules. */
12950 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12951
12952 if (control == mData->mSession.mDirectControl)
12953 {
12954 ComAssertRet(aProgress, E_POINTER);
12955
12956 /* The direct session is being normally closed by the client process
12957 * ----------------------------------------------------------------- */
12958
12959 /* go to the closing state (essential for all open*Session() calls and
12960 * for #checkForDeath()) */
12961 Assert(mData->mSession.mState == SessionState_Locked);
12962 mData->mSession.mState = SessionState_Unlocking;
12963
12964 /* set direct control to NULL to release the remote instance */
12965 mData->mSession.mDirectControl.setNull();
12966 LogFlowThisFunc(("Direct control is set to NULL\n"));
12967
12968 if (mData->mSession.mProgress)
12969 {
12970 /* finalize the progress, someone might wait if a frontend
12971 * closes the session before powering on the VM. */
12972 mData->mSession.mProgress->notifyComplete(E_FAIL,
12973 COM_IIDOF(ISession),
12974 getComponentName(),
12975 tr("The VM session was closed before any attempt to power it on"));
12976 mData->mSession.mProgress.setNull();
12977 }
12978
12979 /* Create the progress object the client will use to wait until
12980 * #checkForDeath() is called to uninitialize this session object after
12981 * it releases the IPC semaphore.
12982 * Note! Because we're "reusing" mProgress here, this must be a proxy
12983 * object just like for LaunchVMProcess. */
12984 Assert(mData->mSession.mProgress.isNull());
12985 ComObjPtr<ProgressProxy> progress;
12986 progress.createObject();
12987 ComPtr<IUnknown> pPeer(mPeer);
12988 progress->init(mParent, pPeer,
12989 Bstr(tr("Closing session")).raw(),
12990 FALSE /* aCancelable */);
12991 progress.queryInterfaceTo(aProgress);
12992 mData->mSession.mProgress = progress;
12993 }
12994 else
12995 {
12996 /* the remote session is being normally closed */
12997 Data::Session::RemoteControlList::iterator it =
12998 mData->mSession.mRemoteControls.begin();
12999 while (it != mData->mSession.mRemoteControls.end())
13000 {
13001 if (control == *it)
13002 break;
13003 ++it;
13004 }
13005 BOOL found = it != mData->mSession.mRemoteControls.end();
13006 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13007 E_INVALIDARG);
13008 // This MUST be erase(it), not remove(*it) as the latter triggers a
13009 // very nasty use after free due to the place where the value "lives".
13010 mData->mSession.mRemoteControls.erase(it);
13011 }
13012
13013 /* signal the client watcher thread, because the client is going away */
13014 mParent->updateClientWatcher();
13015
13016 LogFlowThisFuncLeave();
13017 return S_OK;
13018}
13019
13020/**
13021 * @note Locks this object for writing.
13022 */
13023STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
13024{
13025 LogFlowThisFuncEnter();
13026
13027 CheckComArgOutPointerValid(aProgress);
13028 CheckComArgOutPointerValid(aStateFilePath);
13029
13030 AutoCaller autoCaller(this);
13031 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13032
13033 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13034
13035 AssertReturn( mData->mMachineState == MachineState_Paused
13036 && mConsoleTaskData.mLastState == MachineState_Null
13037 && mConsoleTaskData.strStateFilePath.isEmpty(),
13038 E_FAIL);
13039
13040 /* create a progress object to track operation completion */
13041 ComObjPtr<Progress> pProgress;
13042 pProgress.createObject();
13043 pProgress->init(getVirtualBox(),
13044 static_cast<IMachine *>(this) /* aInitiator */,
13045 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13046 FALSE /* aCancelable */);
13047
13048 Utf8Str strStateFilePath;
13049 /* stateFilePath is null when the machine is not running */
13050 if (mData->mMachineState == MachineState_Paused)
13051 composeSavedStateFilename(strStateFilePath);
13052
13053 /* fill in the console task data */
13054 mConsoleTaskData.mLastState = mData->mMachineState;
13055 mConsoleTaskData.strStateFilePath = strStateFilePath;
13056 mConsoleTaskData.mProgress = pProgress;
13057
13058 /* set the state to Saving (this is expected by Console::SaveState()) */
13059 setMachineState(MachineState_Saving);
13060
13061 strStateFilePath.cloneTo(aStateFilePath);
13062 pProgress.queryInterfaceTo(aProgress);
13063
13064 return S_OK;
13065}
13066
13067/**
13068 * @note Locks mParent + this object for writing.
13069 */
13070STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
13071{
13072 LogFlowThisFunc(("\n"));
13073
13074 AutoCaller autoCaller(this);
13075 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13076
13077 /* endSavingState() need mParent lock */
13078 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13079
13080 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
13081 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
13082 && mConsoleTaskData.mLastState != MachineState_Null
13083 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13084 E_FAIL);
13085
13086 /*
13087 * On failure, set the state to the state we had when BeginSavingState()
13088 * was called (this is expected by Console::SaveState() and the associated
13089 * task). On success the VM process already changed the state to
13090 * MachineState_Saved, so no need to do anything.
13091 */
13092 if (FAILED(iResult))
13093 setMachineState(mConsoleTaskData.mLastState);
13094
13095 return endSavingState(iResult, aErrMsg);
13096}
13097
13098/**
13099 * @note Locks this object for writing.
13100 */
13101STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
13102{
13103 LogFlowThisFunc(("\n"));
13104
13105 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
13106
13107 AutoCaller autoCaller(this);
13108 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13109
13110 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13111
13112 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13113 || mData->mMachineState == MachineState_Teleported
13114 || mData->mMachineState == MachineState_Aborted
13115 , E_FAIL); /** @todo setError. */
13116
13117 Utf8Str stateFilePathFull = aSavedStateFile;
13118 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
13119 if (RT_FAILURE(vrc))
13120 return setError(VBOX_E_FILE_ERROR,
13121 tr("Invalid saved state file path '%ls' (%Rrc)"),
13122 aSavedStateFile,
13123 vrc);
13124
13125 mSSData->strStateFilePath = stateFilePathFull;
13126
13127 /* The below setMachineState() will detect the state transition and will
13128 * update the settings file */
13129
13130 return setMachineState(MachineState_Saved);
13131}
13132
13133STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13134 ComSafeArrayOut(BSTR, aValues),
13135 ComSafeArrayOut(LONG64, aTimestamps),
13136 ComSafeArrayOut(BSTR, aFlags))
13137{
13138 LogFlowThisFunc(("\n"));
13139
13140#ifdef VBOX_WITH_GUEST_PROPS
13141 using namespace guestProp;
13142
13143 AutoCaller autoCaller(this);
13144 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13145
13146 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13147
13148 CheckComArgOutSafeArrayPointerValid(aNames);
13149 CheckComArgOutSafeArrayPointerValid(aValues);
13150 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13151 CheckComArgOutSafeArrayPointerValid(aFlags);
13152
13153 size_t cEntries = mHWData->mGuestProperties.size();
13154 com::SafeArray<BSTR> names(cEntries);
13155 com::SafeArray<BSTR> values(cEntries);
13156 com::SafeArray<LONG64> timestamps(cEntries);
13157 com::SafeArray<BSTR> flags(cEntries);
13158 unsigned i = 0;
13159 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13160 it != mHWData->mGuestProperties.end();
13161 ++it)
13162 {
13163 char szFlags[MAX_FLAGS_LEN + 1];
13164 it->first.cloneTo(&names[i]);
13165 it->second.strValue.cloneTo(&values[i]);
13166 timestamps[i] = it->second.mTimestamp;
13167 /* If it is NULL, keep it NULL. */
13168 if (it->second.mFlags)
13169 {
13170 writeFlags(it->second.mFlags, szFlags);
13171 Bstr(szFlags).cloneTo(&flags[i]);
13172 }
13173 else
13174 flags[i] = NULL;
13175 ++i;
13176 }
13177 names.detachTo(ComSafeArrayOutArg(aNames));
13178 values.detachTo(ComSafeArrayOutArg(aValues));
13179 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13180 flags.detachTo(ComSafeArrayOutArg(aFlags));
13181 return S_OK;
13182#else
13183 ReturnComNotImplemented();
13184#endif
13185}
13186
13187STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13188 IN_BSTR aValue,
13189 LONG64 aTimestamp,
13190 IN_BSTR aFlags)
13191{
13192 LogFlowThisFunc(("\n"));
13193
13194#ifdef VBOX_WITH_GUEST_PROPS
13195 using namespace guestProp;
13196
13197 CheckComArgStrNotEmptyOrNull(aName);
13198 CheckComArgNotNull(aValue);
13199 CheckComArgNotNull(aFlags);
13200
13201 try
13202 {
13203 /*
13204 * Convert input up front.
13205 */
13206 Utf8Str utf8Name(aName);
13207 uint32_t fFlags = NILFLAG;
13208 if (aFlags)
13209 {
13210 Utf8Str utf8Flags(aFlags);
13211 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13212 AssertRCReturn(vrc, E_INVALIDARG);
13213 }
13214
13215 /*
13216 * Now grab the object lock, validate the state and do the update.
13217 */
13218 AutoCaller autoCaller(this);
13219 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13220
13221 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13222
13223 switch (mData->mMachineState)
13224 {
13225 case MachineState_Paused:
13226 case MachineState_Running:
13227 case MachineState_Teleporting:
13228 case MachineState_TeleportingPausedVM:
13229 case MachineState_LiveSnapshotting:
13230 case MachineState_DeletingSnapshotOnline:
13231 case MachineState_DeletingSnapshotPaused:
13232 case MachineState_Saving:
13233 case MachineState_Stopping:
13234 break;
13235
13236 default:
13237 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13238 VBOX_E_INVALID_VM_STATE);
13239 }
13240
13241 setModified(IsModified_MachineData);
13242 mHWData.backup();
13243
13244 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13245 if (it != mHWData->mGuestProperties.end())
13246 {
13247 if (RT_VALID_PTR(aValue) && *(aValue) != '\0')
13248 {
13249 it->second.strValue = aValue;
13250 it->second.mFlags = fFlags;
13251 it->second.mTimestamp = aTimestamp;
13252 }
13253 else
13254 mHWData->mGuestProperties.erase(it);
13255
13256 mData->mGuestPropertiesModified = TRUE;
13257 }
13258
13259 /*
13260 * Send a callback notification if appropriate
13261 */
13262 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13263 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13264 RTSTR_MAX,
13265 utf8Name.c_str(),
13266 RTSTR_MAX, NULL)
13267 )
13268 {
13269 alock.release();
13270
13271 mParent->onGuestPropertyChange(mData->mUuid,
13272 aName,
13273 aValue,
13274 aFlags);
13275 }
13276 }
13277 catch (...)
13278 {
13279 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13280 }
13281 return S_OK;
13282#else
13283 ReturnComNotImplemented();
13284#endif
13285}
13286
13287STDMETHODIMP SessionMachine::LockMedia()
13288{
13289 AutoCaller autoCaller(this);
13290 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13291
13292 AutoMultiWriteLock2 alock(this->lockHandle(),
13293 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13294
13295 AssertReturn( mData->mMachineState == MachineState_Starting
13296 || mData->mMachineState == MachineState_Restoring
13297 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13298
13299 clearError();
13300 alock.release();
13301 return lockMedia();
13302}
13303
13304STDMETHODIMP SessionMachine::UnlockMedia()
13305{
13306 unlockMedia();
13307 return S_OK;
13308}
13309
13310STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13311 IMediumAttachment **aNewAttachment)
13312{
13313 CheckComArgNotNull(aAttachment);
13314 CheckComArgOutPointerValid(aNewAttachment);
13315
13316 AutoCaller autoCaller(this);
13317 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13318
13319 // request the host lock first, since might be calling Host methods for getting host drives;
13320 // next, protect the media tree all the while we're in here, as well as our member variables
13321 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13322 this->lockHandle(),
13323 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13324
13325 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13326
13327 Bstr ctrlName;
13328 LONG lPort;
13329 LONG lDevice;
13330 bool fTempEject;
13331 {
13332 AutoCaller autoAttachCaller(this);
13333 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13334
13335 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13336
13337 /* Need to query the details first, as the IMediumAttachment reference
13338 * might be to the original settings, which we are going to change. */
13339 ctrlName = pAttach->getControllerName();
13340 lPort = pAttach->getPort();
13341 lDevice = pAttach->getDevice();
13342 fTempEject = pAttach->getTempEject();
13343 }
13344
13345 if (!fTempEject)
13346 {
13347 /* Remember previously mounted medium. The medium before taking the
13348 * backup is not necessarily the same thing. */
13349 ComObjPtr<Medium> oldmedium;
13350 oldmedium = pAttach->getMedium();
13351
13352 setModified(IsModified_Storage);
13353 mMediaData.backup();
13354
13355 // The backup operation makes the pAttach reference point to the
13356 // old settings. Re-get the correct reference.
13357 pAttach = findAttachment(mMediaData->mAttachments,
13358 ctrlName.raw(),
13359 lPort,
13360 lDevice);
13361
13362 {
13363 AutoCaller autoAttachCaller(this);
13364 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13365
13366 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13367 if (!oldmedium.isNull())
13368 oldmedium->removeBackReference(mData->mUuid);
13369
13370 pAttach->updateMedium(NULL);
13371 pAttach->updateEjected();
13372 }
13373
13374 setModified(IsModified_Storage);
13375 }
13376 else
13377 {
13378 {
13379 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13380 pAttach->updateEjected();
13381 }
13382 }
13383
13384 pAttach.queryInterfaceTo(aNewAttachment);
13385
13386 return S_OK;
13387}
13388
13389// public methods only for internal purposes
13390/////////////////////////////////////////////////////////////////////////////
13391
13392/**
13393 * Called from the client watcher thread to check for expected or unexpected
13394 * death of the client process that has a direct session to this machine.
13395 *
13396 * On Win32 and on OS/2, this method is called only when we've got the
13397 * mutex (i.e. the client has either died or terminated normally) so it always
13398 * returns @c true (the client is terminated, the session machine is
13399 * uninitialized).
13400 *
13401 * On other platforms, the method returns @c true if the client process has
13402 * terminated normally or abnormally and the session machine was uninitialized,
13403 * and @c false if the client process is still alive.
13404 *
13405 * @note Locks this object for writing.
13406 */
13407bool SessionMachine::checkForDeath()
13408{
13409 Uninit::Reason reason;
13410 bool terminated = false;
13411
13412 /* Enclose autoCaller with a block because calling uninit() from under it
13413 * will deadlock. */
13414 {
13415 AutoCaller autoCaller(this);
13416 if (!autoCaller.isOk())
13417 {
13418 /* return true if not ready, to cause the client watcher to exclude
13419 * the corresponding session from watching */
13420 LogFlowThisFunc(("Already uninitialized!\n"));
13421 return true;
13422 }
13423
13424 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13425
13426 /* Determine the reason of death: if the session state is Closing here,
13427 * everything is fine. Otherwise it means that the client did not call
13428 * OnSessionEnd() before it released the IPC semaphore. This may happen
13429 * either because the client process has abnormally terminated, or
13430 * because it simply forgot to call ISession::Close() before exiting. We
13431 * threat the latter also as an abnormal termination (see
13432 * Session::uninit() for details). */
13433 reason = mData->mSession.mState == SessionState_Unlocking ?
13434 Uninit::Normal :
13435 Uninit::Abnormal;
13436
13437#if defined(RT_OS_WINDOWS)
13438
13439 AssertMsg(mIPCSem, ("semaphore must be created"));
13440
13441 /* release the IPC mutex */
13442 ::ReleaseMutex(mIPCSem);
13443
13444 terminated = true;
13445
13446#elif defined(RT_OS_OS2)
13447
13448 AssertMsg(mIPCSem, ("semaphore must be created"));
13449
13450 /* release the IPC mutex */
13451 ::DosReleaseMutexSem(mIPCSem);
13452
13453 terminated = true;
13454
13455#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
13456
13457 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
13458
13459 int val = ::semctl(mIPCSem, 0, GETVAL);
13460 if (val > 0)
13461 {
13462 /* the semaphore is signaled, meaning the session is terminated */
13463 terminated = true;
13464 }
13465
13466#else
13467# error "Port me!"
13468#endif
13469
13470 } /* AutoCaller block */
13471
13472 if (terminated)
13473 uninit(reason);
13474
13475 return terminated;
13476}
13477
13478/**
13479 * @note Locks this object for reading.
13480 */
13481HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13482{
13483 LogFlowThisFunc(("\n"));
13484
13485 AutoCaller autoCaller(this);
13486 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13487
13488 ComPtr<IInternalSessionControl> directControl;
13489 {
13490 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13491 directControl = mData->mSession.mDirectControl;
13492 }
13493
13494 /* ignore notifications sent after #OnSessionEnd() is called */
13495 if (!directControl)
13496 return S_OK;
13497
13498 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13499}
13500
13501/**
13502 * @note Locks this object for reading.
13503 */
13504HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13505 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
13506{
13507 LogFlowThisFunc(("\n"));
13508
13509 AutoCaller autoCaller(this);
13510 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13511
13512 ComPtr<IInternalSessionControl> directControl;
13513 {
13514 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13515 directControl = mData->mSession.mDirectControl;
13516 }
13517
13518 /* ignore notifications sent after #OnSessionEnd() is called */
13519 if (!directControl)
13520 return S_OK;
13521 /*
13522 * instead acting like callback we ask IVirtualBox deliver corresponding event
13523 */
13524
13525 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13526 return S_OK;
13527}
13528
13529/**
13530 * @note Locks this object for reading.
13531 */
13532HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
13533{
13534 LogFlowThisFunc(("\n"));
13535
13536 AutoCaller autoCaller(this);
13537 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13538
13539 ComPtr<IInternalSessionControl> directControl;
13540 {
13541 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13542 directControl = mData->mSession.mDirectControl;
13543 }
13544
13545 /* ignore notifications sent after #OnSessionEnd() is called */
13546 if (!directControl)
13547 return S_OK;
13548
13549 return directControl->OnSerialPortChange(serialPort);
13550}
13551
13552/**
13553 * @note Locks this object for reading.
13554 */
13555HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
13556{
13557 LogFlowThisFunc(("\n"));
13558
13559 AutoCaller autoCaller(this);
13560 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13561
13562 ComPtr<IInternalSessionControl> directControl;
13563 {
13564 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13565 directControl = mData->mSession.mDirectControl;
13566 }
13567
13568 /* ignore notifications sent after #OnSessionEnd() is called */
13569 if (!directControl)
13570 return S_OK;
13571
13572 return directControl->OnParallelPortChange(parallelPort);
13573}
13574
13575/**
13576 * @note Locks this object for reading.
13577 */
13578HRESULT SessionMachine::onStorageControllerChange()
13579{
13580 LogFlowThisFunc(("\n"));
13581
13582 AutoCaller autoCaller(this);
13583 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13584
13585 ComPtr<IInternalSessionControl> directControl;
13586 {
13587 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13588 directControl = mData->mSession.mDirectControl;
13589 }
13590
13591 /* ignore notifications sent after #OnSessionEnd() is called */
13592 if (!directControl)
13593 return S_OK;
13594
13595 return directControl->OnStorageControllerChange();
13596}
13597
13598/**
13599 * @note Locks this object for reading.
13600 */
13601HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13602{
13603 LogFlowThisFunc(("\n"));
13604
13605 AutoCaller autoCaller(this);
13606 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13607
13608 ComPtr<IInternalSessionControl> directControl;
13609 {
13610 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13611 directControl = mData->mSession.mDirectControl;
13612 }
13613
13614 /* ignore notifications sent after #OnSessionEnd() is called */
13615 if (!directControl)
13616 return S_OK;
13617
13618 return directControl->OnMediumChange(aAttachment, aForce);
13619}
13620
13621/**
13622 * @note Locks this object for reading.
13623 */
13624HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
13625{
13626 LogFlowThisFunc(("\n"));
13627
13628 AutoCaller autoCaller(this);
13629 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13630
13631 ComPtr<IInternalSessionControl> directControl;
13632 {
13633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13634 directControl = mData->mSession.mDirectControl;
13635 }
13636
13637 /* ignore notifications sent after #OnSessionEnd() is called */
13638 if (!directControl)
13639 return S_OK;
13640
13641 return directControl->OnCPUChange(aCPU, aRemove);
13642}
13643
13644HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
13645{
13646 LogFlowThisFunc(("\n"));
13647
13648 AutoCaller autoCaller(this);
13649 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13650
13651 ComPtr<IInternalSessionControl> directControl;
13652 {
13653 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13654 directControl = mData->mSession.mDirectControl;
13655 }
13656
13657 /* ignore notifications sent after #OnSessionEnd() is called */
13658 if (!directControl)
13659 return S_OK;
13660
13661 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13662}
13663
13664/**
13665 * @note Locks this object for reading.
13666 */
13667HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
13668{
13669 LogFlowThisFunc(("\n"));
13670
13671 AutoCaller autoCaller(this);
13672 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13673
13674 ComPtr<IInternalSessionControl> directControl;
13675 {
13676 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13677 directControl = mData->mSession.mDirectControl;
13678 }
13679
13680 /* ignore notifications sent after #OnSessionEnd() is called */
13681 if (!directControl)
13682 return S_OK;
13683
13684 return directControl->OnVRDEServerChange(aRestart);
13685}
13686
13687/**
13688 * @note Locks this object for reading.
13689 */
13690HRESULT SessionMachine::onUSBControllerChange()
13691{
13692 LogFlowThisFunc(("\n"));
13693
13694 AutoCaller autoCaller(this);
13695 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13696
13697 ComPtr<IInternalSessionControl> directControl;
13698 {
13699 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13700 directControl = mData->mSession.mDirectControl;
13701 }
13702
13703 /* ignore notifications sent after #OnSessionEnd() is called */
13704 if (!directControl)
13705 return S_OK;
13706
13707 return directControl->OnUSBControllerChange();
13708}
13709
13710/**
13711 * @note Locks this object for reading.
13712 */
13713HRESULT SessionMachine::onSharedFolderChange()
13714{
13715 LogFlowThisFunc(("\n"));
13716
13717 AutoCaller autoCaller(this);
13718 AssertComRCReturnRC(autoCaller.rc());
13719
13720 ComPtr<IInternalSessionControl> directControl;
13721 {
13722 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13723 directControl = mData->mSession.mDirectControl;
13724 }
13725
13726 /* ignore notifications sent after #OnSessionEnd() is called */
13727 if (!directControl)
13728 return S_OK;
13729
13730 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13731}
13732
13733/**
13734 * @note Locks this object for reading.
13735 */
13736HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
13737{
13738 LogFlowThisFunc(("\n"));
13739
13740 AutoCaller autoCaller(this);
13741 AssertComRCReturnRC(autoCaller.rc());
13742
13743 ComPtr<IInternalSessionControl> directControl;
13744 {
13745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13746 directControl = mData->mSession.mDirectControl;
13747 }
13748
13749 /* ignore notifications sent after #OnSessionEnd() is called */
13750 if (!directControl)
13751 return S_OK;
13752
13753 return directControl->OnClipboardModeChange(aClipboardMode);
13754}
13755
13756/**
13757 * @note Locks this object for reading.
13758 */
13759HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
13760{
13761 LogFlowThisFunc(("\n"));
13762
13763 AutoCaller autoCaller(this);
13764 AssertComRCReturnRC(autoCaller.rc());
13765
13766 ComPtr<IInternalSessionControl> directControl;
13767 {
13768 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13769 directControl = mData->mSession.mDirectControl;
13770 }
13771
13772 /* ignore notifications sent after #OnSessionEnd() is called */
13773 if (!directControl)
13774 return S_OK;
13775
13776 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
13777}
13778
13779/**
13780 * @note Locks this object for reading.
13781 */
13782HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13783{
13784 LogFlowThisFunc(("\n"));
13785
13786 AutoCaller autoCaller(this);
13787 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13788
13789 ComPtr<IInternalSessionControl> directControl;
13790 {
13791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13792 directControl = mData->mSession.mDirectControl;
13793 }
13794
13795 /* ignore notifications sent after #OnSessionEnd() is called */
13796 if (!directControl)
13797 return S_OK;
13798
13799 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13800}
13801
13802/**
13803 * @note Locks this object for reading.
13804 */
13805HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13806{
13807 LogFlowThisFunc(("\n"));
13808
13809 AutoCaller autoCaller(this);
13810 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13811
13812 ComPtr<IInternalSessionControl> directControl;
13813 {
13814 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13815 directControl = mData->mSession.mDirectControl;
13816 }
13817
13818 /* ignore notifications sent after #OnSessionEnd() is called */
13819 if (!directControl)
13820 return S_OK;
13821
13822 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13823}
13824
13825/**
13826 * Returns @c true if this machine's USB controller reports it has a matching
13827 * filter for the given USB device and @c false otherwise.
13828 *
13829 * @note locks this object for reading.
13830 */
13831bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13832{
13833 AutoCaller autoCaller(this);
13834 /* silently return if not ready -- this method may be called after the
13835 * direct machine session has been called */
13836 if (!autoCaller.isOk())
13837 return false;
13838
13839#ifdef VBOX_WITH_USB
13840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13841
13842 switch (mData->mMachineState)
13843 {
13844 case MachineState_Starting:
13845 case MachineState_Restoring:
13846 case MachineState_TeleportingIn:
13847 case MachineState_Paused:
13848 case MachineState_Running:
13849 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13850 * elsewhere... */
13851 alock.release();
13852 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
13853 default: break;
13854 }
13855#else
13856 NOREF(aDevice);
13857 NOREF(aMaskedIfs);
13858#endif
13859 return false;
13860}
13861
13862/**
13863 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13864 */
13865HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
13866 IVirtualBoxErrorInfo *aError,
13867 ULONG aMaskedIfs)
13868{
13869 LogFlowThisFunc(("\n"));
13870
13871 AutoCaller autoCaller(this);
13872
13873 /* This notification may happen after the machine object has been
13874 * uninitialized (the session was closed), so don't assert. */
13875 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13876
13877 ComPtr<IInternalSessionControl> directControl;
13878 {
13879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13880 directControl = mData->mSession.mDirectControl;
13881 }
13882
13883 /* fail on notifications sent after #OnSessionEnd() is called, it is
13884 * expected by the caller */
13885 if (!directControl)
13886 return E_FAIL;
13887
13888 /* No locks should be held at this point. */
13889 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13890 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13891
13892 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13893}
13894
13895/**
13896 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13897 */
13898HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
13899 IVirtualBoxErrorInfo *aError)
13900{
13901 LogFlowThisFunc(("\n"));
13902
13903 AutoCaller autoCaller(this);
13904
13905 /* This notification may happen after the machine object has been
13906 * uninitialized (the session was closed), so don't assert. */
13907 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13908
13909 ComPtr<IInternalSessionControl> directControl;
13910 {
13911 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13912 directControl = mData->mSession.mDirectControl;
13913 }
13914
13915 /* fail on notifications sent after #OnSessionEnd() is called, it is
13916 * expected by the caller */
13917 if (!directControl)
13918 return E_FAIL;
13919
13920 /* No locks should be held at this point. */
13921 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13922 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13923
13924 return directControl->OnUSBDeviceDetach(aId, aError);
13925}
13926
13927// protected methods
13928/////////////////////////////////////////////////////////////////////////////
13929
13930/**
13931 * Helper method to finalize saving the state.
13932 *
13933 * @note Must be called from under this object's lock.
13934 *
13935 * @param aRc S_OK if the snapshot has been taken successfully
13936 * @param aErrMsg human readable error message for failure
13937 *
13938 * @note Locks mParent + this objects for writing.
13939 */
13940HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13941{
13942 LogFlowThisFuncEnter();
13943
13944 AutoCaller autoCaller(this);
13945 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13946
13947 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13948
13949 HRESULT rc = S_OK;
13950
13951 if (SUCCEEDED(aRc))
13952 {
13953 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13954
13955 /* save all VM settings */
13956 rc = saveSettings(NULL);
13957 // no need to check whether VirtualBox.xml needs saving also since
13958 // we can't have a name change pending at this point
13959 }
13960 else
13961 {
13962 // delete the saved state file (it might have been already created);
13963 // we need not check whether this is shared with a snapshot here because
13964 // we certainly created this saved state file here anew
13965 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13966 }
13967
13968 /* notify the progress object about operation completion */
13969 Assert(mConsoleTaskData.mProgress);
13970 if (SUCCEEDED(aRc))
13971 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13972 else
13973 {
13974 if (aErrMsg.length())
13975 mConsoleTaskData.mProgress->notifyComplete(aRc,
13976 COM_IIDOF(ISession),
13977 getComponentName(),
13978 aErrMsg.c_str());
13979 else
13980 mConsoleTaskData.mProgress->notifyComplete(aRc);
13981 }
13982
13983 /* clear out the temporary saved state data */
13984 mConsoleTaskData.mLastState = MachineState_Null;
13985 mConsoleTaskData.strStateFilePath.setNull();
13986 mConsoleTaskData.mProgress.setNull();
13987
13988 LogFlowThisFuncLeave();
13989 return rc;
13990}
13991
13992/**
13993 * Deletes the given file if it is no longer in use by either the current machine state
13994 * (if the machine is "saved") or any of the machine's snapshots.
13995 *
13996 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13997 * but is different for each SnapshotMachine. When calling this, the order of calling this
13998 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13999 * is therefore critical. I know, it's all rather messy.
14000 *
14001 * @param strStateFile
14002 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
14003 */
14004void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
14005 Snapshot *pSnapshotToIgnore)
14006{
14007 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14008 if ( (strStateFile.isNotEmpty())
14009 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14010 )
14011 // ... and it must also not be shared with other snapshots
14012 if ( !mData->mFirstSnapshot
14013 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14014 // this checks the SnapshotMachine's state file paths
14015 )
14016 RTFileDelete(strStateFile.c_str());
14017}
14018
14019/**
14020 * Locks the attached media.
14021 *
14022 * All attached hard disks are locked for writing and DVD/floppy are locked for
14023 * reading. Parents of attached hard disks (if any) are locked for reading.
14024 *
14025 * This method also performs accessibility check of all media it locks: if some
14026 * media is inaccessible, the method will return a failure and a bunch of
14027 * extended error info objects per each inaccessible medium.
14028 *
14029 * Note that this method is atomic: if it returns a success, all media are
14030 * locked as described above; on failure no media is locked at all (all
14031 * succeeded individual locks will be undone).
14032 *
14033 * The caller is responsible for doing the necessary state sanity checks.
14034 *
14035 * The locks made by this method must be undone by calling #unlockMedia() when
14036 * no more needed.
14037 */
14038HRESULT SessionMachine::lockMedia()
14039{
14040 AutoCaller autoCaller(this);
14041 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14042
14043 AutoMultiWriteLock2 alock(this->lockHandle(),
14044 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14045
14046 /* bail out if trying to lock things with already set up locking */
14047 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14048
14049 MultiResult mrc(S_OK);
14050
14051 /* Collect locking information for all medium objects attached to the VM. */
14052 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14053 it != mMediaData->mAttachments.end();
14054 ++it)
14055 {
14056 MediumAttachment* pAtt = *it;
14057 DeviceType_T devType = pAtt->getType();
14058 Medium *pMedium = pAtt->getMedium();
14059
14060 MediumLockList *pMediumLockList(new MediumLockList());
14061 // There can be attachments without a medium (floppy/dvd), and thus
14062 // it's impossible to create a medium lock list. It still makes sense
14063 // to have the empty medium lock list in the map in case a medium is
14064 // attached later.
14065 if (pMedium != NULL)
14066 {
14067 MediumType_T mediumType = pMedium->getType();
14068 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14069 || mediumType == MediumType_Shareable;
14070 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14071
14072 alock.release();
14073 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14074 !fIsReadOnlyLock /* fMediumLockWrite */,
14075 NULL,
14076 *pMediumLockList);
14077 alock.acquire();
14078 if (FAILED(mrc))
14079 {
14080 delete pMediumLockList;
14081 mData->mSession.mLockedMedia.Clear();
14082 break;
14083 }
14084 }
14085
14086 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14087 if (FAILED(rc))
14088 {
14089 mData->mSession.mLockedMedia.Clear();
14090 mrc = setError(rc,
14091 tr("Collecting locking information for all attached media failed"));
14092 break;
14093 }
14094 }
14095
14096 if (SUCCEEDED(mrc))
14097 {
14098 /* Now lock all media. If this fails, nothing is locked. */
14099 alock.release();
14100 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14101 alock.acquire();
14102 if (FAILED(rc))
14103 {
14104 mrc = setError(rc,
14105 tr("Locking of attached media failed"));
14106 }
14107 }
14108
14109 return mrc;
14110}
14111
14112/**
14113 * Undoes the locks made by by #lockMedia().
14114 */
14115void SessionMachine::unlockMedia()
14116{
14117 AutoCaller autoCaller(this);
14118 AssertComRCReturnVoid(autoCaller.rc());
14119
14120 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14121
14122 /* we may be holding important error info on the current thread;
14123 * preserve it */
14124 ErrorInfoKeeper eik;
14125
14126 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14127 AssertComRC(rc);
14128}
14129
14130/**
14131 * Helper to change the machine state (reimplementation).
14132 *
14133 * @note Locks this object for writing.
14134 * @note This method must not call saveSettings or SaveSettings, otherwise
14135 * it can cause crashes in random places due to unexpectedly committing
14136 * the current settings. The caller is responsible for that. The call
14137 * to saveStateSettings is fine, because this method does not commit.
14138 */
14139HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
14140{
14141 LogFlowThisFuncEnter();
14142 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14143
14144 AutoCaller autoCaller(this);
14145 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14146
14147 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14148
14149 MachineState_T oldMachineState = mData->mMachineState;
14150
14151 AssertMsgReturn(oldMachineState != aMachineState,
14152 ("oldMachineState=%s, aMachineState=%s\n",
14153 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14154 E_FAIL);
14155
14156 HRESULT rc = S_OK;
14157
14158 int stsFlags = 0;
14159 bool deleteSavedState = false;
14160
14161 /* detect some state transitions */
14162
14163 if ( ( oldMachineState == MachineState_Saved
14164 && aMachineState == MachineState_Restoring)
14165 || ( ( oldMachineState == MachineState_PoweredOff
14166 || oldMachineState == MachineState_Teleported
14167 || oldMachineState == MachineState_Aborted
14168 )
14169 && ( aMachineState == MachineState_TeleportingIn
14170 || aMachineState == MachineState_Starting
14171 )
14172 )
14173 )
14174 {
14175 /* The EMT thread is about to start */
14176
14177 /* Nothing to do here for now... */
14178
14179 /// @todo NEWMEDIA don't let mDVDDrive and other children
14180 /// change anything when in the Starting/Restoring state
14181 }
14182 else if ( ( oldMachineState == MachineState_Running
14183 || oldMachineState == MachineState_Paused
14184 || oldMachineState == MachineState_Teleporting
14185 || oldMachineState == MachineState_LiveSnapshotting
14186 || oldMachineState == MachineState_Stuck
14187 || oldMachineState == MachineState_Starting
14188 || oldMachineState == MachineState_Stopping
14189 || oldMachineState == MachineState_Saving
14190 || oldMachineState == MachineState_Restoring
14191 || oldMachineState == MachineState_TeleportingPausedVM
14192 || oldMachineState == MachineState_TeleportingIn
14193 )
14194 && ( aMachineState == MachineState_PoweredOff
14195 || aMachineState == MachineState_Saved
14196 || aMachineState == MachineState_Teleported
14197 || aMachineState == MachineState_Aborted
14198 )
14199 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14200 * snapshot */
14201 && ( mConsoleTaskData.mSnapshot.isNull()
14202 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14203 )
14204 )
14205 {
14206 /* The EMT thread has just stopped, unlock attached media. Note that as
14207 * opposed to locking that is done from Console, we do unlocking here
14208 * because the VM process may have aborted before having a chance to
14209 * properly unlock all media it locked. */
14210
14211 unlockMedia();
14212 }
14213
14214 if (oldMachineState == MachineState_Restoring)
14215 {
14216 if (aMachineState != MachineState_Saved)
14217 {
14218 /*
14219 * delete the saved state file once the machine has finished
14220 * restoring from it (note that Console sets the state from
14221 * Restoring to Saved if the VM couldn't restore successfully,
14222 * to give the user an ability to fix an error and retry --
14223 * we keep the saved state file in this case)
14224 */
14225 deleteSavedState = true;
14226 }
14227 }
14228 else if ( oldMachineState == MachineState_Saved
14229 && ( aMachineState == MachineState_PoweredOff
14230 || aMachineState == MachineState_Aborted
14231 || aMachineState == MachineState_Teleported
14232 )
14233 )
14234 {
14235 /*
14236 * delete the saved state after Console::ForgetSavedState() is called
14237 * or if the VM process (owning a direct VM session) crashed while the
14238 * VM was Saved
14239 */
14240
14241 /// @todo (dmik)
14242 // Not sure that deleting the saved state file just because of the
14243 // client death before it attempted to restore the VM is a good
14244 // thing. But when it crashes we need to go to the Aborted state
14245 // which cannot have the saved state file associated... The only
14246 // way to fix this is to make the Aborted condition not a VM state
14247 // but a bool flag: i.e., when a crash occurs, set it to true and
14248 // change the state to PoweredOff or Saved depending on the
14249 // saved state presence.
14250
14251 deleteSavedState = true;
14252 mData->mCurrentStateModified = TRUE;
14253 stsFlags |= SaveSTS_CurStateModified;
14254 }
14255
14256 if ( aMachineState == MachineState_Starting
14257 || aMachineState == MachineState_Restoring
14258 || aMachineState == MachineState_TeleportingIn
14259 )
14260 {
14261 /* set the current state modified flag to indicate that the current
14262 * state is no more identical to the state in the
14263 * current snapshot */
14264 if (!mData->mCurrentSnapshot.isNull())
14265 {
14266 mData->mCurrentStateModified = TRUE;
14267 stsFlags |= SaveSTS_CurStateModified;
14268 }
14269 }
14270
14271 if (deleteSavedState)
14272 {
14273 if (mRemoveSavedState)
14274 {
14275 Assert(!mSSData->strStateFilePath.isEmpty());
14276
14277 // it is safe to delete the saved state file if ...
14278 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14279 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14280 // ... none of the snapshots share the saved state file
14281 )
14282 RTFileDelete(mSSData->strStateFilePath.c_str());
14283 }
14284
14285 mSSData->strStateFilePath.setNull();
14286 stsFlags |= SaveSTS_StateFilePath;
14287 }
14288
14289 /* redirect to the underlying peer machine */
14290 mPeer->setMachineState(aMachineState);
14291
14292 if ( aMachineState == MachineState_PoweredOff
14293 || aMachineState == MachineState_Teleported
14294 || aMachineState == MachineState_Aborted
14295 || aMachineState == MachineState_Saved)
14296 {
14297 /* the machine has stopped execution
14298 * (or the saved state file was adopted) */
14299 stsFlags |= SaveSTS_StateTimeStamp;
14300 }
14301
14302 if ( ( oldMachineState == MachineState_PoweredOff
14303 || oldMachineState == MachineState_Aborted
14304 || oldMachineState == MachineState_Teleported
14305 )
14306 && aMachineState == MachineState_Saved)
14307 {
14308 /* the saved state file was adopted */
14309 Assert(!mSSData->strStateFilePath.isEmpty());
14310 stsFlags |= SaveSTS_StateFilePath;
14311 }
14312
14313#ifdef VBOX_WITH_GUEST_PROPS
14314 if ( aMachineState == MachineState_PoweredOff
14315 || aMachineState == MachineState_Aborted
14316 || aMachineState == MachineState_Teleported)
14317 {
14318 /* Make sure any transient guest properties get removed from the
14319 * property store on shutdown. */
14320
14321 HWData::GuestPropertyMap::const_iterator it;
14322 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14323 if (!fNeedsSaving)
14324 for (it = mHWData->mGuestProperties.begin();
14325 it != mHWData->mGuestProperties.end(); ++it)
14326 if ( (it->second.mFlags & guestProp::TRANSIENT)
14327 || (it->second.mFlags & guestProp::TRANSRESET))
14328 {
14329 fNeedsSaving = true;
14330 break;
14331 }
14332 if (fNeedsSaving)
14333 {
14334 mData->mCurrentStateModified = TRUE;
14335 stsFlags |= SaveSTS_CurStateModified;
14336 }
14337 }
14338#endif
14339
14340 rc = saveStateSettings(stsFlags);
14341
14342 if ( ( oldMachineState != MachineState_PoweredOff
14343 && oldMachineState != MachineState_Aborted
14344 && oldMachineState != MachineState_Teleported
14345 )
14346 && ( aMachineState == MachineState_PoweredOff
14347 || aMachineState == MachineState_Aborted
14348 || aMachineState == MachineState_Teleported
14349 )
14350 )
14351 {
14352 /* we've been shut down for any reason */
14353 /* no special action so far */
14354 }
14355
14356 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14357 LogFlowThisFuncLeave();
14358 return rc;
14359}
14360
14361/**
14362 * Sends the current machine state value to the VM process.
14363 *
14364 * @note Locks this object for reading, then calls a client process.
14365 */
14366HRESULT SessionMachine::updateMachineStateOnClient()
14367{
14368 AutoCaller autoCaller(this);
14369 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14370
14371 ComPtr<IInternalSessionControl> directControl;
14372 {
14373 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14374 AssertReturn(!!mData, E_FAIL);
14375 directControl = mData->mSession.mDirectControl;
14376
14377 /* directControl may be already set to NULL here in #OnSessionEnd()
14378 * called too early by the direct session process while there is still
14379 * some operation (like deleting the snapshot) in progress. The client
14380 * process in this case is waiting inside Session::close() for the
14381 * "end session" process object to complete, while #uninit() called by
14382 * #checkForDeath() on the Watcher thread is waiting for the pending
14383 * operation to complete. For now, we accept this inconsistent behavior
14384 * and simply do nothing here. */
14385
14386 if (mData->mSession.mState == SessionState_Unlocking)
14387 return S_OK;
14388
14389 AssertReturn(!directControl.isNull(), E_FAIL);
14390 }
14391
14392 return directControl->UpdateMachineState(mData->mMachineState);
14393}
Note: See TracBrowser for help on using the repository browser.

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