VirtualBox

source: vbox/trunk/src/VBox/Main/MachineImpl.cpp@ 34645

Last change on this file since 34645 was 34587, checked in by vboxsync, 14 years ago

Main: Bandwidth groups for disks (and later network)

This introduces two new interfaces. The first one named IBandwidthGroup
represents one I/O limit and can be assigned to several mediums which
share this limit (which works only for harddisk images with the disabled
host cache).
The second one IBandwdithControl manages the groups and can create new ones
and destroy them if not required anymore.

VBoxManage: commands to access the bandwidth groups

Syntax:
VBoxManage storageattach <uuid|vmname>

...
--bandwidthgroup <name>

--bandwidthgroup assigns the specified device to the given group.

VBoxManage bandwidthctl <uuid|vmname>

--name <name>
--add disk|network
--limit <megabytes per second>
--delete

The --name parameter gives the name of the bandwidth group.
--add creates a new group of the given type (only disk is implemented so far)

with the given name.

--limit sets the limit to the given amount of MB/s

Note that limit can be changed while the VM is running. The VM
will immediately pick up the new limit for the given group name.

--delete deletes the group with the given name if it isn't used anymore.

Trying to delete a still used group will result in an error.

Example:

VBoxManage bandwidthctl "Test VM" --name Limit --add disk --limit 20
Creates a group named Test having a 20 MB/s limit.

VBoxManage storageattach "Test VM" --storagectl "SATA Controller" --port 0 --device 0 --type hdd --medium test.vdi --bandwidthgroup Limit
Adds a new disk to the SATA controller and assigns the bandwidth group Limit to it.

VBoxManage storageattach "Test VM" --storagectl "SATA Controller" --port 0 --device 0 --type hdd --medium test.vdi --bandwidthgroup none
Removes the bandwidth limit from the disk.

VBoxManage bandwidthctl "Test VM" --name Limit --add disk --limit 10
Changes the limit of bandwidth group Limit to 10 MB/s. If the VM is running the limit will be picked up
immediately.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 390.4 KB
Line 
1/* $Id: MachineImpl.cpp 34587 2010-12-01 20:30:02Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2006-2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
27# include <errno.h>
28# include <sys/types.h>
29# include <sys/stat.h>
30# include <sys/ipc.h>
31# include <sys/sem.h>
32#endif
33
34#include "Logging.h"
35#include "VirtualBoxImpl.h"
36#include "MachineImpl.h"
37#include "ProgressImpl.h"
38#include "ProgressProxyImpl.h"
39#include "MediumAttachmentImpl.h"
40#include "MediumImpl.h"
41#include "MediumLock.h"
42#include "USBControllerImpl.h"
43#include "HostImpl.h"
44#include "SharedFolderImpl.h"
45#include "GuestOSTypeImpl.h"
46#include "VirtualBoxErrorInfoImpl.h"
47#include "GuestImpl.h"
48#include "StorageControllerImpl.h"
49#include "DisplayImpl.h"
50#include "DisplayUtils.h"
51#include "BandwidthControlImpl.h"
52
53#ifdef VBOX_WITH_USB
54# include "USBProxyService.h"
55#endif
56
57#include "AutoCaller.h"
58#include "Performance.h"
59
60#include <iprt/asm.h>
61#include <iprt/path.h>
62#include <iprt/dir.h>
63#include <iprt/env.h>
64#include <iprt/lockvalidator.h>
65#include <iprt/process.h>
66#include <iprt/cpp/utils.h>
67#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
68#include <iprt/string.h>
69
70#include <VBox/com/array.h>
71
72#include <VBox/err.h>
73#include <VBox/param.h>
74#include <VBox/settings.h>
75#include <VBox/ssm.h>
76
77#ifdef VBOX_WITH_GUEST_PROPS
78# include <VBox/HostServices/GuestPropertySvc.h>
79# include <VBox/com/array.h>
80#endif
81
82#include "VBox/com/MultiResult.h"
83
84#include <algorithm>
85
86#include <typeinfo>
87
88#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
89# define HOSTSUFF_EXE ".exe"
90#else /* !RT_OS_WINDOWS */
91# define HOSTSUFF_EXE ""
92#endif /* !RT_OS_WINDOWS */
93
94// defines / prototypes
95/////////////////////////////////////////////////////////////////////////////
96
97/////////////////////////////////////////////////////////////////////////////
98// Machine::Data structure
99/////////////////////////////////////////////////////////////////////////////
100
101Machine::Data::Data()
102{
103 mRegistered = FALSE;
104 pMachineConfigFile = NULL;
105 flModifications = 0;
106 mAccessible = FALSE;
107 /* mUuid is initialized in Machine::init() */
108
109 mMachineState = MachineState_PoweredOff;
110 RTTimeNow(&mLastStateChange);
111
112 mMachineStateDeps = 0;
113 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
114 mMachineStateChangePending = 0;
115
116 mCurrentStateModified = TRUE;
117 mGuestPropertiesModified = FALSE;
118
119 mSession.mPid = NIL_RTPROCESS;
120 mSession.mState = SessionState_Unlocked;
121}
122
123Machine::Data::~Data()
124{
125 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
126 {
127 RTSemEventMultiDestroy(mMachineStateDepsSem);
128 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
129 }
130 if (pMachineConfigFile)
131 {
132 delete pMachineConfigFile;
133 pMachineConfigFile = NULL;
134 }
135}
136
137/////////////////////////////////////////////////////////////////////////////
138// Machine::HWData structure
139/////////////////////////////////////////////////////////////////////////////
140
141Machine::HWData::HWData()
142{
143 /* default values for a newly created machine */
144 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
145 mMemorySize = 128;
146 mCPUCount = 1;
147 mCPUHotPlugEnabled = false;
148 mMemoryBalloonSize = 0;
149 mPageFusionEnabled = false;
150 mVRAMSize = 8;
151 mAccelerate3DEnabled = false;
152 mAccelerate2DVideoEnabled = false;
153 mMonitorCount = 1;
154 mHWVirtExEnabled = true;
155 mHWVirtExNestedPagingEnabled = true;
156#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
157 mHWVirtExLargePagesEnabled = true;
158#else
159 /* Not supported on 32 bits hosts. */
160 mHWVirtExLargePagesEnabled = false;
161#endif
162 mHWVirtExVPIDEnabled = true;
163 mHWVirtExForceEnabled = false;
164#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
165 mHWVirtExExclusive = false;
166#else
167 mHWVirtExExclusive = true;
168#endif
169#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
170 mPAEEnabled = true;
171#else
172 mPAEEnabled = false;
173#endif
174 mSyntheticCpu = false;
175 mHpetEnabled = false;
176
177 /* default boot order: floppy - DVD - HDD */
178 mBootOrder[0] = DeviceType_Floppy;
179 mBootOrder[1] = DeviceType_DVD;
180 mBootOrder[2] = DeviceType_HardDisk;
181 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
182 mBootOrder[i] = DeviceType_Null;
183
184 mClipboardMode = ClipboardMode_Bidirectional;
185 mGuestPropertyNotificationPatterns = "";
186
187 mFirmwareType = FirmwareType_BIOS;
188 mKeyboardHidType = KeyboardHidType_PS2Keyboard;
189 mPointingHidType = PointingHidType_PS2Mouse;
190 mChipsetType = ChipsetType_PIIX3;
191
192 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
193 mCPUAttached[i] = false;
194
195 mIoCacheEnabled = true;
196 mIoCacheSize = 5; /* 5MB */
197
198 /* Maximum CPU execution cap by default. */
199 mCpuExecutionCap = 100;
200}
201
202Machine::HWData::~HWData()
203{
204}
205
206/////////////////////////////////////////////////////////////////////////////
207// Machine::HDData structure
208/////////////////////////////////////////////////////////////////////////////
209
210Machine::MediaData::MediaData()
211{
212}
213
214Machine::MediaData::~MediaData()
215{
216}
217
218/////////////////////////////////////////////////////////////////////////////
219// Machine class
220/////////////////////////////////////////////////////////////////////////////
221
222// constructor / destructor
223/////////////////////////////////////////////////////////////////////////////
224
225Machine::Machine()
226 : mGuestHAL(NULL),
227 mPeer(NULL),
228 mParent(NULL)
229{}
230
231Machine::~Machine()
232{}
233
234HRESULT Machine::FinalConstruct()
235{
236 LogFlowThisFunc(("\n"));
237 return S_OK;
238}
239
240void Machine::FinalRelease()
241{
242 LogFlowThisFunc(("\n"));
243 uninit();
244}
245
246/**
247 * Initializes a new machine instance; this init() variant creates a new, empty machine.
248 * This gets called from VirtualBox::CreateMachine().
249 *
250 * @param aParent Associated parent object
251 * @param strConfigFile Local file system path to the VM settings file (can
252 * be relative to the VirtualBox config directory).
253 * @param strName name for the machine
254 * @param aId UUID for the new machine.
255 * @param aOsType OS Type of this machine or NULL.
256 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
257 *
258 * @return Success indicator. if not S_OK, the machine object is invalid
259 */
260HRESULT Machine::init(VirtualBox *aParent,
261 const Utf8Str &strConfigFile,
262 const Utf8Str &strName,
263 GuestOSType *aOsType,
264 const Guid &aId,
265 bool fForceOverwrite)
266{
267 LogFlowThisFuncEnter();
268 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
269
270 /* Enclose the state transition NotReady->InInit->Ready */
271 AutoInitSpan autoInitSpan(this);
272 AssertReturn(autoInitSpan.isOk(), E_FAIL);
273
274 HRESULT rc = initImpl(aParent, strConfigFile);
275 if (FAILED(rc)) return rc;
276
277 rc = tryCreateMachineConfigFile(fForceOverwrite);
278 if (FAILED(rc)) return rc;
279
280 if (SUCCEEDED(rc))
281 {
282 // create an empty machine config
283 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
284
285 rc = initDataAndChildObjects();
286 }
287
288 if (SUCCEEDED(rc))
289 {
290 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
291 mData->mAccessible = TRUE;
292
293 unconst(mData->mUuid) = aId;
294
295 mUserData->s.strName = strName;
296
297 // the "name sync" flag determines whether the machine directory gets renamed along
298 // with the machine file; say so if the settings file name is the same as the
299 // settings file parent directory (machine directory)
300 mUserData->s.fNameSync = isInOwnDir();
301
302 // initialize the default snapshots folder
303 rc = COMSETTER(SnapshotFolder)(NULL);
304 AssertComRC(rc);
305
306 if (aOsType)
307 {
308 /* Store OS type */
309 mUserData->s.strOsType = aOsType->id();
310
311 /* Apply BIOS defaults */
312 mBIOSSettings->applyDefaults(aOsType);
313
314 /* Apply network adapters defaults */
315 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); ++slot)
316 mNetworkAdapters[slot]->applyDefaults(aOsType);
317
318 /* Apply serial port defaults */
319 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
320 mSerialPorts[slot]->applyDefaults(aOsType);
321 }
322
323 /* commit all changes made during the initialization */
324 commit();
325 }
326
327 /* Confirm a successful initialization when it's the case */
328 if (SUCCEEDED(rc))
329 {
330 if (mData->mAccessible)
331 autoInitSpan.setSucceeded();
332 else
333 autoInitSpan.setLimited();
334 }
335
336 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
337 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
338 mData->mRegistered,
339 mData->mAccessible,
340 rc));
341
342 LogFlowThisFuncLeave();
343
344 return rc;
345}
346
347/**
348 * Initializes a new instance with data from machine XML (formerly Init_Registered).
349 * Gets called in two modes:
350 *
351 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
352 * UUID is specified and we mark the machine as "registered";
353 *
354 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
355 * and the machine remains unregistered until RegisterMachine() is called.
356 *
357 * @param aParent Associated parent object
358 * @param aConfigFile Local file system path to the VM settings file (can
359 * be relative to the VirtualBox config directory).
360 * @param aId UUID of the machine or NULL (see above).
361 *
362 * @return Success indicator. if not S_OK, the machine object is invalid
363 */
364HRESULT Machine::init(VirtualBox *aParent,
365 const Utf8Str &strConfigFile,
366 const Guid *aId)
367{
368 LogFlowThisFuncEnter();
369 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
370
371 /* Enclose the state transition NotReady->InInit->Ready */
372 AutoInitSpan autoInitSpan(this);
373 AssertReturn(autoInitSpan.isOk(), E_FAIL);
374
375 HRESULT rc = initImpl(aParent, strConfigFile);
376 if (FAILED(rc)) return rc;
377
378 if (aId)
379 {
380 // loading a registered VM:
381 unconst(mData->mUuid) = *aId;
382 mData->mRegistered = TRUE;
383 // now load the settings from XML:
384 rc = registeredInit();
385 // this calls initDataAndChildObjects() and loadSettings()
386 }
387 else
388 {
389 // opening an unregistered VM (VirtualBox::OpenMachine()):
390 rc = initDataAndChildObjects();
391
392 if (SUCCEEDED(rc))
393 {
394 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
395 mData->mAccessible = TRUE;
396
397 try
398 {
399 // load and parse machine XML; this will throw on XML or logic errors
400 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
401
402 // reject VM UUID duplicates, they can happen if someone
403 // tries to register an already known VM config again
404 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
405 true /* fPermitInaccessible */,
406 false /* aDoSetError */,
407 NULL) != VBOX_E_OBJECT_NOT_FOUND)
408 {
409 throw setError(E_FAIL,
410 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
411 mData->m_strConfigFile.c_str());
412 }
413
414 // use UUID from machine config
415 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
416
417 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
418 NULL /* puuidRegistry */);
419 if (FAILED(rc)) throw rc;
420
421 commit();
422 }
423 catch (HRESULT err)
424 {
425 /* we assume that error info is set by the thrower */
426 rc = err;
427 }
428 catch (...)
429 {
430 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
431 }
432 }
433 }
434
435 /* Confirm a successful initialization when it's the case */
436 if (SUCCEEDED(rc))
437 {
438 if (mData->mAccessible)
439 autoInitSpan.setSucceeded();
440 else
441 {
442 autoInitSpan.setLimited();
443
444 // uninit media from this machine's media registry, or else
445 // reloading the settings will fail
446 mParent->unregisterMachineMedia(getId());
447 }
448 }
449
450 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
451 "rc=%08X\n",
452 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
453 mData->mRegistered, mData->mAccessible, rc));
454
455 LogFlowThisFuncLeave();
456
457 return rc;
458}
459
460/**
461 * Initializes a new instance from a machine config that is already in memory
462 * (import OVF case). Since we are importing, the UUID in the machine
463 * config is ignored and we always generate a fresh one.
464 *
465 * @param strName Name for the new machine; this overrides what is specified in config and is used
466 * for the settings file as well.
467 * @param config Machine configuration loaded and parsed from XML.
468 *
469 * @return Success indicator. if not S_OK, the machine object is invalid
470 */
471HRESULT Machine::init(VirtualBox *aParent,
472 const Utf8Str &strName,
473 const settings::MachineConfigFile &config)
474{
475 LogFlowThisFuncEnter();
476
477 /* Enclose the state transition NotReady->InInit->Ready */
478 AutoInitSpan autoInitSpan(this);
479 AssertReturn(autoInitSpan.isOk(), E_FAIL);
480
481 Utf8Str strConfigFile;
482 aParent->getDefaultMachineFolder(strConfigFile);
483 strConfigFile.append(RTPATH_DELIMITER);
484 strConfigFile.append(strName);
485 strConfigFile.append(RTPATH_DELIMITER);
486 strConfigFile.append(strName);
487 strConfigFile.append(".vbox");
488
489 HRESULT rc = initImpl(aParent, strConfigFile);
490 if (FAILED(rc)) return rc;
491
492 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
493 if (FAILED(rc)) return rc;
494
495 rc = initDataAndChildObjects();
496
497 if (SUCCEEDED(rc))
498 {
499 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
500 mData->mAccessible = TRUE;
501
502 // create empty machine config for instance data
503 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
504
505 // generate fresh UUID, ignore machine config
506 unconst(mData->mUuid).create();
507
508 rc = loadMachineDataFromSettings(config,
509 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
510
511 // override VM name as well, it may be different
512 mUserData->s.strName = strName;
513
514 /* commit all changes made during the initialization */
515 if (SUCCEEDED(rc))
516 commit();
517 }
518
519 /* Confirm a successful initialization when it's the case */
520 if (SUCCEEDED(rc))
521 {
522 if (mData->mAccessible)
523 autoInitSpan.setSucceeded();
524 else
525 {
526 autoInitSpan.setLimited();
527
528 // uninit media from this machine's media registry, or else
529 // reloading the settings will fail
530 mParent->unregisterMachineMedia(getId());
531 }
532 }
533
534 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
535 "rc=%08X\n",
536 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
537 mData->mRegistered, mData->mAccessible, rc));
538
539 LogFlowThisFuncLeave();
540
541 return rc;
542}
543
544/**
545 * Shared code between the various init() implementations.
546 * @param aParent
547 * @return
548 */
549HRESULT Machine::initImpl(VirtualBox *aParent,
550 const Utf8Str &strConfigFile)
551{
552 LogFlowThisFuncEnter();
553
554 AssertReturn(aParent, E_INVALIDARG);
555 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
556
557 HRESULT rc = S_OK;
558
559 /* share the parent weakly */
560 unconst(mParent) = aParent;
561
562 /* allocate the essential machine data structure (the rest will be
563 * allocated later by initDataAndChildObjects() */
564 mData.allocate();
565
566 /* memorize the config file name (as provided) */
567 mData->m_strConfigFile = strConfigFile;
568
569 /* get the full file name */
570 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
571 if (RT_FAILURE(vrc1))
572 return setError(VBOX_E_FILE_ERROR,
573 tr("Invalid machine settings file name '%s' (%Rrc)"),
574 strConfigFile.c_str(),
575 vrc1);
576
577 LogFlowThisFuncLeave();
578
579 return rc;
580}
581
582/**
583 * Tries to create a machine settings file in the path stored in the machine
584 * instance data. Used when a new machine is created to fail gracefully if
585 * the settings file could not be written (e.g. because machine dir is read-only).
586 * @return
587 */
588HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
589{
590 HRESULT rc = S_OK;
591
592 // when we create a new machine, we must be able to create the settings file
593 RTFILE f = NIL_RTFILE;
594 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
595 if ( RT_SUCCESS(vrc)
596 || vrc == VERR_SHARING_VIOLATION
597 )
598 {
599 if (RT_SUCCESS(vrc))
600 RTFileClose(f);
601 if (!fForceOverwrite)
602 rc = setError(VBOX_E_FILE_ERROR,
603 tr("Machine settings file '%s' already exists"),
604 mData->m_strConfigFileFull.c_str());
605 else
606 {
607 /* try to delete the config file, as otherwise the creation
608 * of a new settings file will fail. */
609 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
610 if (RT_FAILURE(vrc2))
611 rc = setError(VBOX_E_FILE_ERROR,
612 tr("Could not delete the existing settings file '%s' (%Rrc)"),
613 mData->m_strConfigFileFull.c_str(), vrc2);
614 }
615 }
616 else if ( vrc != VERR_FILE_NOT_FOUND
617 && vrc != VERR_PATH_NOT_FOUND
618 )
619 rc = setError(VBOX_E_FILE_ERROR,
620 tr("Invalid machine settings file name '%s' (%Rrc)"),
621 mData->m_strConfigFileFull.c_str(),
622 vrc);
623 return rc;
624}
625
626/**
627 * Initializes the registered machine by loading the settings file.
628 * This method is separated from #init() in order to make it possible to
629 * retry the operation after VirtualBox startup instead of refusing to
630 * startup the whole VirtualBox server in case if the settings file of some
631 * registered VM is invalid or inaccessible.
632 *
633 * @note Must be always called from this object's write lock
634 * (unless called from #init() that doesn't need any locking).
635 * @note Locks the mUSBController method for writing.
636 * @note Subclasses must not call this method.
637 */
638HRESULT Machine::registeredInit()
639{
640 AssertReturn(!isSessionMachine(), E_FAIL);
641 AssertReturn(!isSnapshotMachine(), E_FAIL);
642 AssertReturn(!mData->mUuid.isEmpty(), E_FAIL);
643 AssertReturn(!mData->mAccessible, E_FAIL);
644
645 HRESULT rc = initDataAndChildObjects();
646
647 if (SUCCEEDED(rc))
648 {
649 /* Temporarily reset the registered flag in order to let setters
650 * potentially called from loadSettings() succeed (isMutable() used in
651 * all setters will return FALSE for a Machine instance if mRegistered
652 * is TRUE). */
653 mData->mRegistered = FALSE;
654
655 try
656 {
657 // load and parse machine XML; this will throw on XML or logic errors
658 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
659
660 if (mData->mUuid != mData->pMachineConfigFile->uuid)
661 throw setError(E_FAIL,
662 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
663 mData->pMachineConfigFile->uuid.raw(),
664 mData->m_strConfigFileFull.c_str(),
665 mData->mUuid.toString().c_str(),
666 mParent->settingsFilePath().c_str());
667
668 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
669 NULL /* const Guid *puuidRegistry */);
670 if (FAILED(rc)) throw rc;
671 }
672 catch (HRESULT err)
673 {
674 /* we assume that error info is set by the thrower */
675 rc = err;
676 }
677 catch (...)
678 {
679 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
680 }
681
682 /* Restore the registered flag (even on failure) */
683 mData->mRegistered = TRUE;
684 }
685
686 if (SUCCEEDED(rc))
687 {
688 /* Set mAccessible to TRUE only if we successfully locked and loaded
689 * the settings file */
690 mData->mAccessible = TRUE;
691
692 /* commit all changes made during loading the settings file */
693 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
694 }
695 else
696 {
697 /* If the machine is registered, then, instead of returning a
698 * failure, we mark it as inaccessible and set the result to
699 * success to give it a try later */
700
701 /* fetch the current error info */
702 mData->mAccessError = com::ErrorInfo();
703 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
704 mData->mUuid.raw(),
705 mData->mAccessError.getText().raw()));
706
707 /* rollback all changes */
708 rollback(false /* aNotify */);
709
710 // uninit media from this machine's media registry, or else
711 // reloading the settings will fail
712 mParent->unregisterMachineMedia(getId());
713
714 /* uninitialize the common part to make sure all data is reset to
715 * default (null) values */
716 uninitDataAndChildObjects();
717
718 rc = S_OK;
719 }
720
721 return rc;
722}
723
724/**
725 * Uninitializes the instance.
726 * Called either from FinalRelease() or by the parent when it gets destroyed.
727 *
728 * @note The caller of this method must make sure that this object
729 * a) doesn't have active callers on the current thread and b) is not locked
730 * by the current thread; otherwise uninit() will hang either a) due to
731 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
732 * a dead-lock caused by this thread waiting for all callers on the other
733 * threads are done but preventing them from doing so by holding a lock.
734 */
735void Machine::uninit()
736{
737 LogFlowThisFuncEnter();
738
739 Assert(!isWriteLockOnCurrentThread());
740
741 /* Enclose the state transition Ready->InUninit->NotReady */
742 AutoUninitSpan autoUninitSpan(this);
743 if (autoUninitSpan.uninitDone())
744 return;
745
746 Assert(!isSnapshotMachine());
747 Assert(!isSessionMachine());
748 Assert(!!mData);
749
750 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
751 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
752
753 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
754
755 if (!mData->mSession.mMachine.isNull())
756 {
757 /* Theoretically, this can only happen if the VirtualBox server has been
758 * terminated while there were clients running that owned open direct
759 * sessions. Since in this case we are definitely called by
760 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
761 * won't happen on the client watcher thread (because it does
762 * VirtualBox::addCaller() for the duration of the
763 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
764 * cannot happen until the VirtualBox caller is released). This is
765 * important, because SessionMachine::uninit() cannot correctly operate
766 * after we return from this method (it expects the Machine instance is
767 * still valid). We'll call it ourselves below.
768 */
769 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
770 (SessionMachine*)mData->mSession.mMachine));
771
772 if (Global::IsOnlineOrTransient(mData->mMachineState))
773 {
774 LogWarningThisFunc(("Setting state to Aborted!\n"));
775 /* set machine state using SessionMachine reimplementation */
776 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
777 }
778
779 /*
780 * Uninitialize SessionMachine using public uninit() to indicate
781 * an unexpected uninitialization.
782 */
783 mData->mSession.mMachine->uninit();
784 /* SessionMachine::uninit() must set mSession.mMachine to null */
785 Assert(mData->mSession.mMachine.isNull());
786 }
787
788 // uninit media from this machine's media registry, if they're still there
789 Guid uuidMachine(getId());
790 if (!uuidMachine.isEmpty()) // can be empty if we're called from a failure of Machine::init
791 mParent->unregisterMachineMedia(uuidMachine);
792
793 /* the lock is no more necessary (SessionMachine is uninitialized) */
794 alock.leave();
795
796 // has machine been modified?
797 if (mData->flModifications)
798 {
799 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
800 rollback(false /* aNotify */);
801 }
802
803 if (mData->mAccessible)
804 uninitDataAndChildObjects();
805
806 /* free the essential data structure last */
807 mData.free();
808
809 LogFlowThisFuncLeave();
810}
811
812// IMachine properties
813/////////////////////////////////////////////////////////////////////////////
814
815STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
816{
817 CheckComArgOutPointerValid(aParent);
818
819 AutoLimitedCaller autoCaller(this);
820 if (FAILED(autoCaller.rc())) return autoCaller.rc();
821
822 /* mParent is constant during life time, no need to lock */
823 ComObjPtr<VirtualBox> pVirtualBox(mParent);
824 pVirtualBox.queryInterfaceTo(aParent);
825
826 return S_OK;
827}
828
829STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
830{
831 CheckComArgOutPointerValid(aAccessible);
832
833 AutoLimitedCaller autoCaller(this);
834 if (FAILED(autoCaller.rc())) return autoCaller.rc();
835
836 LogFlowThisFunc(("ENTER\n"));
837
838 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
839
840 HRESULT rc = S_OK;
841
842 if (!mData->mAccessible)
843 {
844 /* try to initialize the VM once more if not accessible */
845
846 AutoReinitSpan autoReinitSpan(this);
847 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
848
849#ifdef DEBUG
850 LogFlowThisFunc(("Dumping media backreferences\n"));
851 mParent->dumpAllBackRefs();
852#endif
853
854 if (mData->pMachineConfigFile)
855 {
856 // reset the XML file to force loadSettings() (called from registeredInit())
857 // to parse it again; the file might have changed
858 delete mData->pMachineConfigFile;
859 mData->pMachineConfigFile = NULL;
860 }
861
862 rc = registeredInit();
863
864 if (SUCCEEDED(rc) && mData->mAccessible)
865 {
866 autoReinitSpan.setSucceeded();
867
868 /* make sure interesting parties will notice the accessibility
869 * state change */
870 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
871 mParent->onMachineDataChange(mData->mUuid);
872 }
873 }
874
875 if (SUCCEEDED(rc))
876 *aAccessible = mData->mAccessible;
877
878 LogFlowThisFuncLeave();
879
880 return rc;
881}
882
883STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
884{
885 CheckComArgOutPointerValid(aAccessError);
886
887 AutoLimitedCaller autoCaller(this);
888 if (FAILED(autoCaller.rc())) return autoCaller.rc();
889
890 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
891
892 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
893 {
894 /* return shortly */
895 aAccessError = NULL;
896 return S_OK;
897 }
898
899 HRESULT rc = S_OK;
900
901 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
902 rc = errorInfo.createObject();
903 if (SUCCEEDED(rc))
904 {
905 errorInfo->init(mData->mAccessError.getResultCode(),
906 mData->mAccessError.getInterfaceID().ref(),
907 Utf8Str(mData->mAccessError.getComponent()).c_str(),
908 Utf8Str(mData->mAccessError.getText()));
909 rc = errorInfo.queryInterfaceTo(aAccessError);
910 }
911
912 return rc;
913}
914
915STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
916{
917 CheckComArgOutPointerValid(aName);
918
919 AutoCaller autoCaller(this);
920 if (FAILED(autoCaller.rc())) return autoCaller.rc();
921
922 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
923
924 mUserData->s.strName.cloneTo(aName);
925
926 return S_OK;
927}
928
929STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
930{
931 CheckComArgStrNotEmptyOrNull(aName);
932
933 AutoCaller autoCaller(this);
934 if (FAILED(autoCaller.rc())) return autoCaller.rc();
935
936 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
937
938 HRESULT rc = checkStateDependency(MutableStateDep);
939 if (FAILED(rc)) return rc;
940
941 setModified(IsModified_MachineData);
942 mUserData.backup();
943 mUserData->s.strName = aName;
944
945 return S_OK;
946}
947
948STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
949{
950 CheckComArgOutPointerValid(aDescription);
951
952 AutoCaller autoCaller(this);
953 if (FAILED(autoCaller.rc())) return autoCaller.rc();
954
955 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
956
957 mUserData->s.strDescription.cloneTo(aDescription);
958
959 return S_OK;
960}
961
962STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
963{
964 AutoCaller autoCaller(this);
965 if (FAILED(autoCaller.rc())) return autoCaller.rc();
966
967 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
968
969 HRESULT rc = checkStateDependency(MutableStateDep);
970 if (FAILED(rc)) return rc;
971
972 setModified(IsModified_MachineData);
973 mUserData.backup();
974 mUserData->s.strDescription = aDescription;
975
976 return S_OK;
977}
978
979STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
980{
981 CheckComArgOutPointerValid(aId);
982
983 AutoLimitedCaller autoCaller(this);
984 if (FAILED(autoCaller.rc())) return autoCaller.rc();
985
986 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
987
988 mData->mUuid.toUtf16().cloneTo(aId);
989
990 return S_OK;
991}
992
993STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
994{
995 CheckComArgOutPointerValid(aOSTypeId);
996
997 AutoCaller autoCaller(this);
998 if (FAILED(autoCaller.rc())) return autoCaller.rc();
999
1000 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1001
1002 mUserData->s.strOsType.cloneTo(aOSTypeId);
1003
1004 return S_OK;
1005}
1006
1007STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1008{
1009 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1010
1011 AutoCaller autoCaller(this);
1012 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1013
1014 /* look up the object by Id to check it is valid */
1015 ComPtr<IGuestOSType> guestOSType;
1016 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1017 if (FAILED(rc)) return rc;
1018
1019 /* when setting, always use the "etalon" value for consistency -- lookup
1020 * by ID is case-insensitive and the input value may have different case */
1021 Bstr osTypeId;
1022 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1023 if (FAILED(rc)) return rc;
1024
1025 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1026
1027 rc = checkStateDependency(MutableStateDep);
1028 if (FAILED(rc)) return rc;
1029
1030 setModified(IsModified_MachineData);
1031 mUserData.backup();
1032 mUserData->s.strOsType = osTypeId;
1033
1034 return S_OK;
1035}
1036
1037
1038STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1039{
1040 CheckComArgOutPointerValid(aFirmwareType);
1041
1042 AutoCaller autoCaller(this);
1043 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1044
1045 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1046
1047 *aFirmwareType = mHWData->mFirmwareType;
1048
1049 return S_OK;
1050}
1051
1052STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1053{
1054 AutoCaller autoCaller(this);
1055 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1056 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1057
1058 int rc = checkStateDependency(MutableStateDep);
1059 if (FAILED(rc)) return rc;
1060
1061 setModified(IsModified_MachineData);
1062 mHWData.backup();
1063 mHWData->mFirmwareType = aFirmwareType;
1064
1065 return S_OK;
1066}
1067
1068STDMETHODIMP Machine::COMGETTER(KeyboardHidType)(KeyboardHidType_T *aKeyboardHidType)
1069{
1070 CheckComArgOutPointerValid(aKeyboardHidType);
1071
1072 AutoCaller autoCaller(this);
1073 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1074
1075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1076
1077 *aKeyboardHidType = mHWData->mKeyboardHidType;
1078
1079 return S_OK;
1080}
1081
1082STDMETHODIMP Machine::COMSETTER(KeyboardHidType)(KeyboardHidType_T aKeyboardHidType)
1083{
1084 AutoCaller autoCaller(this);
1085 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1086 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1087
1088 int rc = checkStateDependency(MutableStateDep);
1089 if (FAILED(rc)) return rc;
1090
1091 setModified(IsModified_MachineData);
1092 mHWData.backup();
1093 mHWData->mKeyboardHidType = aKeyboardHidType;
1094
1095 return S_OK;
1096}
1097
1098STDMETHODIMP Machine::COMGETTER(PointingHidType)(PointingHidType_T *aPointingHidType)
1099{
1100 CheckComArgOutPointerValid(aPointingHidType);
1101
1102 AutoCaller autoCaller(this);
1103 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1104
1105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1106
1107 *aPointingHidType = mHWData->mPointingHidType;
1108
1109 return S_OK;
1110}
1111
1112STDMETHODIMP Machine::COMSETTER(PointingHidType)(PointingHidType_T aPointingHidType)
1113{
1114 AutoCaller autoCaller(this);
1115 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1116 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1117
1118 int rc = checkStateDependency(MutableStateDep);
1119 if (FAILED(rc)) return rc;
1120
1121 setModified(IsModified_MachineData);
1122 mHWData.backup();
1123 mHWData->mPointingHidType = aPointingHidType;
1124
1125 return S_OK;
1126}
1127
1128STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1129{
1130 CheckComArgOutPointerValid(aChipsetType);
1131
1132 AutoCaller autoCaller(this);
1133 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1134
1135 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1136
1137 *aChipsetType = mHWData->mChipsetType;
1138
1139 return S_OK;
1140}
1141
1142STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1143{
1144 AutoCaller autoCaller(this);
1145 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1146 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1147
1148 int rc = checkStateDependency(MutableStateDep);
1149 if (FAILED(rc)) return rc;
1150
1151 setModified(IsModified_MachineData);
1152 mHWData.backup();
1153 mHWData->mChipsetType = aChipsetType;
1154
1155 return S_OK;
1156}
1157
1158STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1159{
1160 if (!aHWVersion)
1161 return E_POINTER;
1162
1163 AutoCaller autoCaller(this);
1164 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1165
1166 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1167
1168 mHWData->mHWVersion.cloneTo(aHWVersion);
1169
1170 return S_OK;
1171}
1172
1173STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1174{
1175 /* check known version */
1176 Utf8Str hwVersion = aHWVersion;
1177 if ( hwVersion.compare("1") != 0
1178 && hwVersion.compare("2") != 0)
1179 return setError(E_INVALIDARG,
1180 tr("Invalid hardware version: %ls\n"), aHWVersion);
1181
1182 AutoCaller autoCaller(this);
1183 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1184
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->mHWVersion = hwVersion;
1193
1194 return S_OK;
1195}
1196
1197STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1198{
1199 CheckComArgOutPointerValid(aUUID);
1200
1201 AutoCaller autoCaller(this);
1202 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1203
1204 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1205
1206 if (!mHWData->mHardwareUUID.isEmpty())
1207 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1208 else
1209 mData->mUuid.toUtf16().cloneTo(aUUID);
1210
1211 return S_OK;
1212}
1213
1214STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1215{
1216 Guid hardwareUUID(aUUID);
1217 if (hardwareUUID.isEmpty())
1218 return E_INVALIDARG;
1219
1220 AutoCaller autoCaller(this);
1221 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1222
1223 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1224
1225 HRESULT rc = checkStateDependency(MutableStateDep);
1226 if (FAILED(rc)) return rc;
1227
1228 setModified(IsModified_MachineData);
1229 mHWData.backup();
1230 if (hardwareUUID == mData->mUuid)
1231 mHWData->mHardwareUUID.clear();
1232 else
1233 mHWData->mHardwareUUID = hardwareUUID;
1234
1235 return S_OK;
1236}
1237
1238STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1239{
1240 if (!memorySize)
1241 return E_POINTER;
1242
1243 AutoCaller autoCaller(this);
1244 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1245
1246 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1247
1248 *memorySize = mHWData->mMemorySize;
1249
1250 return S_OK;
1251}
1252
1253STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1254{
1255 /* check RAM limits */
1256 if ( memorySize < MM_RAM_MIN_IN_MB
1257 || memorySize > MM_RAM_MAX_IN_MB
1258 )
1259 return setError(E_INVALIDARG,
1260 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1261 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1262
1263 AutoCaller autoCaller(this);
1264 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1265
1266 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1267
1268 HRESULT rc = checkStateDependency(MutableStateDep);
1269 if (FAILED(rc)) return rc;
1270
1271 setModified(IsModified_MachineData);
1272 mHWData.backup();
1273 mHWData->mMemorySize = memorySize;
1274
1275 return S_OK;
1276}
1277
1278STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1279{
1280 if (!CPUCount)
1281 return E_POINTER;
1282
1283 AutoCaller autoCaller(this);
1284 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1285
1286 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1287
1288 *CPUCount = mHWData->mCPUCount;
1289
1290 return S_OK;
1291}
1292
1293STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1294{
1295 /* check CPU limits */
1296 if ( CPUCount < SchemaDefs::MinCPUCount
1297 || CPUCount > SchemaDefs::MaxCPUCount
1298 )
1299 return setError(E_INVALIDARG,
1300 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1301 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1302
1303 AutoCaller autoCaller(this);
1304 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1305
1306 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1307
1308 /* We cant go below the current number of CPUs if hotplug is enabled*/
1309 if (mHWData->mCPUHotPlugEnabled)
1310 {
1311 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1312 {
1313 if (mHWData->mCPUAttached[idx])
1314 return setError(E_INVALIDARG,
1315 tr(": %lu (must be higher than or equal to %lu)"),
1316 CPUCount, idx+1);
1317 }
1318 }
1319
1320 HRESULT rc = checkStateDependency(MutableStateDep);
1321 if (FAILED(rc)) return rc;
1322
1323 setModified(IsModified_MachineData);
1324 mHWData.backup();
1325 mHWData->mCPUCount = CPUCount;
1326
1327 return S_OK;
1328}
1329
1330STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1331{
1332 if (!aExecutionCap)
1333 return E_POINTER;
1334
1335 AutoCaller autoCaller(this);
1336 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1337
1338 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1339
1340 *aExecutionCap = mHWData->mCpuExecutionCap;
1341
1342 return S_OK;
1343}
1344
1345STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1346{
1347 HRESULT rc = S_OK;
1348
1349 /* check throttle limits */
1350 if ( aExecutionCap < 1
1351 || aExecutionCap > 100
1352 )
1353 return setError(E_INVALIDARG,
1354 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1355 aExecutionCap, 1, 100);
1356
1357 AutoCaller autoCaller(this);
1358 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1359
1360 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1361
1362 alock.release();
1363 rc = onCPUExecutionCapChange(aExecutionCap);
1364 alock.acquire();
1365 if (FAILED(rc)) return rc;
1366
1367 setModified(IsModified_MachineData);
1368 mHWData.backup();
1369 mHWData->mCpuExecutionCap = aExecutionCap;
1370
1371 /* Save settings if online - todo why is this required?? */
1372 if (Global::IsOnline(mData->mMachineState))
1373 saveSettings(NULL);
1374
1375 return S_OK;
1376}
1377
1378
1379STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *enabled)
1380{
1381 if (!enabled)
1382 return E_POINTER;
1383
1384 AutoCaller autoCaller(this);
1385 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1386
1387 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1388
1389 *enabled = mHWData->mCPUHotPlugEnabled;
1390
1391 return S_OK;
1392}
1393
1394STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled)
1395{
1396 HRESULT rc = S_OK;
1397
1398 AutoCaller autoCaller(this);
1399 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1400
1401 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1402
1403 rc = checkStateDependency(MutableStateDep);
1404 if (FAILED(rc)) return rc;
1405
1406 if (mHWData->mCPUHotPlugEnabled != enabled)
1407 {
1408 if (enabled)
1409 {
1410 setModified(IsModified_MachineData);
1411 mHWData.backup();
1412
1413 /* Add the amount of CPUs currently attached */
1414 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1415 {
1416 mHWData->mCPUAttached[i] = true;
1417 }
1418 }
1419 else
1420 {
1421 /*
1422 * We can disable hotplug only if the amount of maximum CPUs is equal
1423 * to the amount of attached CPUs
1424 */
1425 unsigned cCpusAttached = 0;
1426 unsigned iHighestId = 0;
1427
1428 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1429 {
1430 if (mHWData->mCPUAttached[i])
1431 {
1432 cCpusAttached++;
1433 iHighestId = i;
1434 }
1435 }
1436
1437 if ( (cCpusAttached != mHWData->mCPUCount)
1438 || (iHighestId >= mHWData->mCPUCount))
1439 return setError(E_INVALIDARG,
1440 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached\n"));
1441
1442 setModified(IsModified_MachineData);
1443 mHWData.backup();
1444 }
1445 }
1446
1447 mHWData->mCPUHotPlugEnabled = enabled;
1448
1449 return rc;
1450}
1451
1452STDMETHODIMP Machine::COMGETTER(HpetEnabled)(BOOL *enabled)
1453{
1454 CheckComArgOutPointerValid(enabled);
1455
1456 AutoCaller autoCaller(this);
1457 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1458 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1459
1460 *enabled = mHWData->mHpetEnabled;
1461
1462 return S_OK;
1463}
1464
1465STDMETHODIMP Machine::COMSETTER(HpetEnabled)(BOOL enabled)
1466{
1467 HRESULT rc = S_OK;
1468
1469 AutoCaller autoCaller(this);
1470 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1471 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1472
1473 rc = checkStateDependency(MutableStateDep);
1474 if (FAILED(rc)) return rc;
1475
1476 setModified(IsModified_MachineData);
1477 mHWData.backup();
1478
1479 mHWData->mHpetEnabled = enabled;
1480
1481 return rc;
1482}
1483
1484STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1485{
1486 if (!memorySize)
1487 return E_POINTER;
1488
1489 AutoCaller autoCaller(this);
1490 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1491
1492 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1493
1494 *memorySize = mHWData->mVRAMSize;
1495
1496 return S_OK;
1497}
1498
1499STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1500{
1501 /* check VRAM limits */
1502 if (memorySize < SchemaDefs::MinGuestVRAM ||
1503 memorySize > SchemaDefs::MaxGuestVRAM)
1504 return setError(E_INVALIDARG,
1505 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1506 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1507
1508 AutoCaller autoCaller(this);
1509 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1510
1511 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1512
1513 HRESULT rc = checkStateDependency(MutableStateDep);
1514 if (FAILED(rc)) return rc;
1515
1516 setModified(IsModified_MachineData);
1517 mHWData.backup();
1518 mHWData->mVRAMSize = memorySize;
1519
1520 return S_OK;
1521}
1522
1523/** @todo this method should not be public */
1524STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1525{
1526 if (!memoryBalloonSize)
1527 return E_POINTER;
1528
1529 AutoCaller autoCaller(this);
1530 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1531
1532 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1533
1534 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
1535
1536 return S_OK;
1537}
1538
1539/**
1540 * Set the memory balloon size.
1541 *
1542 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1543 * we have to make sure that we never call IGuest from here.
1544 */
1545STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
1546{
1547 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1548#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1549 /* check limits */
1550 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1551 return setError(E_INVALIDARG,
1552 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1553 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1554
1555 AutoCaller autoCaller(this);
1556 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1557
1558 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1559
1560 setModified(IsModified_MachineData);
1561 mHWData.backup();
1562 mHWData->mMemoryBalloonSize = memoryBalloonSize;
1563
1564 return S_OK;
1565#else
1566 NOREF(memoryBalloonSize);
1567 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1568#endif
1569}
1570
1571STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *enabled)
1572{
1573 if (!enabled)
1574 return E_POINTER;
1575
1576 AutoCaller autoCaller(this);
1577 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1578
1579 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1580
1581 *enabled = mHWData->mPageFusionEnabled;
1582 return S_OK;
1583}
1584
1585STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL enabled)
1586{
1587#ifdef VBOX_WITH_PAGE_SHARING
1588 AutoCaller autoCaller(this);
1589 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1590
1591 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1592
1593 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1594 setModified(IsModified_MachineData);
1595 mHWData.backup();
1596 mHWData->mPageFusionEnabled = enabled;
1597 return S_OK;
1598#else
1599 NOREF(enabled);
1600 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1601#endif
1602}
1603
1604STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *enabled)
1605{
1606 if (!enabled)
1607 return E_POINTER;
1608
1609 AutoCaller autoCaller(this);
1610 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1611
1612 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1613
1614 *enabled = mHWData->mAccelerate3DEnabled;
1615
1616 return S_OK;
1617}
1618
1619STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
1620{
1621 AutoCaller autoCaller(this);
1622 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1623
1624 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1625
1626 HRESULT rc = checkStateDependency(MutableStateDep);
1627 if (FAILED(rc)) return rc;
1628
1629 /** @todo check validity! */
1630
1631 setModified(IsModified_MachineData);
1632 mHWData.backup();
1633 mHWData->mAccelerate3DEnabled = enable;
1634
1635 return S_OK;
1636}
1637
1638
1639STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *enabled)
1640{
1641 if (!enabled)
1642 return E_POINTER;
1643
1644 AutoCaller autoCaller(this);
1645 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1646
1647 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1648
1649 *enabled = mHWData->mAccelerate2DVideoEnabled;
1650
1651 return S_OK;
1652}
1653
1654STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
1655{
1656 AutoCaller autoCaller(this);
1657 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1658
1659 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1660
1661 HRESULT rc = checkStateDependency(MutableStateDep);
1662 if (FAILED(rc)) return rc;
1663
1664 /** @todo check validity! */
1665
1666 setModified(IsModified_MachineData);
1667 mHWData.backup();
1668 mHWData->mAccelerate2DVideoEnabled = enable;
1669
1670 return S_OK;
1671}
1672
1673STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
1674{
1675 if (!monitorCount)
1676 return E_POINTER;
1677
1678 AutoCaller autoCaller(this);
1679 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1680
1681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1682
1683 *monitorCount = mHWData->mMonitorCount;
1684
1685 return S_OK;
1686}
1687
1688STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
1689{
1690 /* make sure monitor count is a sensible number */
1691 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
1692 return setError(E_INVALIDARG,
1693 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1694 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
1695
1696 AutoCaller autoCaller(this);
1697 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1698
1699 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1700
1701 HRESULT rc = checkStateDependency(MutableStateDep);
1702 if (FAILED(rc)) return rc;
1703
1704 setModified(IsModified_MachineData);
1705 mHWData.backup();
1706 mHWData->mMonitorCount = monitorCount;
1707
1708 return S_OK;
1709}
1710
1711STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
1712{
1713 if (!biosSettings)
1714 return E_POINTER;
1715
1716 AutoCaller autoCaller(this);
1717 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1718
1719 /* mBIOSSettings is constant during life time, no need to lock */
1720 mBIOSSettings.queryInterfaceTo(biosSettings);
1721
1722 return S_OK;
1723}
1724
1725STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
1726{
1727 if (!aVal)
1728 return E_POINTER;
1729
1730 AutoCaller autoCaller(this);
1731 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1732
1733 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1734
1735 switch(property)
1736 {
1737 case CPUPropertyType_PAE:
1738 *aVal = mHWData->mPAEEnabled;
1739 break;
1740
1741 case CPUPropertyType_Synthetic:
1742 *aVal = mHWData->mSyntheticCpu;
1743 break;
1744
1745 default:
1746 return E_INVALIDARG;
1747 }
1748 return S_OK;
1749}
1750
1751STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
1752{
1753 AutoCaller autoCaller(this);
1754 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1755
1756 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1757
1758 HRESULT rc = checkStateDependency(MutableStateDep);
1759 if (FAILED(rc)) return rc;
1760
1761 switch(property)
1762 {
1763 case CPUPropertyType_PAE:
1764 setModified(IsModified_MachineData);
1765 mHWData.backup();
1766 mHWData->mPAEEnabled = !!aVal;
1767 break;
1768
1769 case CPUPropertyType_Synthetic:
1770 setModified(IsModified_MachineData);
1771 mHWData.backup();
1772 mHWData->mSyntheticCpu = !!aVal;
1773 break;
1774
1775 default:
1776 return E_INVALIDARG;
1777 }
1778 return S_OK;
1779}
1780
1781STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
1782{
1783 CheckComArgOutPointerValid(aValEax);
1784 CheckComArgOutPointerValid(aValEbx);
1785 CheckComArgOutPointerValid(aValEcx);
1786 CheckComArgOutPointerValid(aValEdx);
1787
1788 AutoCaller autoCaller(this);
1789 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1790
1791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1792
1793 switch(aId)
1794 {
1795 case 0x0:
1796 case 0x1:
1797 case 0x2:
1798 case 0x3:
1799 case 0x4:
1800 case 0x5:
1801 case 0x6:
1802 case 0x7:
1803 case 0x8:
1804 case 0x9:
1805 case 0xA:
1806 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
1807 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is not set"), aId);
1808
1809 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
1810 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
1811 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
1812 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
1813 break;
1814
1815 case 0x80000000:
1816 case 0x80000001:
1817 case 0x80000002:
1818 case 0x80000003:
1819 case 0x80000004:
1820 case 0x80000005:
1821 case 0x80000006:
1822 case 0x80000007:
1823 case 0x80000008:
1824 case 0x80000009:
1825 case 0x8000000A:
1826 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
1827 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is not set"), aId);
1828
1829 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
1830 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
1831 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
1832 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
1833 break;
1834
1835 default:
1836 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
1837 }
1838 return S_OK;
1839}
1840
1841STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
1842{
1843 AutoCaller autoCaller(this);
1844 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1845
1846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1847
1848 HRESULT rc = checkStateDependency(MutableStateDep);
1849 if (FAILED(rc)) return rc;
1850
1851 switch(aId)
1852 {
1853 case 0x0:
1854 case 0x1:
1855 case 0x2:
1856 case 0x3:
1857 case 0x4:
1858 case 0x5:
1859 case 0x6:
1860 case 0x7:
1861 case 0x8:
1862 case 0x9:
1863 case 0xA:
1864 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xA);
1865 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
1866 setModified(IsModified_MachineData);
1867 mHWData.backup();
1868 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
1869 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
1870 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
1871 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
1872 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
1873 break;
1874
1875 case 0x80000000:
1876 case 0x80000001:
1877 case 0x80000002:
1878 case 0x80000003:
1879 case 0x80000004:
1880 case 0x80000005:
1881 case 0x80000006:
1882 case 0x80000007:
1883 case 0x80000008:
1884 case 0x80000009:
1885 case 0x8000000A:
1886 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xA);
1887 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
1888 setModified(IsModified_MachineData);
1889 mHWData.backup();
1890 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
1891 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
1892 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
1893 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
1894 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
1895 break;
1896
1897 default:
1898 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
1899 }
1900 return S_OK;
1901}
1902
1903STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
1904{
1905 AutoCaller autoCaller(this);
1906 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1907
1908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1909
1910 HRESULT rc = checkStateDependency(MutableStateDep);
1911 if (FAILED(rc)) return rc;
1912
1913 switch(aId)
1914 {
1915 case 0x0:
1916 case 0x1:
1917 case 0x2:
1918 case 0x3:
1919 case 0x4:
1920 case 0x5:
1921 case 0x6:
1922 case 0x7:
1923 case 0x8:
1924 case 0x9:
1925 case 0xA:
1926 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xA);
1927 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
1928 setModified(IsModified_MachineData);
1929 mHWData.backup();
1930 /* Invalidate leaf. */
1931 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
1932 break;
1933
1934 case 0x80000000:
1935 case 0x80000001:
1936 case 0x80000002:
1937 case 0x80000003:
1938 case 0x80000004:
1939 case 0x80000005:
1940 case 0x80000006:
1941 case 0x80000007:
1942 case 0x80000008:
1943 case 0x80000009:
1944 case 0x8000000A:
1945 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xA);
1946 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
1947 setModified(IsModified_MachineData);
1948 mHWData.backup();
1949 /* Invalidate leaf. */
1950 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
1951 break;
1952
1953 default:
1954 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
1955 }
1956 return S_OK;
1957}
1958
1959STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
1960{
1961 AutoCaller autoCaller(this);
1962 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1963
1964 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1965
1966 HRESULT rc = checkStateDependency(MutableStateDep);
1967 if (FAILED(rc)) return rc;
1968
1969 setModified(IsModified_MachineData);
1970 mHWData.backup();
1971
1972 /* Invalidate all standard leafs. */
1973 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
1974 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
1975
1976 /* Invalidate all extended leafs. */
1977 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
1978 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
1979
1980 return S_OK;
1981}
1982
1983STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
1984{
1985 if (!aVal)
1986 return E_POINTER;
1987
1988 AutoCaller autoCaller(this);
1989 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1990
1991 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1992
1993 switch(property)
1994 {
1995 case HWVirtExPropertyType_Enabled:
1996 *aVal = mHWData->mHWVirtExEnabled;
1997 break;
1998
1999 case HWVirtExPropertyType_Exclusive:
2000 *aVal = mHWData->mHWVirtExExclusive;
2001 break;
2002
2003 case HWVirtExPropertyType_VPID:
2004 *aVal = mHWData->mHWVirtExVPIDEnabled;
2005 break;
2006
2007 case HWVirtExPropertyType_NestedPaging:
2008 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2009 break;
2010
2011 case HWVirtExPropertyType_LargePages:
2012 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2013 break;
2014
2015 case HWVirtExPropertyType_Force:
2016 *aVal = mHWData->mHWVirtExForceEnabled;
2017 break;
2018
2019 default:
2020 return E_INVALIDARG;
2021 }
2022 return S_OK;
2023}
2024
2025STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2026{
2027 AutoCaller autoCaller(this);
2028 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2029
2030 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2031
2032 HRESULT rc = checkStateDependency(MutableStateDep);
2033 if (FAILED(rc)) return rc;
2034
2035 switch(property)
2036 {
2037 case HWVirtExPropertyType_Enabled:
2038 setModified(IsModified_MachineData);
2039 mHWData.backup();
2040 mHWData->mHWVirtExEnabled = !!aVal;
2041 break;
2042
2043 case HWVirtExPropertyType_Exclusive:
2044 setModified(IsModified_MachineData);
2045 mHWData.backup();
2046 mHWData->mHWVirtExExclusive = !!aVal;
2047 break;
2048
2049 case HWVirtExPropertyType_VPID:
2050 setModified(IsModified_MachineData);
2051 mHWData.backup();
2052 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2053 break;
2054
2055 case HWVirtExPropertyType_NestedPaging:
2056 setModified(IsModified_MachineData);
2057 mHWData.backup();
2058 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2059 break;
2060
2061 case HWVirtExPropertyType_LargePages:
2062 setModified(IsModified_MachineData);
2063 mHWData.backup();
2064 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2065 break;
2066
2067 case HWVirtExPropertyType_Force:
2068 setModified(IsModified_MachineData);
2069 mHWData.backup();
2070 mHWData->mHWVirtExForceEnabled = !!aVal;
2071 break;
2072
2073 default:
2074 return E_INVALIDARG;
2075 }
2076
2077 return S_OK;
2078}
2079
2080STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2081{
2082 CheckComArgOutPointerValid(aSnapshotFolder);
2083
2084 AutoCaller autoCaller(this);
2085 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2086
2087 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2088
2089 Utf8Str strFullSnapshotFolder;
2090 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2091 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2092
2093 return S_OK;
2094}
2095
2096STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2097{
2098 /* @todo (r=dmik):
2099 * 1. Allow to change the name of the snapshot folder containing snapshots
2100 * 2. Rename the folder on disk instead of just changing the property
2101 * value (to be smart and not to leave garbage). Note that it cannot be
2102 * done here because the change may be rolled back. Thus, the right
2103 * place is #saveSettings().
2104 */
2105
2106 AutoCaller autoCaller(this);
2107 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2108
2109 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2110
2111 HRESULT rc = checkStateDependency(MutableStateDep);
2112 if (FAILED(rc)) return rc;
2113
2114 if (!mData->mCurrentSnapshot.isNull())
2115 return setError(E_FAIL,
2116 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2117
2118 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2119
2120 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2121 if (strSnapshotFolder.isEmpty())
2122 strSnapshotFolder = "Snapshots";
2123 int vrc = calculateFullPath(strSnapshotFolder,
2124 strSnapshotFolder);
2125 if (RT_FAILURE(vrc))
2126 return setError(E_FAIL,
2127 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2128 aSnapshotFolder, vrc);
2129
2130 setModified(IsModified_MachineData);
2131 mUserData.backup();
2132
2133 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2134
2135 return S_OK;
2136}
2137
2138STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2139{
2140 if (ComSafeArrayOutIsNull(aAttachments))
2141 return E_POINTER;
2142
2143 AutoCaller autoCaller(this);
2144 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2145
2146 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2147
2148 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2149 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2150
2151 return S_OK;
2152}
2153
2154STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2155{
2156 if (!vrdeServer)
2157 return E_POINTER;
2158
2159 AutoCaller autoCaller(this);
2160 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2161
2162 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2163
2164 Assert(!!mVRDEServer);
2165 mVRDEServer.queryInterfaceTo(vrdeServer);
2166
2167 return S_OK;
2168}
2169
2170STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2171{
2172 if (!audioAdapter)
2173 return E_POINTER;
2174
2175 AutoCaller autoCaller(this);
2176 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2177
2178 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2179
2180 mAudioAdapter.queryInterfaceTo(audioAdapter);
2181 return S_OK;
2182}
2183
2184STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2185{
2186#ifdef VBOX_WITH_VUSB
2187 CheckComArgOutPointerValid(aUSBController);
2188
2189 AutoCaller autoCaller(this);
2190 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2191 MultiResult rc(S_OK);
2192
2193# ifdef VBOX_WITH_USB
2194 rc = mParent->host()->checkUSBProxyService();
2195 if (FAILED(rc)) return rc;
2196# endif
2197
2198 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2199
2200 return rc = mUSBController.queryInterfaceTo(aUSBController);
2201#else
2202 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2203 * extended error info to indicate that USB is simply not available
2204 * (w/o treating it as a failure), for example, as in OSE */
2205 NOREF(aUSBController);
2206 ReturnComNotImplemented();
2207#endif /* VBOX_WITH_VUSB */
2208}
2209
2210STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2211{
2212 CheckComArgOutPointerValid(aFilePath);
2213
2214 AutoLimitedCaller autoCaller(this);
2215 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2216
2217 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2218
2219 mData->m_strConfigFileFull.cloneTo(aFilePath);
2220 return S_OK;
2221}
2222
2223STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2224{
2225 CheckComArgOutPointerValid(aModified);
2226
2227 AutoCaller autoCaller(this);
2228 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2229
2230 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2231
2232 HRESULT rc = checkStateDependency(MutableStateDep);
2233 if (FAILED(rc)) return rc;
2234
2235 if (!mData->pMachineConfigFile->fileExists())
2236 // this is a new machine, and no config file exists yet:
2237 *aModified = TRUE;
2238 else
2239 *aModified = (mData->flModifications != 0);
2240
2241 return S_OK;
2242}
2243
2244STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2245{
2246 CheckComArgOutPointerValid(aSessionState);
2247
2248 AutoCaller autoCaller(this);
2249 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2250
2251 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2252
2253 *aSessionState = mData->mSession.mState;
2254
2255 return S_OK;
2256}
2257
2258STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2259{
2260 CheckComArgOutPointerValid(aSessionType);
2261
2262 AutoCaller autoCaller(this);
2263 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2264
2265 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2266
2267 mData->mSession.mType.cloneTo(aSessionType);
2268
2269 return S_OK;
2270}
2271
2272STDMETHODIMP Machine::COMGETTER(SessionPid)(ULONG *aSessionPid)
2273{
2274 CheckComArgOutPointerValid(aSessionPid);
2275
2276 AutoCaller autoCaller(this);
2277 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2278
2279 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2280
2281 *aSessionPid = mData->mSession.mPid;
2282
2283 return S_OK;
2284}
2285
2286STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2287{
2288 if (!machineState)
2289 return E_POINTER;
2290
2291 AutoCaller autoCaller(this);
2292 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2293
2294 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2295
2296 *machineState = mData->mMachineState;
2297
2298 return S_OK;
2299}
2300
2301STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2302{
2303 CheckComArgOutPointerValid(aLastStateChange);
2304
2305 AutoCaller autoCaller(this);
2306 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2307
2308 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2309
2310 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2311
2312 return S_OK;
2313}
2314
2315STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2316{
2317 CheckComArgOutPointerValid(aStateFilePath);
2318
2319 AutoCaller autoCaller(this);
2320 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2321
2322 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2323
2324 mSSData->mStateFilePath.cloneTo(aStateFilePath);
2325
2326 return S_OK;
2327}
2328
2329STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2330{
2331 CheckComArgOutPointerValid(aLogFolder);
2332
2333 AutoCaller autoCaller(this);
2334 AssertComRCReturnRC(autoCaller.rc());
2335
2336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2337
2338 Utf8Str logFolder;
2339 getLogFolder(logFolder);
2340 logFolder.cloneTo(aLogFolder);
2341
2342 return S_OK;
2343}
2344
2345STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2346{
2347 CheckComArgOutPointerValid(aCurrentSnapshot);
2348
2349 AutoCaller autoCaller(this);
2350 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2351
2352 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2353
2354 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2355
2356 return S_OK;
2357}
2358
2359STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2360{
2361 CheckComArgOutPointerValid(aSnapshotCount);
2362
2363 AutoCaller autoCaller(this);
2364 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2365
2366 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2367
2368 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2369 ? 0
2370 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2371
2372 return S_OK;
2373}
2374
2375STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2376{
2377 CheckComArgOutPointerValid(aCurrentStateModified);
2378
2379 AutoCaller autoCaller(this);
2380 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2381
2382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2383
2384 /* Note: for machines with no snapshots, we always return FALSE
2385 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2386 * reasons :) */
2387
2388 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2389 ? FALSE
2390 : mData->mCurrentStateModified;
2391
2392 return S_OK;
2393}
2394
2395STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2396{
2397 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2398
2399 AutoCaller autoCaller(this);
2400 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2401
2402 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2403
2404 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2405 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2406
2407 return S_OK;
2408}
2409
2410STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2411{
2412 CheckComArgOutPointerValid(aClipboardMode);
2413
2414 AutoCaller autoCaller(this);
2415 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2416
2417 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2418
2419 *aClipboardMode = mHWData->mClipboardMode;
2420
2421 return S_OK;
2422}
2423
2424STDMETHODIMP
2425Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2426{
2427 AutoCaller autoCaller(this);
2428 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2429
2430 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2431
2432 HRESULT rc = checkStateDependency(MutableStateDep);
2433 if (FAILED(rc)) return rc;
2434
2435 setModified(IsModified_MachineData);
2436 mHWData.backup();
2437 mHWData->mClipboardMode = aClipboardMode;
2438
2439 return S_OK;
2440}
2441
2442STDMETHODIMP
2443Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
2444{
2445 CheckComArgOutPointerValid(aPatterns);
2446
2447 AutoCaller autoCaller(this);
2448 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2449
2450 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2451
2452 try
2453 {
2454 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
2455 }
2456 catch (...)
2457 {
2458 return VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
2459 }
2460
2461 return S_OK;
2462}
2463
2464STDMETHODIMP
2465Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
2466{
2467 AutoCaller autoCaller(this);
2468 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2469
2470 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2471
2472 HRESULT rc = checkStateDependency(MutableStateDep);
2473 if (FAILED(rc)) return rc;
2474
2475 setModified(IsModified_MachineData);
2476 mHWData.backup();
2477 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
2478 return rc;
2479}
2480
2481STDMETHODIMP
2482Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
2483{
2484 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
2485
2486 AutoCaller autoCaller(this);
2487 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2488
2489 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2490
2491 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
2492 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
2493
2494 return S_OK;
2495}
2496
2497STDMETHODIMP
2498Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
2499{
2500 CheckComArgOutPointerValid(aEnabled);
2501
2502 AutoCaller autoCaller(this);
2503 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2504
2505 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2506
2507 *aEnabled = mUserData->s.fTeleporterEnabled;
2508
2509 return S_OK;
2510}
2511
2512STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
2513{
2514 AutoCaller autoCaller(this);
2515 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2516
2517 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2518
2519 /* Only allow it to be set to true when PoweredOff or Aborted.
2520 (Clearing it is always permitted.) */
2521 if ( aEnabled
2522 && mData->mRegistered
2523 && ( !isSessionMachine()
2524 || ( mData->mMachineState != MachineState_PoweredOff
2525 && mData->mMachineState != MachineState_Teleported
2526 && mData->mMachineState != MachineState_Aborted
2527 )
2528 )
2529 )
2530 return setError(VBOX_E_INVALID_VM_STATE,
2531 tr("The machine is not powered off (state is %s)"),
2532 Global::stringifyMachineState(mData->mMachineState));
2533
2534 setModified(IsModified_MachineData);
2535 mUserData.backup();
2536 mUserData->s.fTeleporterEnabled = !!aEnabled;
2537
2538 return S_OK;
2539}
2540
2541STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
2542{
2543 CheckComArgOutPointerValid(aPort);
2544
2545 AutoCaller autoCaller(this);
2546 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2547
2548 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2549
2550 *aPort = (ULONG)mUserData->s.uTeleporterPort;
2551
2552 return S_OK;
2553}
2554
2555STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
2556{
2557 if (aPort >= _64K)
2558 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
2559
2560 AutoCaller autoCaller(this);
2561 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2562
2563 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2564
2565 HRESULT rc = checkStateDependency(MutableStateDep);
2566 if (FAILED(rc)) return rc;
2567
2568 setModified(IsModified_MachineData);
2569 mUserData.backup();
2570 mUserData->s.uTeleporterPort = (uint32_t)aPort;
2571
2572 return S_OK;
2573}
2574
2575STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
2576{
2577 CheckComArgOutPointerValid(aAddress);
2578
2579 AutoCaller autoCaller(this);
2580 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2581
2582 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2583
2584 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
2585
2586 return S_OK;
2587}
2588
2589STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
2590{
2591 AutoCaller autoCaller(this);
2592 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2593
2594 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2595
2596 HRESULT rc = checkStateDependency(MutableStateDep);
2597 if (FAILED(rc)) return rc;
2598
2599 setModified(IsModified_MachineData);
2600 mUserData.backup();
2601 mUserData->s.strTeleporterAddress = aAddress;
2602
2603 return S_OK;
2604}
2605
2606STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
2607{
2608 CheckComArgOutPointerValid(aPassword);
2609
2610 AutoCaller autoCaller(this);
2611 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2612
2613 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2614
2615 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
2616
2617 return S_OK;
2618}
2619
2620STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
2621{
2622 AutoCaller autoCaller(this);
2623 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2624
2625 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2626
2627 HRESULT rc = checkStateDependency(MutableStateDep);
2628 if (FAILED(rc)) return rc;
2629
2630 setModified(IsModified_MachineData);
2631 mUserData.backup();
2632 mUserData->s.strTeleporterPassword = aPassword;
2633
2634 return S_OK;
2635}
2636
2637STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
2638{
2639 CheckComArgOutPointerValid(aState);
2640
2641 AutoCaller autoCaller(this);
2642 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2643
2644 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2645
2646 *aState = mUserData->s.enmFaultToleranceState;
2647 return S_OK;
2648}
2649
2650STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
2651{
2652 AutoCaller autoCaller(this);
2653 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2654
2655 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2656
2657 /* @todo deal with running state change. */
2658 HRESULT rc = checkStateDependency(MutableStateDep);
2659 if (FAILED(rc)) return rc;
2660
2661 setModified(IsModified_MachineData);
2662 mUserData.backup();
2663 mUserData->s.enmFaultToleranceState = aState;
2664 return S_OK;
2665}
2666
2667STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
2668{
2669 CheckComArgOutPointerValid(aAddress);
2670
2671 AutoCaller autoCaller(this);
2672 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2673
2674 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2675
2676 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
2677 return S_OK;
2678}
2679
2680STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
2681{
2682 AutoCaller autoCaller(this);
2683 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2684
2685 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2686
2687 /* @todo deal with running state change. */
2688 HRESULT rc = checkStateDependency(MutableStateDep);
2689 if (FAILED(rc)) return rc;
2690
2691 setModified(IsModified_MachineData);
2692 mUserData.backup();
2693 mUserData->s.strFaultToleranceAddress = aAddress;
2694 return S_OK;
2695}
2696
2697STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
2698{
2699 CheckComArgOutPointerValid(aPort);
2700
2701 AutoCaller autoCaller(this);
2702 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2703
2704 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2705
2706 *aPort = mUserData->s.uFaultTolerancePort;
2707 return S_OK;
2708}
2709
2710STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
2711{
2712 AutoCaller autoCaller(this);
2713 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2714
2715 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2716
2717 /* @todo deal with running state change. */
2718 HRESULT rc = checkStateDependency(MutableStateDep);
2719 if (FAILED(rc)) return rc;
2720
2721 setModified(IsModified_MachineData);
2722 mUserData.backup();
2723 mUserData->s.uFaultTolerancePort = aPort;
2724 return S_OK;
2725}
2726
2727STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
2728{
2729 CheckComArgOutPointerValid(aPassword);
2730
2731 AutoCaller autoCaller(this);
2732 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2733
2734 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2735
2736 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
2737
2738 return S_OK;
2739}
2740
2741STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
2742{
2743 AutoCaller autoCaller(this);
2744 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2745
2746 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2747
2748 /* @todo deal with running state change. */
2749 HRESULT rc = checkStateDependency(MutableStateDep);
2750 if (FAILED(rc)) return rc;
2751
2752 setModified(IsModified_MachineData);
2753 mUserData.backup();
2754 mUserData->s.strFaultTolerancePassword = aPassword;
2755
2756 return S_OK;
2757}
2758
2759STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
2760{
2761 CheckComArgOutPointerValid(aInterval);
2762
2763 AutoCaller autoCaller(this);
2764 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2765
2766 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2767
2768 *aInterval = mUserData->s.uFaultToleranceInterval;
2769 return S_OK;
2770}
2771
2772STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
2773{
2774 AutoCaller autoCaller(this);
2775 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2776
2777 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2778
2779 /* @todo deal with running state change. */
2780 HRESULT rc = checkStateDependency(MutableStateDep);
2781 if (FAILED(rc)) return rc;
2782
2783 setModified(IsModified_MachineData);
2784 mUserData.backup();
2785 mUserData->s.uFaultToleranceInterval = aInterval;
2786 return S_OK;
2787}
2788
2789STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
2790{
2791 CheckComArgOutPointerValid(aEnabled);
2792
2793 AutoCaller autoCaller(this);
2794 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2795
2796 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2797
2798 *aEnabled = mUserData->s.fRTCUseUTC;
2799
2800 return S_OK;
2801}
2802
2803STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
2804{
2805 AutoCaller autoCaller(this);
2806 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2807
2808 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2809
2810 /* Only allow it to be set to true when PoweredOff or Aborted.
2811 (Clearing it is always permitted.) */
2812 if ( aEnabled
2813 && mData->mRegistered
2814 && ( !isSessionMachine()
2815 || ( mData->mMachineState != MachineState_PoweredOff
2816 && mData->mMachineState != MachineState_Teleported
2817 && mData->mMachineState != MachineState_Aborted
2818 )
2819 )
2820 )
2821 return setError(VBOX_E_INVALID_VM_STATE,
2822 tr("The machine is not powered off (state is %s)"),
2823 Global::stringifyMachineState(mData->mMachineState));
2824
2825 setModified(IsModified_MachineData);
2826 mUserData.backup();
2827 mUserData->s.fRTCUseUTC = !!aEnabled;
2828
2829 return S_OK;
2830}
2831
2832STDMETHODIMP Machine::COMGETTER(IoCacheEnabled)(BOOL *aEnabled)
2833{
2834 CheckComArgOutPointerValid(aEnabled);
2835
2836 AutoCaller autoCaller(this);
2837 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2838
2839 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2840
2841 *aEnabled = mHWData->mIoCacheEnabled;
2842
2843 return S_OK;
2844}
2845
2846STDMETHODIMP Machine::COMSETTER(IoCacheEnabled)(BOOL aEnabled)
2847{
2848 AutoCaller autoCaller(this);
2849 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2850
2851 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2852
2853 HRESULT rc = checkStateDependency(MutableStateDep);
2854 if (FAILED(rc)) return rc;
2855
2856 setModified(IsModified_MachineData);
2857 mHWData.backup();
2858 mHWData->mIoCacheEnabled = aEnabled;
2859
2860 return S_OK;
2861}
2862
2863STDMETHODIMP Machine::COMGETTER(IoCacheSize)(ULONG *aIoCacheSize)
2864{
2865 CheckComArgOutPointerValid(aIoCacheSize);
2866
2867 AutoCaller autoCaller(this);
2868 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2869
2870 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2871
2872 *aIoCacheSize = mHWData->mIoCacheSize;
2873
2874 return S_OK;
2875}
2876
2877STDMETHODIMP Machine::COMSETTER(IoCacheSize)(ULONG aIoCacheSize)
2878{
2879 AutoCaller autoCaller(this);
2880 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2881
2882 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2883
2884 HRESULT rc = checkStateDependency(MutableStateDep);
2885 if (FAILED(rc)) return rc;
2886
2887 setModified(IsModified_MachineData);
2888 mHWData.backup();
2889 mHWData->mIoCacheSize = aIoCacheSize;
2890
2891 return S_OK;
2892}
2893
2894
2895/**
2896 * @note Locks objects!
2897 */
2898STDMETHODIMP Machine::LockMachine(ISession *aSession,
2899 LockType_T lockType)
2900{
2901 CheckComArgNotNull(aSession);
2902
2903 AutoCaller autoCaller(this);
2904 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2905
2906 /* check the session state */
2907 SessionState_T state;
2908 HRESULT rc = aSession->COMGETTER(State)(&state);
2909 if (FAILED(rc)) return rc;
2910
2911 if (state != SessionState_Unlocked)
2912 return setError(VBOX_E_INVALID_OBJECT_STATE,
2913 tr("The given session is busy"));
2914
2915 // get the client's IInternalSessionControl interface
2916 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2917 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
2918 E_INVALIDARG);
2919
2920 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2921
2922 if (!mData->mRegistered)
2923 return setError(E_UNEXPECTED,
2924 tr("The machine '%s' is not registered"),
2925 mUserData->s.strName.c_str());
2926
2927 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2928
2929 SessionState_T oldState = mData->mSession.mState;
2930 /* Hack: in case the session is closing and there is a progress object
2931 * which allows waiting for the session to be closed, take the opportunity
2932 * and do a limited wait (max. 1 second). This helps a lot when the system
2933 * is busy and thus session closing can take a little while. */
2934 if ( mData->mSession.mState == SessionState_Unlocking
2935 && mData->mSession.mProgress)
2936 {
2937 alock.release();
2938 mData->mSession.mProgress->WaitForCompletion(1000);
2939 alock.acquire();
2940 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2941 }
2942
2943 // try again now
2944 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
2945 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
2946 )
2947 {
2948 // OK, share the session... we are now dealing with three processes:
2949 // 1) VBoxSVC (where this code runs);
2950 // 2) process C: the caller's client process (who wants a shared session);
2951 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
2952
2953 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
2954 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
2955 ComAssertRet(!pSessionW.isNull(), E_FAIL);
2956 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
2957 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
2958
2959 /*
2960 * Leave the lock before calling the client process. It's safe here
2961 * since the only thing to do after we get the lock again is to add
2962 * the remote control to the list (which doesn't directly influence
2963 * anything).
2964 */
2965 alock.leave();
2966
2967 // get the console of the session holding the write lock (this is a remote call)
2968 ComPtr<IConsole> pConsoleW;
2969 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
2970 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
2971 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
2972 if (FAILED(rc))
2973 // the failure may occur w/o any error info (from RPC), so provide one
2974 return setError(VBOX_E_VM_ERROR,
2975 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
2976
2977 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
2978
2979 // share the session machine and W's console with the caller's session
2980 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
2981 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
2982 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
2983
2984 if (FAILED(rc))
2985 // the failure may occur w/o any error info (from RPC), so provide one
2986 return setError(VBOX_E_VM_ERROR,
2987 tr("Failed to assign the machine to the session (%Rrc)"), rc);
2988 alock.enter();
2989
2990 // need to revalidate the state after entering the lock again
2991 if (mData->mSession.mState != SessionState_Locked)
2992 {
2993 pSessionControl->Uninitialize();
2994 return setError(VBOX_E_INVALID_SESSION_STATE,
2995 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
2996 mUserData->s.strName.c_str());
2997 }
2998
2999 // add the caller's session to the list
3000 mData->mSession.mRemoteControls.push_back(pSessionControl);
3001 }
3002 else if ( mData->mSession.mState == SessionState_Locked
3003 || mData->mSession.mState == SessionState_Unlocking
3004 )
3005 {
3006 // sharing not permitted, or machine still unlocking:
3007 return setError(VBOX_E_INVALID_OBJECT_STATE,
3008 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3009 mUserData->s.strName.c_str());
3010 }
3011 else
3012 {
3013 // machine is not locked: then write-lock the machine (create the session machine)
3014
3015 // must not be busy
3016 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3017
3018 // get the caller's session PID
3019 RTPROCESS pid = NIL_RTPROCESS;
3020 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3021 pSessionControl->GetPID((ULONG*)&pid);
3022 Assert(pid != NIL_RTPROCESS);
3023
3024 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3025
3026 if (fLaunchingVMProcess)
3027 {
3028 // this machine is awaiting for a spawning session to be opened:
3029 // then the calling process must be the one that got started by
3030 // launchVMProcess()
3031
3032 LogFlowThisFunc(("mSession.mPid=%d(0x%x)\n", mData->mSession.mPid, mData->mSession.mPid));
3033 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3034
3035 if (mData->mSession.mPid != pid)
3036 return setError(E_ACCESSDENIED,
3037 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3038 "machine '%s', while only the process started by launchVMProcess (PID=0x%08X) is allowed"),
3039 pid, mUserData->s.strName.c_str(), mData->mSession.mPid);
3040 }
3041
3042 // create the mutable SessionMachine from the current machine
3043 ComObjPtr<SessionMachine> sessionMachine;
3044 sessionMachine.createObject();
3045 rc = sessionMachine->init(this);
3046 AssertComRC(rc);
3047
3048 /* NOTE: doing return from this function after this point but
3049 * before the end is forbidden since it may call SessionMachine::uninit()
3050 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3051 * lock while still holding the Machine lock in alock so that a deadlock
3052 * is possible due to the wrong lock order. */
3053
3054 if (SUCCEEDED(rc))
3055 {
3056 /*
3057 * Set the session state to Spawning to protect against subsequent
3058 * attempts to open a session and to unregister the machine after
3059 * we leave the lock.
3060 */
3061 SessionState_T origState = mData->mSession.mState;
3062 mData->mSession.mState = SessionState_Spawning;
3063
3064 /*
3065 * Leave the lock before calling the client process -- it will call
3066 * Machine/SessionMachine methods. Leaving the lock here is quite safe
3067 * because the state is Spawning, so that openRemotesession() and
3068 * openExistingSession() calls will fail. This method, called before we
3069 * enter the lock again, will fail because of the wrong PID.
3070 *
3071 * Note that mData->mSession.mRemoteControls accessed outside
3072 * the lock may not be modified when state is Spawning, so it's safe.
3073 */
3074 alock.leave();
3075
3076 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3077 rc = pSessionControl->AssignMachine(sessionMachine);
3078 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3079
3080 /* The failure may occur w/o any error info (from RPC), so provide one */
3081 if (FAILED(rc))
3082 setError(VBOX_E_VM_ERROR,
3083 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3084
3085 if ( SUCCEEDED(rc)
3086 && fLaunchingVMProcess
3087 )
3088 {
3089 /* complete the remote session initialization */
3090
3091 /* get the console from the direct session */
3092 ComPtr<IConsole> console;
3093 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3094 ComAssertComRC(rc);
3095
3096 if (SUCCEEDED(rc) && !console)
3097 {
3098 ComAssert(!!console);
3099 rc = E_FAIL;
3100 }
3101
3102 /* assign machine & console to the remote session */
3103 if (SUCCEEDED(rc))
3104 {
3105 /*
3106 * after openRemoteSession(), the first and the only
3107 * entry in remoteControls is that remote session
3108 */
3109 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3110 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3111 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3112
3113 /* The failure may occur w/o any error info (from RPC), so provide one */
3114 if (FAILED(rc))
3115 setError(VBOX_E_VM_ERROR,
3116 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3117 }
3118
3119 if (FAILED(rc))
3120 pSessionControl->Uninitialize();
3121 }
3122
3123 /* enter the lock again */
3124 alock.enter();
3125
3126 /* Restore the session state */
3127 mData->mSession.mState = origState;
3128 }
3129
3130 // finalize spawning anyway (this is why we don't return on errors above)
3131 if (fLaunchingVMProcess)
3132 {
3133 /* Note that the progress object is finalized later */
3134 /** @todo Consider checking mData->mSession.mProgress for cancellation
3135 * around here. */
3136
3137 /* We don't reset mSession.mPid here because it is necessary for
3138 * SessionMachine::uninit() to reap the child process later. */
3139
3140 if (FAILED(rc))
3141 {
3142 /* Close the remote session, remove the remote control from the list
3143 * and reset session state to Closed (@note keep the code in sync
3144 * with the relevant part in openSession()). */
3145
3146 Assert(mData->mSession.mRemoteControls.size() == 1);
3147 if (mData->mSession.mRemoteControls.size() == 1)
3148 {
3149 ErrorInfoKeeper eik;
3150 mData->mSession.mRemoteControls.front()->Uninitialize();
3151 }
3152
3153 mData->mSession.mRemoteControls.clear();
3154 mData->mSession.mState = SessionState_Unlocked;
3155 }
3156 }
3157 else
3158 {
3159 /* memorize PID of the directly opened session */
3160 if (SUCCEEDED(rc))
3161 mData->mSession.mPid = pid;
3162 }
3163
3164 if (SUCCEEDED(rc))
3165 {
3166 /* memorize the direct session control and cache IUnknown for it */
3167 mData->mSession.mDirectControl = pSessionControl;
3168 mData->mSession.mState = SessionState_Locked;
3169 /* associate the SessionMachine with this Machine */
3170 mData->mSession.mMachine = sessionMachine;
3171
3172 /* request an IUnknown pointer early from the remote party for later
3173 * identity checks (it will be internally cached within mDirectControl
3174 * at least on XPCOM) */
3175 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3176 NOREF(unk);
3177 }
3178
3179 /* Leave the lock since SessionMachine::uninit() locks VirtualBox which
3180 * would break the lock order */
3181 alock.leave();
3182
3183 /* uninitialize the created session machine on failure */
3184 if (FAILED(rc))
3185 sessionMachine->uninit();
3186
3187 }
3188
3189 if (SUCCEEDED(rc))
3190 {
3191 /*
3192 * tell the client watcher thread to update the set of
3193 * machines that have open sessions
3194 */
3195 mParent->updateClientWatcher();
3196
3197 if (oldState != SessionState_Locked)
3198 /* fire an event */
3199 mParent->onSessionStateChange(getId(), SessionState_Locked);
3200 }
3201
3202 return rc;
3203}
3204
3205/**
3206 * @note Locks objects!
3207 */
3208STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3209 IN_BSTR aType,
3210 IN_BSTR aEnvironment,
3211 IProgress **aProgress)
3212{
3213 CheckComArgNotNull(aSession);
3214 CheckComArgStrNotEmptyOrNull(aType);
3215 CheckComArgOutPointerValid(aProgress);
3216
3217 AutoCaller autoCaller(this);
3218 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3219
3220 /* check the session state */
3221 SessionState_T state;
3222 HRESULT rc = aSession->COMGETTER(State)(&state);
3223 if (FAILED(rc)) return rc;
3224
3225 if (state != SessionState_Unlocked)
3226 return setError(VBOX_E_INVALID_OBJECT_STATE,
3227 tr("The given session is busy"));
3228
3229 /* get the IInternalSessionControl interface */
3230 ComPtr<IInternalSessionControl> control = aSession;
3231 ComAssertMsgRet(!!control, ("No IInternalSessionControl interface"),
3232 E_INVALIDARG);
3233
3234 /* get the teleporter enable state for the progress object init. */
3235 BOOL fTeleporterEnabled;
3236 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3237 if (FAILED(rc))
3238 return rc;
3239
3240 /* create a progress object */
3241 ComObjPtr<ProgressProxy> progress;
3242 progress.createObject();
3243 rc = progress->init(mParent,
3244 static_cast<IMachine*>(this),
3245 Bstr(tr("Spawning session")).raw(),
3246 TRUE /* aCancelable */,
3247 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3248 Bstr(tr("Spawning session")).raw(),
3249 2 /* uFirstOperationWeight */,
3250 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3251 if (SUCCEEDED(rc))
3252 {
3253 rc = openRemoteSession(control, aType, aEnvironment, progress);
3254 if (SUCCEEDED(rc))
3255 {
3256 progress.queryInterfaceTo(aProgress);
3257
3258 /* signal the client watcher thread */
3259 mParent->updateClientWatcher();
3260
3261 /* fire an event */
3262 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3263 }
3264 }
3265
3266 return rc;
3267}
3268
3269STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3270{
3271 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3272 return setError(E_INVALIDARG,
3273 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3274 aPosition, SchemaDefs::MaxBootPosition);
3275
3276 if (aDevice == DeviceType_USB)
3277 return setError(E_NOTIMPL,
3278 tr("Booting from USB device is currently not supported"));
3279
3280 AutoCaller autoCaller(this);
3281 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3282
3283 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3284
3285 HRESULT rc = checkStateDependency(MutableStateDep);
3286 if (FAILED(rc)) return rc;
3287
3288 setModified(IsModified_MachineData);
3289 mHWData.backup();
3290 mHWData->mBootOrder[aPosition - 1] = aDevice;
3291
3292 return S_OK;
3293}
3294
3295STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3296{
3297 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3298 return setError(E_INVALIDARG,
3299 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3300 aPosition, SchemaDefs::MaxBootPosition);
3301
3302 AutoCaller autoCaller(this);
3303 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3304
3305 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3306
3307 *aDevice = mHWData->mBootOrder[aPosition - 1];
3308
3309 return S_OK;
3310}
3311
3312STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
3313 LONG aControllerPort,
3314 LONG aDevice,
3315 DeviceType_T aType,
3316 IMedium *aMedium)
3317{
3318 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aId=\"%ls\"\n",
3319 aControllerName, aControllerPort, aDevice, aType, aMedium));
3320
3321 CheckComArgStrNotEmptyOrNull(aControllerName);
3322
3323 AutoCaller autoCaller(this);
3324 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3325
3326 // request the host lock first, since might be calling Host methods for getting host drives;
3327 // next, protect the media tree all the while we're in here, as well as our member variables
3328 AutoMultiWriteLock2 alock(mParent->host()->lockHandle(),
3329 this->lockHandle() COMMA_LOCKVAL_SRC_POS);
3330 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3331
3332 HRESULT rc = checkStateDependency(MutableStateDep);
3333 if (FAILED(rc)) return rc;
3334
3335 GuidList llRegistriesThatNeedSaving;
3336
3337 /// @todo NEWMEDIA implicit machine registration
3338 if (!mData->mRegistered)
3339 return setError(VBOX_E_INVALID_OBJECT_STATE,
3340 tr("Cannot attach storage devices to an unregistered machine"));
3341
3342 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3343
3344 if (Global::IsOnlineOrTransient(mData->mMachineState))
3345 return setError(VBOX_E_INVALID_VM_STATE,
3346 tr("Invalid machine state: %s"),
3347 Global::stringifyMachineState(mData->mMachineState));
3348
3349 /* Check for an existing controller. */
3350 ComObjPtr<StorageController> ctl;
3351 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
3352 if (FAILED(rc)) return rc;
3353
3354 // check that the port and device are not out of range
3355 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
3356 if (FAILED(rc)) return rc;
3357
3358 /* check if the device slot is already busy */
3359 MediumAttachment *pAttachTemp;
3360 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
3361 aControllerName,
3362 aControllerPort,
3363 aDevice)))
3364 {
3365 Medium *pMedium = pAttachTemp->getMedium();
3366 if (pMedium)
3367 {
3368 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3369 return setError(VBOX_E_OBJECT_IN_USE,
3370 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3371 pMedium->getLocationFull().c_str(),
3372 aControllerPort,
3373 aDevice,
3374 aControllerName);
3375 }
3376 else
3377 return setError(VBOX_E_OBJECT_IN_USE,
3378 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3379 aControllerPort, aDevice, aControllerName);
3380 }
3381
3382 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
3383 if (aMedium && medium.isNull())
3384 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3385
3386 AutoCaller mediumCaller(medium);
3387 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3388
3389 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3390
3391 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
3392 && !medium.isNull()
3393 )
3394 return setError(VBOX_E_OBJECT_IN_USE,
3395 tr("Medium '%s' is already attached to this virtual machine"),
3396 medium->getLocationFull().c_str());
3397
3398 bool fIndirect = false;
3399 if (!medium.isNull())
3400 fIndirect = medium->isReadOnly();
3401 bool associate = true;
3402
3403 do
3404 {
3405 if ( aType == DeviceType_HardDisk
3406 && mMediaData.isBackedUp())
3407 {
3408 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3409
3410 /* check if the medium was attached to the VM before we started
3411 * changing attachments in which case the attachment just needs to
3412 * be restored */
3413 if ((pAttachTemp = findAttachment(oldAtts, medium)))
3414 {
3415 AssertReturn(!fIndirect, E_FAIL);
3416
3417 /* see if it's the same bus/channel/device */
3418 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
3419 {
3420 /* the simplest case: restore the whole attachment
3421 * and return, nothing else to do */
3422 mMediaData->mAttachments.push_back(pAttachTemp);
3423 return S_OK;
3424 }
3425
3426 /* bus/channel/device differ; we need a new attachment object,
3427 * but don't try to associate it again */
3428 associate = false;
3429 break;
3430 }
3431 }
3432
3433 /* go further only if the attachment is to be indirect */
3434 if (!fIndirect)
3435 break;
3436
3437 /* perform the so called smart attachment logic for indirect
3438 * attachments. Note that smart attachment is only applicable to base
3439 * hard disks. */
3440
3441 if (medium->getParent().isNull())
3442 {
3443 /* first, investigate the backup copy of the current hard disk
3444 * attachments to make it possible to re-attach existing diffs to
3445 * another device slot w/o losing their contents */
3446 if (mMediaData.isBackedUp())
3447 {
3448 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3449
3450 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3451 uint32_t foundLevel = 0;
3452
3453 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
3454 it != oldAtts.end();
3455 ++it)
3456 {
3457 uint32_t level = 0;
3458 MediumAttachment *pAttach = *it;
3459 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3460 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3461 if (pMedium.isNull())
3462 continue;
3463
3464 if (pMedium->getBase(&level) == medium)
3465 {
3466 /* skip the hard disk if its currently attached (we
3467 * cannot attach the same hard disk twice) */
3468 if (findAttachment(mMediaData->mAttachments,
3469 pMedium))
3470 continue;
3471
3472 /* matched device, channel and bus (i.e. attached to the
3473 * same place) will win and immediately stop the search;
3474 * otherwise the attachment that has the youngest
3475 * descendant of medium will be used
3476 */
3477 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
3478 {
3479 /* the simplest case: restore the whole attachment
3480 * and return, nothing else to do */
3481 mMediaData->mAttachments.push_back(*it);
3482 return S_OK;
3483 }
3484 else if ( foundIt == oldAtts.end()
3485 || level > foundLevel /* prefer younger */
3486 )
3487 {
3488 foundIt = it;
3489 foundLevel = level;
3490 }
3491 }
3492 }
3493
3494 if (foundIt != oldAtts.end())
3495 {
3496 /* use the previously attached hard disk */
3497 medium = (*foundIt)->getMedium();
3498 mediumCaller.attach(medium);
3499 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3500 mediumLock.attach(medium);
3501 /* not implicit, doesn't require association with this VM */
3502 fIndirect = false;
3503 associate = false;
3504 /* go right to the MediumAttachment creation */
3505 break;
3506 }
3507 }
3508
3509 /* must give up the medium lock and medium tree lock as below we
3510 * go over snapshots, which needs a lock with higher lock order. */
3511 mediumLock.release();
3512 treeLock.release();
3513
3514 /* then, search through snapshots for the best diff in the given
3515 * hard disk's chain to base the new diff on */
3516
3517 ComObjPtr<Medium> base;
3518 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3519 while (snap)
3520 {
3521 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3522
3523 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
3524
3525 MediumAttachment *pAttachFound = NULL;
3526 uint32_t foundLevel = 0;
3527
3528 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
3529 it != snapAtts.end();
3530 ++it)
3531 {
3532 MediumAttachment *pAttach = *it;
3533 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3534 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3535 if (pMedium.isNull())
3536 continue;
3537
3538 uint32_t level = 0;
3539 if (pMedium->getBase(&level) == medium)
3540 {
3541 /* matched device, channel and bus (i.e. attached to the
3542 * same place) will win and immediately stop the search;
3543 * otherwise the attachment that has the youngest
3544 * descendant of medium will be used
3545 */
3546 if ( pAttach->getDevice() == aDevice
3547 && pAttach->getPort() == aControllerPort
3548 && pAttach->getControllerName() == aControllerName
3549 )
3550 {
3551 pAttachFound = pAttach;
3552 break;
3553 }
3554 else if ( !pAttachFound
3555 || level > foundLevel /* prefer younger */
3556 )
3557 {
3558 pAttachFound = pAttach;
3559 foundLevel = level;
3560 }
3561 }
3562 }
3563
3564 if (pAttachFound)
3565 {
3566 base = pAttachFound->getMedium();
3567 break;
3568 }
3569
3570 snap = snap->getParent();
3571 }
3572
3573 /* re-lock medium tree and the medium, as we need it below */
3574 treeLock.acquire();
3575 mediumLock.acquire();
3576
3577 /* found a suitable diff, use it as a base */
3578 if (!base.isNull())
3579 {
3580 medium = base;
3581 mediumCaller.attach(medium);
3582 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3583 mediumLock.attach(medium);
3584 }
3585 }
3586
3587 Utf8Str strFullSnapshotFolder;
3588 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3589
3590 ComObjPtr<Medium> diff;
3591 diff.createObject();
3592 rc = diff->init(mParent,
3593 medium->getPreferredDiffFormat(),
3594 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3595 medium->getFirstRegistryMachineId(), // store this diff in the same registry as the parent
3596 &llRegistriesThatNeedSaving);
3597 if (FAILED(rc)) return rc;
3598
3599 /* Apply the normal locking logic to the entire chain. */
3600 MediumLockList *pMediumLockList(new MediumLockList());
3601 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
3602 true /* fMediumLockWrite */,
3603 medium,
3604 *pMediumLockList);
3605 if (SUCCEEDED(rc))
3606 {
3607 rc = pMediumLockList->Lock();
3608 if (FAILED(rc))
3609 setError(rc,
3610 tr("Could not lock medium when creating diff '%s'"),
3611 diff->getLocationFull().c_str());
3612 else
3613 {
3614 /* will leave the lock before the potentially lengthy operation, so
3615 * protect with the special state */
3616 MachineState_T oldState = mData->mMachineState;
3617 setMachineState(MachineState_SettingUp);
3618
3619 mediumLock.leave();
3620 treeLock.leave();
3621 alock.leave();
3622
3623 rc = medium->createDiffStorage(diff,
3624 MediumVariant_Standard,
3625 pMediumLockList,
3626 NULL /* aProgress */,
3627 true /* aWait */,
3628 &llRegistriesThatNeedSaving);
3629
3630 alock.enter();
3631 treeLock.enter();
3632 mediumLock.enter();
3633
3634 setMachineState(oldState);
3635 }
3636 }
3637
3638 /* Unlock the media and free the associated memory. */
3639 delete pMediumLockList;
3640
3641 if (FAILED(rc)) return rc;
3642
3643 /* use the created diff for the actual attachment */
3644 medium = diff;
3645 mediumCaller.attach(medium);
3646 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3647 mediumLock.attach(medium);
3648 }
3649 while (0);
3650
3651 ComObjPtr<MediumAttachment> attachment;
3652 attachment.createObject();
3653 rc = attachment->init(this,
3654 medium,
3655 aControllerName,
3656 aControllerPort,
3657 aDevice,
3658 aType,
3659 fIndirect,
3660 NULL);
3661 if (FAILED(rc)) return rc;
3662
3663 if (associate && !medium.isNull())
3664 {
3665 // as the last step, associate the medium to the VM
3666 rc = medium->addBackReference(mData->mUuid);
3667 // here we can fail because of Deleting, or being in process of creating a Diff
3668 if (FAILED(rc)) return rc;
3669
3670 // and decide which medium registry to use now that the medium is attached:
3671 Guid uuid;
3672 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
3673 // machine XML is VirtualBox 4.0 or higher:
3674 uuid = getId(); // machine UUID
3675 else
3676 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
3677
3678 if (medium->addRegistry(uuid))
3679 // registry actually changed:
3680 mParent->addGuidToListUniquely(llRegistriesThatNeedSaving, uuid);
3681 }
3682
3683 /* success: finally remember the attachment */
3684 setModified(IsModified_Storage);
3685 mMediaData.backup();
3686 mMediaData->mAttachments.push_back(attachment);
3687
3688 mediumLock.release();
3689 treeLock.leave();
3690 alock.release();
3691
3692 mParent->saveRegistries(llRegistriesThatNeedSaving);
3693
3694 return rc;
3695}
3696
3697STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
3698 LONG aDevice)
3699{
3700 CheckComArgStrNotEmptyOrNull(aControllerName);
3701
3702 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n",
3703 aControllerName, aControllerPort, aDevice));
3704
3705 AutoCaller autoCaller(this);
3706 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3707
3708 GuidList llRegistriesThatNeedSaving;
3709
3710 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3711
3712 HRESULT rc = checkStateDependency(MutableStateDep);
3713 if (FAILED(rc)) return rc;
3714
3715 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3716
3717 if (Global::IsOnlineOrTransient(mData->mMachineState))
3718 return setError(VBOX_E_INVALID_VM_STATE,
3719 tr("Invalid machine state: %s"),
3720 Global::stringifyMachineState(mData->mMachineState));
3721
3722 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
3723 aControllerName,
3724 aControllerPort,
3725 aDevice);
3726 if (!pAttach)
3727 return setError(VBOX_E_OBJECT_NOT_FOUND,
3728 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
3729 aDevice, aControllerPort, aControllerName);
3730
3731 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */, &llRegistriesThatNeedSaving);
3732
3733 alock.release();
3734
3735 if (SUCCEEDED(rc))
3736 rc = mParent->saveRegistries(llRegistriesThatNeedSaving);
3737
3738 return rc;
3739}
3740
3741STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
3742 LONG aDevice, BOOL aPassthrough)
3743{
3744 CheckComArgStrNotEmptyOrNull(aControllerName);
3745
3746 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld aPassthrough=%d\n",
3747 aControllerName, aControllerPort, aDevice, aPassthrough));
3748
3749 AutoCaller autoCaller(this);
3750 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3751
3752 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3753
3754 HRESULT rc = checkStateDependency(MutableStateDep);
3755 if (FAILED(rc)) return rc;
3756
3757 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3758
3759 if (Global::IsOnlineOrTransient(mData->mMachineState))
3760 return setError(VBOX_E_INVALID_VM_STATE,
3761 tr("Invalid machine state: %s"),
3762 Global::stringifyMachineState(mData->mMachineState));
3763
3764 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
3765 aControllerName,
3766 aControllerPort,
3767 aDevice);
3768 if (!pAttach)
3769 return setError(VBOX_E_OBJECT_NOT_FOUND,
3770 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
3771 aDevice, aControllerPort, aControllerName);
3772
3773
3774 setModified(IsModified_Storage);
3775 mMediaData.backup();
3776
3777 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3778
3779 if (pAttach->getType() != DeviceType_DVD)
3780 return setError(E_INVALIDARG,
3781 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
3782 aDevice, aControllerPort, aControllerName);
3783 pAttach->updatePassthrough(!!aPassthrough);
3784
3785 return S_OK;
3786}
3787
3788STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
3789 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
3790{
3791 CheckComArgStrNotEmptyOrNull(aControllerName);
3792
3793 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n",
3794 aControllerName, aControllerPort, aDevice));
3795
3796 AutoCaller autoCaller(this);
3797 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3798
3799 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3800
3801 HRESULT rc = checkStateDependency(MutableStateDep);
3802 if (FAILED(rc)) return rc;
3803
3804 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3805
3806 if (Global::IsOnlineOrTransient(mData->mMachineState))
3807 return setError(VBOX_E_INVALID_VM_STATE,
3808 tr("Invalid machine state: %s"),
3809 Global::stringifyMachineState(mData->mMachineState));
3810
3811 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
3812 aControllerName,
3813 aControllerPort,
3814 aDevice);
3815 if (!pAttach)
3816 return setError(VBOX_E_OBJECT_NOT_FOUND,
3817 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
3818 aDevice, aControllerPort, aControllerName);
3819
3820
3821 setModified(IsModified_Storage);
3822 mMediaData.backup();
3823
3824 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
3825 if (aBandwidthGroup && group.isNull())
3826 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
3827
3828 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3829
3830 pAttach->updateBandwidthGroup(group);
3831
3832 return S_OK;
3833}
3834
3835
3836STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
3837 LONG aControllerPort,
3838 LONG aDevice,
3839 IMedium *aMedium,
3840 BOOL aForce)
3841{
3842 int rc = S_OK;
3843 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld aForce=%d\n",
3844 aControllerName, aControllerPort, aDevice, aForce));
3845
3846 CheckComArgStrNotEmptyOrNull(aControllerName);
3847
3848 AutoCaller autoCaller(this);
3849 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3850
3851 // request the host lock first, since might be calling Host methods for getting host drives;
3852 // next, protect the media tree all the while we're in here, as well as our member variables
3853 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
3854 this->lockHandle(),
3855 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3856
3857 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
3858 aControllerName,
3859 aControllerPort,
3860 aDevice);
3861 if (pAttach.isNull())
3862 return setError(VBOX_E_OBJECT_NOT_FOUND,
3863 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
3864 aDevice, aControllerPort, aControllerName);
3865
3866 /* Remember previously mounted medium. The medium before taking the
3867 * backup is not necessarily the same thing. */
3868 ComObjPtr<Medium> oldmedium;
3869 oldmedium = pAttach->getMedium();
3870
3871 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
3872 if (aMedium && pMedium.isNull())
3873 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3874
3875 AutoCaller mediumCaller(pMedium);
3876 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3877
3878 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3879 if (pMedium)
3880 {
3881 DeviceType_T mediumType = pAttach->getType();
3882 switch (mediumType)
3883 {
3884 case DeviceType_DVD:
3885 case DeviceType_Floppy:
3886 break;
3887
3888 default:
3889 return setError(VBOX_E_INVALID_OBJECT_STATE,
3890 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
3891 aControllerPort,
3892 aDevice,
3893 aControllerName);
3894 }
3895 }
3896
3897 setModified(IsModified_Storage);
3898 mMediaData.backup();
3899
3900 GuidList llRegistriesThatNeedSaving;
3901
3902 {
3903 // The backup operation makes the pAttach reference point to the
3904 // old settings. Re-get the correct reference.
3905 pAttach = findAttachment(mMediaData->mAttachments,
3906 aControllerName,
3907 aControllerPort,
3908 aDevice);
3909 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3910 if (!oldmedium.isNull())
3911 oldmedium->removeBackReference(mData->mUuid);
3912 if (!pMedium.isNull())
3913 {
3914 pMedium->addBackReference(mData->mUuid);
3915
3916 // and decide which medium registry to use now that the medium is attached:
3917 Guid uuid;
3918 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
3919 // machine XML is VirtualBox 4.0 or higher:
3920 uuid = getId(); // machine UUID
3921 else
3922 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
3923
3924 if (pMedium->addRegistry(uuid))
3925 // registry actually changed:
3926 mParent->addGuidToListUniquely(llRegistriesThatNeedSaving, uuid);
3927 }
3928
3929 pAttach->updateMedium(pMedium);
3930 }
3931
3932 setModified(IsModified_Storage);
3933
3934 mediumLock.release();
3935 multiLock.release();
3936 rc = onMediumChange(pAttach, aForce);
3937 multiLock.acquire();
3938 mediumLock.acquire();
3939
3940 /* On error roll back this change only. */
3941 if (FAILED(rc))
3942 {
3943 if (!pMedium.isNull())
3944 pMedium->removeBackReference(mData->mUuid);
3945 pAttach = findAttachment(mMediaData->mAttachments,
3946 aControllerName,
3947 aControllerPort,
3948 aDevice);
3949 /* If the attachment is gone in the meantime, bail out. */
3950 if (pAttach.isNull())
3951 return rc;
3952 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3953 if (!oldmedium.isNull())
3954 oldmedium->addBackReference(mData->mUuid);
3955 pAttach->updateMedium(oldmedium);
3956 }
3957
3958 mediumLock.release();
3959 multiLock.release();
3960
3961 mParent->saveRegistries(llRegistriesThatNeedSaving);
3962
3963 return rc;
3964}
3965
3966STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
3967 LONG aControllerPort,
3968 LONG aDevice,
3969 IMedium **aMedium)
3970{
3971 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n",
3972 aControllerName, aControllerPort, aDevice));
3973
3974 CheckComArgStrNotEmptyOrNull(aControllerName);
3975 CheckComArgOutPointerValid(aMedium);
3976
3977 AutoCaller autoCaller(this);
3978 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3979
3980 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3981
3982 *aMedium = NULL;
3983
3984 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
3985 aControllerName,
3986 aControllerPort,
3987 aDevice);
3988 if (pAttach.isNull())
3989 return setError(VBOX_E_OBJECT_NOT_FOUND,
3990 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
3991 aDevice, aControllerPort, aControllerName);
3992
3993 pAttach->getMedium().queryInterfaceTo(aMedium);
3994
3995 return S_OK;
3996}
3997
3998STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
3999{
4000 CheckComArgOutPointerValid(port);
4001 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
4002
4003 AutoCaller autoCaller(this);
4004 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4005
4006 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4007
4008 mSerialPorts[slot].queryInterfaceTo(port);
4009
4010 return S_OK;
4011}
4012
4013STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
4014{
4015 CheckComArgOutPointerValid(port);
4016 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
4017
4018 AutoCaller autoCaller(this);
4019 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4020
4021 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4022
4023 mParallelPorts[slot].queryInterfaceTo(port);
4024
4025 return S_OK;
4026}
4027
4028STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
4029{
4030 CheckComArgOutPointerValid(adapter);
4031 CheckComArgExpr(slot, slot < RT_ELEMENTS(mNetworkAdapters));
4032
4033 AutoCaller autoCaller(this);
4034 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4035
4036 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4037
4038 mNetworkAdapters[slot].queryInterfaceTo(adapter);
4039
4040 return S_OK;
4041}
4042
4043STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
4044{
4045 if (ComSafeArrayOutIsNull(aKeys))
4046 return E_POINTER;
4047
4048 AutoCaller autoCaller(this);
4049 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4050
4051 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4052
4053 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4054 int i = 0;
4055 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4056 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4057 ++it, ++i)
4058 {
4059 const Utf8Str &strKey = it->first;
4060 strKey.cloneTo(&saKeys[i]);
4061 }
4062 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
4063
4064 return S_OK;
4065 }
4066
4067 /**
4068 * @note Locks this object for reading.
4069 */
4070STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
4071 BSTR *aValue)
4072{
4073 CheckComArgStrNotEmptyOrNull(aKey);
4074 CheckComArgOutPointerValid(aValue);
4075
4076 AutoCaller autoCaller(this);
4077 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4078
4079 /* start with nothing found */
4080 Bstr bstrResult("");
4081
4082 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4083
4084 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
4085 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4086 // found:
4087 bstrResult = it->second; // source is a Utf8Str
4088
4089 /* return the result to caller (may be empty) */
4090 bstrResult.cloneTo(aValue);
4091
4092 return S_OK;
4093}
4094
4095 /**
4096 * @note Locks mParent for writing + this object for writing.
4097 */
4098STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
4099{
4100 CheckComArgStrNotEmptyOrNull(aKey);
4101
4102 AutoCaller autoCaller(this);
4103 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4104
4105 Utf8Str strKey(aKey);
4106 Utf8Str strValue(aValue);
4107 Utf8Str strOldValue; // empty
4108
4109 // locking note: we only hold the read lock briefly to look up the old value,
4110 // then release it and call the onExtraCanChange callbacks. There is a small
4111 // chance of a race insofar as the callback might be called twice if two callers
4112 // change the same key at the same time, but that's a much better solution
4113 // than the deadlock we had here before. The actual changing of the extradata
4114 // is then performed under the write lock and race-free.
4115
4116 // look up the old value first; if nothing has changed then we need not do anything
4117 {
4118 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4119 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
4120 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4121 strOldValue = it->second;
4122 }
4123
4124 bool fChanged;
4125 if ((fChanged = (strOldValue != strValue)))
4126 {
4127 // ask for permission from all listeners outside the locks;
4128 // onExtraDataCanChange() only briefly requests the VirtualBox
4129 // lock to copy the list of callbacks to invoke
4130 Bstr error;
4131 Bstr bstrValue(aValue);
4132
4133 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
4134 {
4135 const char *sep = error.isEmpty() ? "" : ": ";
4136 CBSTR err = error.raw();
4137 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4138 sep, err));
4139 return setError(E_ACCESSDENIED,
4140 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
4141 aKey,
4142 bstrValue.raw(),
4143 sep,
4144 err);
4145 }
4146
4147 // data is changing and change not vetoed: then write it out under the lock
4148 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4149
4150 if (isSnapshotMachine())
4151 {
4152 HRESULT rc = checkStateDependency(MutableStateDep);
4153 if (FAILED(rc)) return rc;
4154 }
4155
4156 if (strValue.isEmpty())
4157 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
4158 else
4159 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
4160 // creates a new key if needed
4161
4162 bool fNeedsGlobalSaveSettings = false;
4163 saveSettings(&fNeedsGlobalSaveSettings);
4164
4165 if (fNeedsGlobalSaveSettings)
4166 {
4167 // save the global settings; for that we should hold only the VirtualBox lock
4168 alock.release();
4169 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4170 mParent->saveSettings();
4171 }
4172 }
4173
4174 // fire notification outside the lock
4175 if (fChanged)
4176 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
4177
4178 return S_OK;
4179}
4180
4181STDMETHODIMP Machine::SaveSettings()
4182{
4183 AutoCaller autoCaller(this);
4184 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4185
4186 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4187
4188 /* when there was auto-conversion, we want to save the file even if
4189 * the VM is saved */
4190 HRESULT rc = checkStateDependency(MutableStateDep);
4191 if (FAILED(rc)) return rc;
4192
4193 /* the settings file path may never be null */
4194 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4195
4196 /* save all VM data excluding snapshots */
4197 bool fNeedsGlobalSaveSettings = false;
4198 rc = saveSettings(&fNeedsGlobalSaveSettings);
4199 mlock.release();
4200
4201 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4202 {
4203 // save the global settings; for that we should hold only the VirtualBox lock
4204 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4205 rc = mParent->saveSettings();
4206 }
4207
4208 return rc;
4209}
4210
4211STDMETHODIMP Machine::DiscardSettings()
4212{
4213 AutoCaller autoCaller(this);
4214 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4215
4216 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4217
4218 HRESULT rc = checkStateDependency(MutableStateDep);
4219 if (FAILED(rc)) return rc;
4220
4221 /*
4222 * during this rollback, the session will be notified if data has
4223 * been actually changed
4224 */
4225 rollback(true /* aNotify */);
4226
4227 return S_OK;
4228}
4229
4230/** @note Locks objects! */
4231STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
4232 ComSafeArrayOut(IMedium*, aMedia))
4233{
4234 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
4235 AutoLimitedCaller autoCaller(this);
4236 AssertComRCReturnRC(autoCaller.rc());
4237
4238 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4239
4240 Guid id(getId());
4241
4242 if (mData->mSession.mState != SessionState_Unlocked)
4243 return setError(VBOX_E_INVALID_OBJECT_STATE,
4244 tr("Cannot unregister the machine '%s' while it is locked"),
4245 mUserData->s.strName.c_str());
4246
4247 // wait for state dependents to drop to zero
4248 ensureNoStateDependencies();
4249
4250 if (!mData->mAccessible)
4251 {
4252 // inaccessible maschines can only be unregistered; uninitialize ourselves
4253 // here because currently there may be no unregistered that are inaccessible
4254 // (this state combination is not supported). Note releasing the caller and
4255 // leaving the lock before calling uninit()
4256 alock.leave();
4257 autoCaller.release();
4258
4259 uninit();
4260
4261 mParent->unregisterMachine(this, id);
4262 // calls VirtualBox::saveSettings()
4263
4264 return S_OK;
4265 }
4266
4267 HRESULT rc = S_OK;
4268
4269 // discard saved state
4270 if (mData->mMachineState == MachineState_Saved)
4271 {
4272 // add the saved state file to the list of files the caller should delete
4273 Assert(!mSSData->mStateFilePath.isEmpty());
4274 mData->llFilesToDelete.push_back(mSSData->mStateFilePath);
4275
4276 mSSData->mStateFilePath.setNull();
4277
4278 // unconditionally set the machine state to powered off, we now
4279 // know no session has locked the machine
4280 mData->mMachineState = MachineState_PoweredOff;
4281 }
4282
4283 size_t cSnapshots = 0;
4284 if (mData->mFirstSnapshot)
4285 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
4286 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
4287 // fail now before we start detaching media
4288 return setError(VBOX_E_INVALID_OBJECT_STATE,
4289 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
4290 mUserData->s.strName.c_str(), cSnapshots);
4291
4292 // this list collects the medium objects from all medium attachments
4293 // which got detached from the machine and its snapshots, in the following
4294 // order:
4295 // 1) media from machine attachments (these have the "leaf" attachments with snapshots
4296 // and must be closed first, or closing the parents will fail because they will
4297 // children);
4298 // 2) media from the youngest snapshots followed those from the parent snapshots until
4299 // the root ("first") snapshot of the machine
4300 // This order allows for closing the media on this list from the beginning to the end
4301 // without getting "media in use" errors.
4302 MediaList llMedia;
4303
4304 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
4305 && mMediaData->mAttachments.size()
4306 )
4307 {
4308 // we have media attachments: detach them all and add the Medium objects to our list
4309 if (cleanupMode != CleanupMode_UnregisterOnly)
4310 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
4311 else
4312 return setError(VBOX_E_INVALID_OBJECT_STATE,
4313 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
4314 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
4315 }
4316
4317 if (cSnapshots)
4318 {
4319 // autoCleanup must be true here, or we would have failed above
4320
4321 // add the media from the medium attachments of the snapshots to llMedia
4322 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4323 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4324 // into the children first
4325
4326 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4327 MachineState_T oldState = mData->mMachineState;
4328 mData->mMachineState = MachineState_DeletingSnapshot;
4329
4330 // make a copy of the first snapshot so the refcount does not drop to 0
4331 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
4332 // because of the AutoCaller voodoo)
4333 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4334
4335 // GO!
4336 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
4337
4338 mData->mMachineState = oldState;
4339 }
4340
4341 if (FAILED(rc))
4342 {
4343 rollbackMedia();
4344 return rc;
4345 }
4346
4347 // commit all the media changes made above
4348 commitMedia();
4349
4350 mData->mRegistered = false;
4351
4352 // machine lock no longer needed
4353 alock.release();
4354
4355 // return media to caller
4356 SafeIfaceArray<IMedium> sfaMedia(llMedia);
4357 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
4358
4359 mParent->unregisterMachine(this, id);
4360 // calls VirtualBox::saveSettings()
4361
4362 return S_OK;
4363}
4364
4365struct Machine::DeleteTask
4366{
4367 ComObjPtr<Machine> pMachine;
4368 std::list<Utf8Str> llFilesToDelete;
4369 ComObjPtr<Progress> pProgress;
4370 GuidList llRegistriesThatNeedSaving;
4371};
4372
4373STDMETHODIMP Machine::Delete(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
4374{
4375 LogFlowFuncEnter();
4376
4377 AutoCaller autoCaller(this);
4378 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4379
4380 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4381
4382 HRESULT rc = checkStateDependency(MutableStateDep);
4383 if (FAILED(rc)) return rc;
4384
4385 if (mData->mRegistered)
4386 return setError(VBOX_E_INVALID_VM_STATE,
4387 tr("Cannot delete settings of a registered machine"));
4388
4389 DeleteTask *pTask = new DeleteTask;
4390 pTask->pMachine = this;
4391 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
4392
4393 // collect files to delete
4394 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
4395
4396 for (size_t i = 0; i < sfaMedia.size(); ++i)
4397 {
4398 IMedium *pIMedium(sfaMedia[i]);
4399 Medium *pMedium = static_cast<Medium*>(pIMedium);
4400 AutoCaller mediumAutoCaller(pMedium);
4401 if (FAILED(mediumAutoCaller.rc())) return mediumAutoCaller.rc();
4402
4403 Utf8Str bstrLocation = pMedium->getLocationFull();
4404 // close the medium now; if that succeeds, then that means the medium is no longer
4405 // in use and we can add it to the list of files to delete
4406 rc = pMedium->close(&pTask->llRegistriesThatNeedSaving,
4407 mediumAutoCaller);
4408 if (SUCCEEDED(rc))
4409 pTask->llFilesToDelete.push_back(bstrLocation);
4410 }
4411 if (mData->pMachineConfigFile->fileExists())
4412 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
4413
4414 pTask->pProgress.createObject();
4415 pTask->pProgress->init(getVirtualBox(),
4416 static_cast<IMachine*>(this) /* aInitiator */,
4417 Bstr(tr("Deleting files")).raw(),
4418 true /* fCancellable */,
4419 pTask->llFilesToDelete.size() + 1, // cOperations
4420 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
4421
4422 int vrc = RTThreadCreate(NULL,
4423 Machine::deleteThread,
4424 (void*)pTask,
4425 0,
4426 RTTHREADTYPE_MAIN_WORKER,
4427 0,
4428 "MachineDelete");
4429
4430 pTask->pProgress.queryInterfaceTo(aProgress);
4431
4432 if (RT_FAILURE(vrc))
4433 {
4434 delete pTask;
4435 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
4436 }
4437
4438 LogFlowFuncLeave();
4439
4440 return S_OK;
4441}
4442
4443/**
4444 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
4445 * calls Machine::deleteTaskWorker() on the actual machine object.
4446 * @param Thread
4447 * @param pvUser
4448 * @return
4449 */
4450/*static*/
4451DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
4452{
4453 LogFlowFuncEnter();
4454
4455 DeleteTask *pTask = (DeleteTask*)pvUser;
4456 Assert(pTask);
4457 Assert(pTask->pMachine);
4458 Assert(pTask->pProgress);
4459
4460 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
4461 pTask->pProgress->notifyComplete(rc);
4462
4463 delete pTask;
4464
4465 LogFlowFuncLeave();
4466
4467 NOREF(Thread);
4468
4469 return VINF_SUCCESS;
4470}
4471
4472/**
4473 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
4474 * @param task
4475 * @return
4476 */
4477HRESULT Machine::deleteTaskWorker(DeleteTask &task)
4478{
4479 AutoCaller autoCaller(this);
4480 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4481
4482 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4483
4484 ULONG uLogHistoryCount = 3;
4485 ComPtr<ISystemProperties> systemProperties;
4486 mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
4487 if (!systemProperties.isNull())
4488 systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
4489
4490 // delete the files pushed on the task list by Machine::Delete()
4491 // (this includes saved states of the machine and snapshots and
4492 // medium storage files from the IMedium list passed in, and the
4493 // machine XML file)
4494 std::list<Utf8Str>::const_iterator it = task.llFilesToDelete.begin();
4495 while (it != task.llFilesToDelete.end())
4496 {
4497 const Utf8Str &strFile = *it;
4498 LogFunc(("Deleting file %s\n", strFile.c_str()));
4499 RTFileDelete(strFile.c_str());
4500
4501 ++it;
4502 if (it == task.llFilesToDelete.end())
4503 {
4504 task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
4505 break;
4506 }
4507
4508 task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
4509 }
4510
4511 /* delete the settings only when the file actually exists */
4512 if (mData->pMachineConfigFile->fileExists())
4513 {
4514 /* Delete any backup or uncommitted XML files. Ignore failures.
4515 See the fSafe parameter of xml::XmlFileWriter::write for details. */
4516 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
4517 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
4518 RTFileDelete(otherXml.c_str());
4519 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
4520 RTFileDelete(otherXml.c_str());
4521
4522 /* delete the Logs folder, nothing important should be left
4523 * there (we don't check for errors because the user might have
4524 * some private files there that we don't want to delete) */
4525 Utf8Str logFolder;
4526 getLogFolder(logFolder);
4527 Assert(logFolder.length());
4528 if (RTDirExists(logFolder.c_str()))
4529 {
4530 /* Delete all VBox.log[.N] files from the Logs folder
4531 * (this must be in sync with the rotation logic in
4532 * Console::powerUpThread()). Also, delete the VBox.png[.N]
4533 * files that may have been created by the GUI. */
4534 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
4535 logFolder.c_str(), RTPATH_DELIMITER);
4536 RTFileDelete(log.c_str());
4537 log = Utf8StrFmt("%s%cVBox.png",
4538 logFolder.c_str(), RTPATH_DELIMITER);
4539 RTFileDelete(log.c_str());
4540 for (int i = uLogHistoryCount; i > 0; i--)
4541 {
4542 log = Utf8StrFmt("%s%cVBox.log.%d",
4543 logFolder.c_str(), RTPATH_DELIMITER, i);
4544 RTFileDelete(log.c_str());
4545 log = Utf8StrFmt("%s%cVBox.png.%d",
4546 logFolder.c_str(), RTPATH_DELIMITER, i);
4547 RTFileDelete(log.c_str());
4548 }
4549
4550 RTDirRemove(logFolder.c_str());
4551 }
4552
4553 /* delete the Snapshots folder, nothing important should be left
4554 * there (we don't check for errors because the user might have
4555 * some private files there that we don't want to delete) */
4556 Utf8Str strFullSnapshotFolder;
4557 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4558 Assert(!strFullSnapshotFolder.isEmpty());
4559 if (RTDirExists(strFullSnapshotFolder.c_str()))
4560 RTDirRemove(strFullSnapshotFolder.c_str());
4561
4562 // delete the directory that contains the settings file, but only
4563 // if it matches the VM name
4564 Utf8Str settingsDir;
4565 if (isInOwnDir(&settingsDir))
4566 RTDirRemove(settingsDir.c_str());
4567 }
4568
4569 alock.release();
4570
4571 mParent->saveRegistries(task.llRegistriesThatNeedSaving);
4572
4573 return S_OK;
4574}
4575
4576STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
4577{
4578 CheckComArgOutPointerValid(aSnapshot);
4579
4580 AutoCaller autoCaller(this);
4581 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4582
4583 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4584
4585 ComObjPtr<Snapshot> pSnapshot;
4586 HRESULT rc;
4587
4588 if (!aNameOrId || !*aNameOrId)
4589 // null case (caller wants root snapshot): findSnapshotById() handles this
4590 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
4591 else
4592 {
4593 Guid uuid(aNameOrId);
4594 if (!uuid.isEmpty())
4595 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
4596 else
4597 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
4598 }
4599 pSnapshot.queryInterfaceTo(aSnapshot);
4600
4601 return rc;
4602}
4603
4604STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
4605{
4606 CheckComArgStrNotEmptyOrNull(aName);
4607 CheckComArgStrNotEmptyOrNull(aHostPath);
4608
4609 AutoCaller autoCaller(this);
4610 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4611
4612 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4613
4614 HRESULT rc = checkStateDependency(MutableStateDep);
4615 if (FAILED(rc)) return rc;
4616
4617 ComObjPtr<SharedFolder> sharedFolder;
4618 rc = findSharedFolder(aName, sharedFolder, false /* aSetError */);
4619 if (SUCCEEDED(rc))
4620 return setError(VBOX_E_OBJECT_IN_USE,
4621 tr("Shared folder named '%ls' already exists"),
4622 aName);
4623
4624 sharedFolder.createObject();
4625 rc = sharedFolder->init(getMachine(), aName, aHostPath, aWritable, aAutoMount);
4626 if (FAILED(rc)) return rc;
4627
4628 setModified(IsModified_SharedFolders);
4629 mHWData.backup();
4630 mHWData->mSharedFolders.push_back(sharedFolder);
4631
4632 /* inform the direct session if any */
4633 alock.leave();
4634 onSharedFolderChange();
4635
4636 return S_OK;
4637}
4638
4639STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
4640{
4641 CheckComArgStrNotEmptyOrNull(aName);
4642
4643 AutoCaller autoCaller(this);
4644 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4645
4646 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4647
4648 HRESULT rc = checkStateDependency(MutableStateDep);
4649 if (FAILED(rc)) return rc;
4650
4651 ComObjPtr<SharedFolder> sharedFolder;
4652 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
4653 if (FAILED(rc)) return rc;
4654
4655 setModified(IsModified_SharedFolders);
4656 mHWData.backup();
4657 mHWData->mSharedFolders.remove(sharedFolder);
4658
4659 /* inform the direct session if any */
4660 alock.leave();
4661 onSharedFolderChange();
4662
4663 return S_OK;
4664}
4665
4666STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
4667{
4668 CheckComArgOutPointerValid(aCanShow);
4669
4670 /* start with No */
4671 *aCanShow = FALSE;
4672
4673 AutoCaller autoCaller(this);
4674 AssertComRCReturnRC(autoCaller.rc());
4675
4676 ComPtr<IInternalSessionControl> directControl;
4677 {
4678 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4679
4680 if (mData->mSession.mState != SessionState_Locked)
4681 return setError(VBOX_E_INVALID_VM_STATE,
4682 tr("Machine is not locked for session (session state: %s)"),
4683 Global::stringifySessionState(mData->mSession.mState));
4684
4685 directControl = mData->mSession.mDirectControl;
4686 }
4687
4688 /* ignore calls made after #OnSessionEnd() is called */
4689 if (!directControl)
4690 return S_OK;
4691
4692 LONG64 dummy;
4693 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
4694}
4695
4696STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
4697{
4698 CheckComArgOutPointerValid(aWinId);
4699
4700 AutoCaller autoCaller(this);
4701 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4702
4703 ComPtr<IInternalSessionControl> directControl;
4704 {
4705 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4706
4707 if (mData->mSession.mState != SessionState_Locked)
4708 return setError(E_FAIL,
4709 tr("Machine is not locked for session (session state: %s)"),
4710 Global::stringifySessionState(mData->mSession.mState));
4711
4712 directControl = mData->mSession.mDirectControl;
4713 }
4714
4715 /* ignore calls made after #OnSessionEnd() is called */
4716 if (!directControl)
4717 return S_OK;
4718
4719 BOOL dummy;
4720 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
4721}
4722
4723#ifdef VBOX_WITH_GUEST_PROPS
4724/**
4725 * Look up a guest property in VBoxSVC's internal structures.
4726 */
4727HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
4728 BSTR *aValue,
4729 LONG64 *aTimestamp,
4730 BSTR *aFlags) const
4731{
4732 using namespace guestProp;
4733
4734 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4735 Utf8Str strName(aName);
4736 HWData::GuestPropertyList::const_iterator it;
4737
4738 for (it = mHWData->mGuestProperties.begin();
4739 it != mHWData->mGuestProperties.end(); ++it)
4740 {
4741 if (it->strName == strName)
4742 {
4743 char szFlags[MAX_FLAGS_LEN + 1];
4744 it->strValue.cloneTo(aValue);
4745 *aTimestamp = it->mTimestamp;
4746 writeFlags(it->mFlags, szFlags);
4747 Bstr(szFlags).cloneTo(aFlags);
4748 break;
4749 }
4750 }
4751 return S_OK;
4752}
4753
4754/**
4755 * Query the VM that a guest property belongs to for the property.
4756 * @returns E_ACCESSDENIED if the VM process is not available or not
4757 * currently handling queries and the lookup should then be done in
4758 * VBoxSVC.
4759 */
4760HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
4761 BSTR *aValue,
4762 LONG64 *aTimestamp,
4763 BSTR *aFlags) const
4764{
4765 HRESULT rc;
4766 ComPtr<IInternalSessionControl> directControl;
4767 directControl = mData->mSession.mDirectControl;
4768
4769 /* fail if we were called after #OnSessionEnd() is called. This is a
4770 * silly race condition. */
4771
4772 if (!directControl)
4773 rc = E_ACCESSDENIED;
4774 else
4775 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
4776 false /* isSetter */,
4777 aValue, aTimestamp, aFlags);
4778 return rc;
4779}
4780#endif // VBOX_WITH_GUEST_PROPS
4781
4782STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
4783 BSTR *aValue,
4784 LONG64 *aTimestamp,
4785 BSTR *aFlags)
4786{
4787#ifndef VBOX_WITH_GUEST_PROPS
4788 ReturnComNotImplemented();
4789#else // VBOX_WITH_GUEST_PROPS
4790 CheckComArgStrNotEmptyOrNull(aName);
4791 CheckComArgOutPointerValid(aValue);
4792 CheckComArgOutPointerValid(aTimestamp);
4793 CheckComArgOutPointerValid(aFlags);
4794
4795 AutoCaller autoCaller(this);
4796 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4797
4798 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
4799 if (rc == E_ACCESSDENIED)
4800 /* The VM is not running or the service is not (yet) accessible */
4801 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
4802 return rc;
4803#endif // VBOX_WITH_GUEST_PROPS
4804}
4805
4806STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
4807{
4808 LONG64 dummyTimestamp;
4809 Bstr dummyFlags;
4810 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
4811}
4812
4813STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
4814{
4815 Bstr dummyValue;
4816 Bstr dummyFlags;
4817 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
4818}
4819
4820#ifdef VBOX_WITH_GUEST_PROPS
4821/**
4822 * Set a guest property in VBoxSVC's internal structures.
4823 */
4824HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
4825 IN_BSTR aFlags)
4826{
4827 using namespace guestProp;
4828
4829 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4830 HRESULT rc = S_OK;
4831 HWData::GuestProperty property;
4832 property.mFlags = NILFLAG;
4833 bool found = false;
4834
4835 rc = checkStateDependency(MutableStateDep);
4836 if (FAILED(rc)) return rc;
4837
4838 try
4839 {
4840 Utf8Str utf8Name(aName);
4841 Utf8Str utf8Flags(aFlags);
4842 uint32_t fFlags = NILFLAG;
4843 if ( (aFlags != NULL)
4844 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags))
4845 )
4846 return setError(E_INVALIDARG,
4847 tr("Invalid flag values: '%ls'"),
4848 aFlags);
4849
4850 /** @todo r=bird: see efficiency rant in PushGuestProperty. (Yeah, I
4851 * know, this is simple and do an OK job atm.) */
4852 HWData::GuestPropertyList::iterator it;
4853 for (it = mHWData->mGuestProperties.begin();
4854 it != mHWData->mGuestProperties.end(); ++it)
4855 if (it->strName == utf8Name)
4856 {
4857 property = *it;
4858 if (it->mFlags & (RDONLYHOST))
4859 rc = setError(E_ACCESSDENIED,
4860 tr("The property '%ls' cannot be changed by the host"),
4861 aName);
4862 else
4863 {
4864 setModified(IsModified_MachineData);
4865 mHWData.backup(); // @todo r=dj backup in a loop?!?
4866
4867 /* The backup() operation invalidates our iterator, so
4868 * get a new one. */
4869 for (it = mHWData->mGuestProperties.begin();
4870 it->strName != utf8Name;
4871 ++it)
4872 ;
4873 mHWData->mGuestProperties.erase(it);
4874 }
4875 found = true;
4876 break;
4877 }
4878 if (found && SUCCEEDED(rc))
4879 {
4880 if (*aValue)
4881 {
4882 RTTIMESPEC time;
4883 property.strValue = aValue;
4884 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
4885 if (aFlags != NULL)
4886 property.mFlags = fFlags;
4887 mHWData->mGuestProperties.push_back(property);
4888 }
4889 }
4890 else if (SUCCEEDED(rc) && *aValue)
4891 {
4892 RTTIMESPEC time;
4893 setModified(IsModified_MachineData);
4894 mHWData.backup();
4895 property.strName = aName;
4896 property.strValue = aValue;
4897 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
4898 property.mFlags = fFlags;
4899 mHWData->mGuestProperties.push_back(property);
4900 }
4901 if ( SUCCEEDED(rc)
4902 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
4903 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
4904 RTSTR_MAX,
4905 utf8Name.c_str(),
4906 RTSTR_MAX,
4907 NULL)
4908 )
4909 )
4910 {
4911 /** @todo r=bird: Why aren't we leaving the lock here? The
4912 * same code in PushGuestProperty does... */
4913 mParent->onGuestPropertyChange(mData->mUuid, aName, aValue, aFlags);
4914 }
4915 }
4916 catch (std::bad_alloc &)
4917 {
4918 rc = E_OUTOFMEMORY;
4919 }
4920
4921 return rc;
4922}
4923
4924/**
4925 * Set a property on the VM that that property belongs to.
4926 * @returns E_ACCESSDENIED if the VM process is not available or not
4927 * currently handling queries and the setting should then be done in
4928 * VBoxSVC.
4929 */
4930HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
4931 IN_BSTR aFlags)
4932{
4933 HRESULT rc;
4934
4935 try {
4936 ComPtr<IInternalSessionControl> directControl =
4937 mData->mSession.mDirectControl;
4938
4939 BSTR dummy = NULL; /* will not be changed (setter) */
4940 LONG64 dummy64;
4941 if (!directControl)
4942 rc = E_ACCESSDENIED;
4943 else
4944 rc = directControl->AccessGuestProperty
4945 (aName,
4946 /** @todo Fix when adding DeleteGuestProperty(),
4947 see defect. */
4948 *aValue ? aValue : NULL, aFlags, true /* isSetter */,
4949 &dummy, &dummy64, &dummy);
4950 }
4951 catch (std::bad_alloc &)
4952 {
4953 rc = E_OUTOFMEMORY;
4954 }
4955
4956 return rc;
4957}
4958#endif // VBOX_WITH_GUEST_PROPS
4959
4960STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
4961 IN_BSTR aFlags)
4962{
4963#ifndef VBOX_WITH_GUEST_PROPS
4964 ReturnComNotImplemented();
4965#else // VBOX_WITH_GUEST_PROPS
4966 CheckComArgStrNotEmptyOrNull(aName);
4967 if ((aFlags != NULL) && !VALID_PTR(aFlags))
4968 return E_INVALIDARG;
4969 AutoCaller autoCaller(this);
4970 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4971
4972 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
4973 if (rc == E_ACCESSDENIED)
4974 /* The VM is not running or the service is not (yet) accessible */
4975 rc = setGuestPropertyToService(aName, aValue, aFlags);
4976 return rc;
4977#endif // VBOX_WITH_GUEST_PROPS
4978}
4979
4980STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
4981{
4982 return SetGuestProperty(aName, aValue, NULL);
4983}
4984
4985#ifdef VBOX_WITH_GUEST_PROPS
4986/**
4987 * Enumerate the guest properties in VBoxSVC's internal structures.
4988 */
4989HRESULT Machine::enumerateGuestPropertiesInService
4990 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
4991 ComSafeArrayOut(BSTR, aValues),
4992 ComSafeArrayOut(LONG64, aTimestamps),
4993 ComSafeArrayOut(BSTR, aFlags))
4994{
4995 using namespace guestProp;
4996
4997 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4998 Utf8Str strPatterns(aPatterns);
4999
5000 /*
5001 * Look for matching patterns and build up a list.
5002 */
5003 HWData::GuestPropertyList propList;
5004 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
5005 it != mHWData->mGuestProperties.end();
5006 ++it)
5007 if ( strPatterns.isEmpty()
5008 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5009 RTSTR_MAX,
5010 it->strName.c_str(),
5011 RTSTR_MAX,
5012 NULL)
5013 )
5014 propList.push_back(*it);
5015
5016 /*
5017 * And build up the arrays for returning the property information.
5018 */
5019 size_t cEntries = propList.size();
5020 SafeArray<BSTR> names(cEntries);
5021 SafeArray<BSTR> values(cEntries);
5022 SafeArray<LONG64> timestamps(cEntries);
5023 SafeArray<BSTR> flags(cEntries);
5024 size_t iProp = 0;
5025 for (HWData::GuestPropertyList::iterator it = propList.begin();
5026 it != propList.end();
5027 ++it)
5028 {
5029 char szFlags[MAX_FLAGS_LEN + 1];
5030 it->strName.cloneTo(&names[iProp]);
5031 it->strValue.cloneTo(&values[iProp]);
5032 timestamps[iProp] = it->mTimestamp;
5033 writeFlags(it->mFlags, szFlags);
5034 Bstr(szFlags).cloneTo(&flags[iProp]);
5035 ++iProp;
5036 }
5037 names.detachTo(ComSafeArrayOutArg(aNames));
5038 values.detachTo(ComSafeArrayOutArg(aValues));
5039 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
5040 flags.detachTo(ComSafeArrayOutArg(aFlags));
5041 return S_OK;
5042}
5043
5044/**
5045 * Enumerate the properties managed by a VM.
5046 * @returns E_ACCESSDENIED if the VM process is not available or not
5047 * currently handling queries and the setting should then be done in
5048 * VBoxSVC.
5049 */
5050HRESULT Machine::enumerateGuestPropertiesOnVM
5051 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5052 ComSafeArrayOut(BSTR, aValues),
5053 ComSafeArrayOut(LONG64, aTimestamps),
5054 ComSafeArrayOut(BSTR, aFlags))
5055{
5056 HRESULT rc;
5057 ComPtr<IInternalSessionControl> directControl;
5058 directControl = mData->mSession.mDirectControl;
5059
5060 if (!directControl)
5061 rc = E_ACCESSDENIED;
5062 else
5063 rc = directControl->EnumerateGuestProperties
5064 (aPatterns, ComSafeArrayOutArg(aNames),
5065 ComSafeArrayOutArg(aValues),
5066 ComSafeArrayOutArg(aTimestamps),
5067 ComSafeArrayOutArg(aFlags));
5068 return rc;
5069}
5070#endif // VBOX_WITH_GUEST_PROPS
5071
5072STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
5073 ComSafeArrayOut(BSTR, aNames),
5074 ComSafeArrayOut(BSTR, aValues),
5075 ComSafeArrayOut(LONG64, aTimestamps),
5076 ComSafeArrayOut(BSTR, aFlags))
5077{
5078#ifndef VBOX_WITH_GUEST_PROPS
5079 ReturnComNotImplemented();
5080#else // VBOX_WITH_GUEST_PROPS
5081 if (!VALID_PTR(aPatterns) && (aPatterns != NULL))
5082 return E_POINTER;
5083
5084 CheckComArgOutSafeArrayPointerValid(aNames);
5085 CheckComArgOutSafeArrayPointerValid(aValues);
5086 CheckComArgOutSafeArrayPointerValid(aTimestamps);
5087 CheckComArgOutSafeArrayPointerValid(aFlags);
5088
5089 AutoCaller autoCaller(this);
5090 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5091
5092 HRESULT rc = enumerateGuestPropertiesOnVM
5093 (aPatterns, ComSafeArrayOutArg(aNames),
5094 ComSafeArrayOutArg(aValues),
5095 ComSafeArrayOutArg(aTimestamps),
5096 ComSafeArrayOutArg(aFlags));
5097 if (rc == E_ACCESSDENIED)
5098 /* The VM is not running or the service is not (yet) accessible */
5099 rc = enumerateGuestPropertiesInService
5100 (aPatterns, ComSafeArrayOutArg(aNames),
5101 ComSafeArrayOutArg(aValues),
5102 ComSafeArrayOutArg(aTimestamps),
5103 ComSafeArrayOutArg(aFlags));
5104 return rc;
5105#endif // VBOX_WITH_GUEST_PROPS
5106}
5107
5108STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
5109 ComSafeArrayOut(IMediumAttachment*, aAttachments))
5110{
5111 MediaData::AttachmentList atts;
5112
5113 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
5114 if (FAILED(rc)) return rc;
5115
5116 SafeIfaceArray<IMediumAttachment> attachments(atts);
5117 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
5118
5119 return S_OK;
5120}
5121
5122STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
5123 LONG aControllerPort,
5124 LONG aDevice,
5125 IMediumAttachment **aAttachment)
5126{
5127 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5128 aControllerName, aControllerPort, aDevice));
5129
5130 CheckComArgStrNotEmptyOrNull(aControllerName);
5131 CheckComArgOutPointerValid(aAttachment);
5132
5133 AutoCaller autoCaller(this);
5134 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5135
5136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5137
5138 *aAttachment = NULL;
5139
5140 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5141 aControllerName,
5142 aControllerPort,
5143 aDevice);
5144 if (pAttach.isNull())
5145 return setError(VBOX_E_OBJECT_NOT_FOUND,
5146 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5147 aDevice, aControllerPort, aControllerName);
5148
5149 pAttach.queryInterfaceTo(aAttachment);
5150
5151 return S_OK;
5152}
5153
5154STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
5155 StorageBus_T aConnectionType,
5156 IStorageController **controller)
5157{
5158 CheckComArgStrNotEmptyOrNull(aName);
5159
5160 if ( (aConnectionType <= StorageBus_Null)
5161 || (aConnectionType > StorageBus_SAS))
5162 return setError(E_INVALIDARG,
5163 tr("Invalid connection type: %d"),
5164 aConnectionType);
5165
5166 AutoCaller autoCaller(this);
5167 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5168
5169 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5170
5171 HRESULT rc = checkStateDependency(MutableStateDep);
5172 if (FAILED(rc)) return rc;
5173
5174 /* try to find one with the name first. */
5175 ComObjPtr<StorageController> ctrl;
5176
5177 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
5178 if (SUCCEEDED(rc))
5179 return setError(VBOX_E_OBJECT_IN_USE,
5180 tr("Storage controller named '%ls' already exists"),
5181 aName);
5182
5183 ctrl.createObject();
5184
5185 /* get a new instance number for the storage controller */
5186 ULONG ulInstance = 0;
5187 bool fBootable = true;
5188 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5189 it != mStorageControllers->end();
5190 ++it)
5191 {
5192 if ((*it)->getStorageBus() == aConnectionType)
5193 {
5194 ULONG ulCurInst = (*it)->getInstance();
5195
5196 if (ulCurInst >= ulInstance)
5197 ulInstance = ulCurInst + 1;
5198
5199 /* Only one controller of each type can be marked as bootable. */
5200 if ((*it)->getBootable())
5201 fBootable = false;
5202 }
5203 }
5204
5205 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5206 if (FAILED(rc)) return rc;
5207
5208 setModified(IsModified_Storage);
5209 mStorageControllers.backup();
5210 mStorageControllers->push_back(ctrl);
5211
5212 ctrl.queryInterfaceTo(controller);
5213
5214 /* inform the direct session if any */
5215 alock.leave();
5216 onStorageControllerChange();
5217
5218 return S_OK;
5219}
5220
5221STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
5222 IStorageController **aStorageController)
5223{
5224 CheckComArgStrNotEmptyOrNull(aName);
5225
5226 AutoCaller autoCaller(this);
5227 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5228
5229 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5230
5231 ComObjPtr<StorageController> ctrl;
5232
5233 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5234 if (SUCCEEDED(rc))
5235 ctrl.queryInterfaceTo(aStorageController);
5236
5237 return rc;
5238}
5239
5240STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
5241 IStorageController **aStorageController)
5242{
5243 AutoCaller autoCaller(this);
5244 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5245
5246 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5247
5248 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5249 it != mStorageControllers->end();
5250 ++it)
5251 {
5252 if ((*it)->getInstance() == aInstance)
5253 {
5254 (*it).queryInterfaceTo(aStorageController);
5255 return S_OK;
5256 }
5257 }
5258
5259 return setError(VBOX_E_OBJECT_NOT_FOUND,
5260 tr("Could not find a storage controller with instance number '%lu'"),
5261 aInstance);
5262}
5263
5264STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
5265{
5266 AutoCaller autoCaller(this);
5267 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5268
5269 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5270
5271 HRESULT rc = checkStateDependency(MutableStateDep);
5272 if (FAILED(rc)) return rc;
5273
5274 ComObjPtr<StorageController> ctrl;
5275
5276 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5277 if (SUCCEEDED(rc))
5278 {
5279 /* Ensure that only one controller of each type is marked as bootable. */
5280 if (fBootable == TRUE)
5281 {
5282 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5283 it != mStorageControllers->end();
5284 ++it)
5285 {
5286 ComObjPtr<StorageController> aCtrl = (*it);
5287
5288 if ( (aCtrl->getName() != Utf8Str(aName))
5289 && aCtrl->getBootable() == TRUE
5290 && aCtrl->getStorageBus() == ctrl->getStorageBus()
5291 && aCtrl->getControllerType() == ctrl->getControllerType())
5292 {
5293 aCtrl->setBootable(FALSE);
5294 break;
5295 }
5296 }
5297 }
5298
5299 if (SUCCEEDED(rc))
5300 {
5301 ctrl->setBootable(fBootable);
5302 setModified(IsModified_Storage);
5303 }
5304 }
5305
5306 if (SUCCEEDED(rc))
5307 {
5308 /* inform the direct session if any */
5309 alock.leave();
5310 onStorageControllerChange();
5311 }
5312
5313 return rc;
5314}
5315
5316STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
5317{
5318 CheckComArgStrNotEmptyOrNull(aName);
5319
5320 AutoCaller autoCaller(this);
5321 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5322
5323 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5324
5325 HRESULT rc = checkStateDependency(MutableStateDep);
5326 if (FAILED(rc)) return rc;
5327
5328 ComObjPtr<StorageController> ctrl;
5329 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5330 if (FAILED(rc)) return rc;
5331
5332 /* We can remove the controller only if there is no device attached. */
5333 /* check if the device slot is already busy */
5334 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
5335 it != mMediaData->mAttachments.end();
5336 ++it)
5337 {
5338 if ((*it)->getControllerName() == aName)
5339 return setError(VBOX_E_OBJECT_IN_USE,
5340 tr("Storage controller named '%ls' has still devices attached"),
5341 aName);
5342 }
5343
5344 /* We can remove it now. */
5345 setModified(IsModified_Storage);
5346 mStorageControllers.backup();
5347
5348 ctrl->unshare();
5349
5350 mStorageControllers->remove(ctrl);
5351
5352 /* inform the direct session if any */
5353 alock.leave();
5354 onStorageControllerChange();
5355
5356 return S_OK;
5357}
5358
5359STDMETHODIMP Machine::QuerySavedGuestSize(ULONG uScreenId, ULONG *puWidth, ULONG *puHeight)
5360{
5361 LogFlowThisFunc(("\n"));
5362
5363 CheckComArgNotNull(puWidth);
5364 CheckComArgNotNull(puHeight);
5365
5366 uint32_t u32Width = 0;
5367 uint32_t u32Height = 0;
5368
5369 int vrc = readSavedGuestSize(mSSData->mStateFilePath, uScreenId, &u32Width, &u32Height);
5370 if (RT_FAILURE(vrc))
5371 return setError(VBOX_E_IPRT_ERROR,
5372 tr("Saved guest size is not available (%Rrc)"),
5373 vrc);
5374
5375 *puWidth = u32Width;
5376 *puHeight = u32Height;
5377
5378 return S_OK;
5379}
5380
5381STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
5382{
5383 LogFlowThisFunc(("\n"));
5384
5385 CheckComArgNotNull(aSize);
5386 CheckComArgNotNull(aWidth);
5387 CheckComArgNotNull(aHeight);
5388
5389 if (aScreenId != 0)
5390 return E_NOTIMPL;
5391
5392 AutoCaller autoCaller(this);
5393 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5394
5395 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5396
5397 uint8_t *pu8Data = NULL;
5398 uint32_t cbData = 0;
5399 uint32_t u32Width = 0;
5400 uint32_t u32Height = 0;
5401
5402 int vrc = readSavedDisplayScreenshot(mSSData->mStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
5403
5404 if (RT_FAILURE(vrc))
5405 return setError(VBOX_E_IPRT_ERROR,
5406 tr("Saved screenshot data is not available (%Rrc)"),
5407 vrc);
5408
5409 *aSize = cbData;
5410 *aWidth = u32Width;
5411 *aHeight = u32Height;
5412
5413 freeSavedDisplayScreenshot(pu8Data);
5414
5415 return S_OK;
5416}
5417
5418STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
5419{
5420 LogFlowThisFunc(("\n"));
5421
5422 CheckComArgNotNull(aWidth);
5423 CheckComArgNotNull(aHeight);
5424 CheckComArgOutSafeArrayPointerValid(aData);
5425
5426 if (aScreenId != 0)
5427 return E_NOTIMPL;
5428
5429 AutoCaller autoCaller(this);
5430 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5431
5432 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5433
5434 uint8_t *pu8Data = NULL;
5435 uint32_t cbData = 0;
5436 uint32_t u32Width = 0;
5437 uint32_t u32Height = 0;
5438
5439 int vrc = readSavedDisplayScreenshot(mSSData->mStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
5440
5441 if (RT_FAILURE(vrc))
5442 return setError(VBOX_E_IPRT_ERROR,
5443 tr("Saved screenshot data is not available (%Rrc)"),
5444 vrc);
5445
5446 *aWidth = u32Width;
5447 *aHeight = u32Height;
5448
5449 com::SafeArray<BYTE> bitmap(cbData);
5450 /* Convert pixels to format expected by the API caller. */
5451 if (aBGR)
5452 {
5453 /* [0] B, [1] G, [2] R, [3] A. */
5454 for (unsigned i = 0; i < cbData; i += 4)
5455 {
5456 bitmap[i] = pu8Data[i];
5457 bitmap[i + 1] = pu8Data[i + 1];
5458 bitmap[i + 2] = pu8Data[i + 2];
5459 bitmap[i + 3] = 0xff;
5460 }
5461 }
5462 else
5463 {
5464 /* [0] R, [1] G, [2] B, [3] A. */
5465 for (unsigned i = 0; i < cbData; i += 4)
5466 {
5467 bitmap[i] = pu8Data[i + 2];
5468 bitmap[i + 1] = pu8Data[i + 1];
5469 bitmap[i + 2] = pu8Data[i];
5470 bitmap[i + 3] = 0xff;
5471 }
5472 }
5473 bitmap.detachTo(ComSafeArrayOutArg(aData));
5474
5475 freeSavedDisplayScreenshot(pu8Data);
5476
5477 return S_OK;
5478}
5479
5480
5481STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
5482{
5483 LogFlowThisFunc(("\n"));
5484
5485 CheckComArgNotNull(aWidth);
5486 CheckComArgNotNull(aHeight);
5487 CheckComArgOutSafeArrayPointerValid(aData);
5488
5489 if (aScreenId != 0)
5490 return E_NOTIMPL;
5491
5492 AutoCaller autoCaller(this);
5493 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5494
5495 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5496
5497 uint8_t *pu8Data = NULL;
5498 uint32_t cbData = 0;
5499 uint32_t u32Width = 0;
5500 uint32_t u32Height = 0;
5501
5502 int vrc = readSavedDisplayScreenshot(mSSData->mStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
5503
5504 if (RT_FAILURE(vrc))
5505 return setError(VBOX_E_IPRT_ERROR,
5506 tr("Saved screenshot data is not available (%Rrc)"),
5507 vrc);
5508
5509 *aWidth = u32Width;
5510 *aHeight = u32Height;
5511
5512 uint8_t *pu8PNG = NULL;
5513 uint32_t cbPNG = 0;
5514 uint32_t cxPNG = 0;
5515 uint32_t cyPNG = 0;
5516
5517 DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
5518
5519 com::SafeArray<BYTE> screenData(cbPNG);
5520 screenData.initFrom(pu8PNG, cbPNG);
5521 RTMemFree(pu8PNG);
5522
5523 screenData.detachTo(ComSafeArrayOutArg(aData));
5524
5525 freeSavedDisplayScreenshot(pu8Data);
5526
5527 return S_OK;
5528}
5529
5530STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
5531{
5532 LogFlowThisFunc(("\n"));
5533
5534 CheckComArgNotNull(aSize);
5535 CheckComArgNotNull(aWidth);
5536 CheckComArgNotNull(aHeight);
5537
5538 if (aScreenId != 0)
5539 return E_NOTIMPL;
5540
5541 AutoCaller autoCaller(this);
5542 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5543
5544 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5545
5546 uint8_t *pu8Data = NULL;
5547 uint32_t cbData = 0;
5548 uint32_t u32Width = 0;
5549 uint32_t u32Height = 0;
5550
5551 int vrc = readSavedDisplayScreenshot(mSSData->mStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
5552
5553 if (RT_FAILURE(vrc))
5554 return setError(VBOX_E_IPRT_ERROR,
5555 tr("Saved screenshot data is not available (%Rrc)"),
5556 vrc);
5557
5558 *aSize = cbData;
5559 *aWidth = u32Width;
5560 *aHeight = u32Height;
5561
5562 freeSavedDisplayScreenshot(pu8Data);
5563
5564 return S_OK;
5565}
5566
5567STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
5568{
5569 LogFlowThisFunc(("\n"));
5570
5571 CheckComArgNotNull(aWidth);
5572 CheckComArgNotNull(aHeight);
5573 CheckComArgOutSafeArrayPointerValid(aData);
5574
5575 if (aScreenId != 0)
5576 return E_NOTIMPL;
5577
5578 AutoCaller autoCaller(this);
5579 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5580
5581 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5582
5583 uint8_t *pu8Data = NULL;
5584 uint32_t cbData = 0;
5585 uint32_t u32Width = 0;
5586 uint32_t u32Height = 0;
5587
5588 int vrc = readSavedDisplayScreenshot(mSSData->mStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
5589
5590 if (RT_FAILURE(vrc))
5591 return setError(VBOX_E_IPRT_ERROR,
5592 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
5593 vrc);
5594
5595 *aWidth = u32Width;
5596 *aHeight = u32Height;
5597
5598 com::SafeArray<BYTE> png(cbData);
5599 png.initFrom(pu8Data, cbData);
5600 png.detachTo(ComSafeArrayOutArg(aData));
5601
5602 freeSavedDisplayScreenshot(pu8Data);
5603
5604 return S_OK;
5605}
5606
5607STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
5608{
5609 HRESULT rc = S_OK;
5610 LogFlowThisFunc(("\n"));
5611
5612 AutoCaller autoCaller(this);
5613 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5614
5615 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5616
5617 if (!mHWData->mCPUHotPlugEnabled)
5618 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
5619
5620 if (aCpu >= mHWData->mCPUCount)
5621 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
5622
5623 if (mHWData->mCPUAttached[aCpu])
5624 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
5625
5626 alock.release();
5627 rc = onCPUChange(aCpu, false);
5628 alock.acquire();
5629 if (FAILED(rc)) return rc;
5630
5631 setModified(IsModified_MachineData);
5632 mHWData.backup();
5633 mHWData->mCPUAttached[aCpu] = true;
5634
5635 /* Save settings if online */
5636 if (Global::IsOnline(mData->mMachineState))
5637 saveSettings(NULL);
5638
5639 return S_OK;
5640}
5641
5642STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
5643{
5644 HRESULT rc = S_OK;
5645 LogFlowThisFunc(("\n"));
5646
5647 AutoCaller autoCaller(this);
5648 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5649
5650 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5651
5652 if (!mHWData->mCPUHotPlugEnabled)
5653 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
5654
5655 if (aCpu >= SchemaDefs::MaxCPUCount)
5656 return setError(E_INVALIDARG,
5657 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
5658 SchemaDefs::MaxCPUCount);
5659
5660 if (!mHWData->mCPUAttached[aCpu])
5661 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
5662
5663 /* CPU 0 can't be detached */
5664 if (aCpu == 0)
5665 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
5666
5667 alock.release();
5668 rc = onCPUChange(aCpu, true);
5669 alock.acquire();
5670 if (FAILED(rc)) return rc;
5671
5672 setModified(IsModified_MachineData);
5673 mHWData.backup();
5674 mHWData->mCPUAttached[aCpu] = false;
5675
5676 /* Save settings if online */
5677 if (Global::IsOnline(mData->mMachineState))
5678 saveSettings(NULL);
5679
5680 return S_OK;
5681}
5682
5683STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
5684{
5685 LogFlowThisFunc(("\n"));
5686
5687 CheckComArgNotNull(aCpuAttached);
5688
5689 *aCpuAttached = false;
5690
5691 AutoCaller autoCaller(this);
5692 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5693
5694 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5695
5696 /* If hotplug is enabled the CPU is always enabled. */
5697 if (!mHWData->mCPUHotPlugEnabled)
5698 {
5699 if (aCpu < mHWData->mCPUCount)
5700 *aCpuAttached = true;
5701 }
5702 else
5703 {
5704 if (aCpu < SchemaDefs::MaxCPUCount)
5705 *aCpuAttached = mHWData->mCPUAttached[aCpu];
5706 }
5707
5708 return S_OK;
5709}
5710
5711STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
5712{
5713 CheckComArgOutPointerValid(aName);
5714
5715 AutoCaller autoCaller(this);
5716 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5717
5718 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5719
5720 Utf8Str log = queryLogFilename(aIdx);
5721 if (!RTFileExists(log.c_str()))
5722 log.setNull();
5723 log.cloneTo(aName);
5724
5725 return S_OK;
5726}
5727
5728STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
5729{
5730 LogFlowThisFunc(("\n"));
5731 CheckComArgOutSafeArrayPointerValid(aData);
5732 if (aSize < 0)
5733 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
5734
5735 AutoCaller autoCaller(this);
5736 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5737
5738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5739
5740 HRESULT rc = S_OK;
5741 Utf8Str log = queryLogFilename(aIdx);
5742
5743 /* do not unnecessarily hold the lock while doing something which does
5744 * not need the lock and potentially takes a long time. */
5745 alock.release();
5746
5747 /* Limit the chunk size to 32K for now, as that gives better performance
5748 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
5749 * One byte expands to approx. 25 bytes of breathtaking XML. */
5750 size_t cbData = (size_t)RT_MIN(aSize, 32768);
5751 com::SafeArray<BYTE> logData(cbData);
5752
5753 RTFILE LogFile;
5754 int vrc = RTFileOpen(&LogFile, log.c_str(),
5755 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
5756 if (RT_SUCCESS(vrc))
5757 {
5758 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
5759 if (RT_SUCCESS(vrc))
5760 logData.resize(cbData);
5761 else
5762 rc = setError(VBOX_E_IPRT_ERROR,
5763 tr("Could not read log file '%s' (%Rrc)"),
5764 log.c_str(), vrc);
5765 RTFileClose(LogFile);
5766 }
5767 else
5768 rc = setError(VBOX_E_IPRT_ERROR,
5769 tr("Could not open log file '%s' (%Rrc)"),
5770 log.c_str(), vrc);
5771
5772 if (FAILED(rc))
5773 logData.resize(0);
5774 logData.detachTo(ComSafeArrayOutArg(aData));
5775
5776 return rc;
5777}
5778
5779
5780STDMETHODIMP Machine::AttachHostPciDevice(LONG hostAddress, LONG desiredGuestAddress, IEventContext * /*eventContext*/, BOOL /*tryToUnbind*/)
5781{
5782 AutoCaller autoCaller(this);
5783 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5784 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5785
5786 ComObjPtr<PciDeviceAttachment> pda;
5787 char name[32];
5788
5789 pda.createObject();
5790 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
5791 Bstr bname(name);
5792 pda.createObject();
5793 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
5794
5795 mPciDeviceAssignments.push_back(pda);
5796 return S_OK;
5797}
5798
5799STDMETHODIMP Machine::DetachHostPciDevice(LONG /*hostAddress*/)
5800{
5801 AutoCaller autoCaller(this);
5802 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5803 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5804
5805 return E_NOTIMPL;
5806}
5807
5808STDMETHODIMP Machine::COMGETTER(PciDeviceAssignments)(ComSafeArrayOut(IPciDeviceAttachment *, aAssignments))
5809{
5810 CheckComArgOutSafeArrayPointerValid(aAssignments);
5811
5812 AutoCaller autoCaller(this);
5813 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5814
5815 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5816
5817 SafeIfaceArray<IPciDeviceAttachment> assignments(mPciDeviceAssignments);
5818 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
5819
5820 return S_OK;
5821}
5822
5823STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
5824{
5825 CheckComArgOutPointerValid(aBandwidthControl);
5826
5827 AutoCaller autoCaller(this);
5828 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5829
5830 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
5831
5832 return S_OK;
5833}
5834
5835// public methods for internal purposes
5836/////////////////////////////////////////////////////////////////////////////
5837
5838/**
5839 * Adds the given IsModified_* flag to the dirty flags of the machine.
5840 * This must be called either during loadSettings or under the machine write lock.
5841 * @param fl
5842 */
5843void Machine::setModified(uint32_t fl)
5844{
5845 mData->flModifications |= fl;
5846}
5847
5848/**
5849 * Saves the registry entry of this machine to the given configuration node.
5850 *
5851 * @param aEntryNode Node to save the registry entry to.
5852 *
5853 * @note locks this object for reading.
5854 */
5855HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
5856{
5857 AutoLimitedCaller autoCaller(this);
5858 AssertComRCReturnRC(autoCaller.rc());
5859
5860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5861
5862 data.uuid = mData->mUuid;
5863 data.strSettingsFile = mData->m_strConfigFile;
5864
5865 return S_OK;
5866}
5867
5868/**
5869 * Calculates the absolute path of the given path taking the directory of the
5870 * machine settings file as the current directory.
5871 *
5872 * @param aPath Path to calculate the absolute path for.
5873 * @param aResult Where to put the result (used only on success, can be the
5874 * same Utf8Str instance as passed in @a aPath).
5875 * @return IPRT result.
5876 *
5877 * @note Locks this object for reading.
5878 */
5879int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
5880{
5881 AutoCaller autoCaller(this);
5882 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5883
5884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5885
5886 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
5887
5888 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
5889
5890 strSettingsDir.stripFilename();
5891 char folder[RTPATH_MAX];
5892 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
5893 if (RT_SUCCESS(vrc))
5894 aResult = folder;
5895
5896 return vrc;
5897}
5898
5899/**
5900 * Copies strSource to strTarget, making it relative to the machine folder
5901 * if it is a subdirectory thereof, or simply copying it otherwise.
5902 *
5903 * @param strSource Path to evaluate and copy.
5904 * @param strTarget Buffer to receive target path.
5905 *
5906 * @note Locks this object for reading.
5907 */
5908void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
5909 Utf8Str &strTarget)
5910{
5911 AutoCaller autoCaller(this);
5912 AssertComRCReturn(autoCaller.rc(), (void)0);
5913
5914 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5915
5916 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
5917 // use strTarget as a temporary buffer to hold the machine settings dir
5918 strTarget = mData->m_strConfigFileFull;
5919 strTarget.stripFilename();
5920 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
5921 // is relative: then append what's left
5922 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
5923 else
5924 // is not relative: then overwrite
5925 strTarget = strSource;
5926}
5927
5928/**
5929 * Returns the full path to the machine's log folder in the
5930 * \a aLogFolder argument.
5931 */
5932void Machine::getLogFolder(Utf8Str &aLogFolder)
5933{
5934 AutoCaller autoCaller(this);
5935 AssertComRCReturnVoid(autoCaller.rc());
5936
5937 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5938
5939 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
5940 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
5941 aLogFolder.append(RTPATH_DELIMITER);
5942 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
5943}
5944
5945/**
5946 * Returns the full path to the machine's log file for an given index.
5947 */
5948Utf8Str Machine::queryLogFilename(ULONG idx)
5949{
5950 Utf8Str logFolder;
5951 getLogFolder(logFolder);
5952 Assert(logFolder.length());
5953 Utf8Str log;
5954 if (idx == 0)
5955 log = Utf8StrFmt("%s%cVBox.log",
5956 logFolder.c_str(), RTPATH_DELIMITER);
5957 else
5958 log = Utf8StrFmt("%s%cVBox.log.%d",
5959 logFolder.c_str(), RTPATH_DELIMITER, idx);
5960 return log;
5961}
5962
5963/**
5964 * @note Locks this object for writing, calls the client process
5965 * (inside the lock).
5966 */
5967HRESULT Machine::openRemoteSession(IInternalSessionControl *aControl,
5968 IN_BSTR aType,
5969 IN_BSTR aEnvironment,
5970 ProgressProxy *aProgress)
5971{
5972 LogFlowThisFuncEnter();
5973
5974 AssertReturn(aControl, E_FAIL);
5975 AssertReturn(aProgress, E_FAIL);
5976
5977 AutoCaller autoCaller(this);
5978 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5979
5980 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5981
5982 if (!mData->mRegistered)
5983 return setError(E_UNEXPECTED,
5984 tr("The machine '%s' is not registered"),
5985 mUserData->s.strName.c_str());
5986
5987 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
5988
5989 if ( mData->mSession.mState == SessionState_Locked
5990 || mData->mSession.mState == SessionState_Spawning
5991 || mData->mSession.mState == SessionState_Unlocking)
5992 return setError(VBOX_E_INVALID_OBJECT_STATE,
5993 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
5994 mUserData->s.strName.c_str());
5995
5996 /* may not be busy */
5997 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
5998
5999 /* get the path to the executable */
6000 char szPath[RTPATH_MAX];
6001 RTPathAppPrivateArch(szPath, RTPATH_MAX);
6002 size_t sz = strlen(szPath);
6003 szPath[sz++] = RTPATH_DELIMITER;
6004 szPath[sz] = 0;
6005 char *cmd = szPath + sz;
6006 sz = RTPATH_MAX - sz;
6007
6008 int vrc = VINF_SUCCESS;
6009 RTPROCESS pid = NIL_RTPROCESS;
6010
6011 RTENV env = RTENV_DEFAULT;
6012
6013 if (aEnvironment != NULL && *aEnvironment)
6014 {
6015 char *newEnvStr = NULL;
6016
6017 do
6018 {
6019 /* clone the current environment */
6020 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
6021 AssertRCBreakStmt(vrc2, vrc = vrc2);
6022
6023 newEnvStr = RTStrDup(Utf8Str(aEnvironment).c_str());
6024 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
6025
6026 /* put new variables to the environment
6027 * (ignore empty variable names here since RTEnv API
6028 * intentionally doesn't do that) */
6029 char *var = newEnvStr;
6030 for (char *p = newEnvStr; *p; ++p)
6031 {
6032 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
6033 {
6034 *p = '\0';
6035 if (*var)
6036 {
6037 char *val = strchr(var, '=');
6038 if (val)
6039 {
6040 *val++ = '\0';
6041 vrc2 = RTEnvSetEx(env, var, val);
6042 }
6043 else
6044 vrc2 = RTEnvUnsetEx(env, var);
6045 if (RT_FAILURE(vrc2))
6046 break;
6047 }
6048 var = p + 1;
6049 }
6050 }
6051 if (RT_SUCCESS(vrc2) && *var)
6052 vrc2 = RTEnvPutEx(env, var);
6053
6054 AssertRCBreakStmt(vrc2, vrc = vrc2);
6055 }
6056 while (0);
6057
6058 if (newEnvStr != NULL)
6059 RTStrFree(newEnvStr);
6060 }
6061
6062 Utf8Str strType(aType);
6063
6064 /* Qt is default */
6065#ifdef VBOX_WITH_QTGUI
6066 if (strType == "gui" || strType == "GUI/Qt")
6067 {
6068# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
6069 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
6070# else
6071 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
6072# endif
6073 Assert(sz >= sizeof(VirtualBox_exe));
6074 strcpy(cmd, VirtualBox_exe);
6075
6076 Utf8Str idStr = mData->mUuid.toString();
6077 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
6078 vrc = RTProcCreate(szPath, args, env, 0, &pid);
6079 }
6080#else /* !VBOX_WITH_QTGUI */
6081 if (0)
6082 ;
6083#endif /* VBOX_WITH_QTGUI */
6084
6085 else
6086
6087#ifdef VBOX_WITH_VBOXSDL
6088 if (strType == "sdl" || strType == "GUI/SDL")
6089 {
6090 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
6091 Assert(sz >= sizeof(VBoxSDL_exe));
6092 strcpy(cmd, VBoxSDL_exe);
6093
6094 Utf8Str idStr = mData->mUuid.toString();
6095 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
6096 vrc = RTProcCreate(szPath, args, env, 0, &pid);
6097 }
6098#else /* !VBOX_WITH_VBOXSDL */
6099 if (0)
6100 ;
6101#endif /* !VBOX_WITH_VBOXSDL */
6102
6103 else
6104
6105#ifdef VBOX_WITH_HEADLESS
6106 if ( strType == "headless"
6107 || strType == "capture"
6108 || strType == "vrdp" /* Deprecated. Same as headless. */
6109 )
6110 {
6111 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
6112 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
6113 * and a VM works even if the server has not been installed.
6114 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
6115 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
6116 * differently in 4.0 and 3.x.
6117 */
6118 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
6119 Assert(sz >= sizeof(VBoxHeadless_exe));
6120 strcpy(cmd, VBoxHeadless_exe);
6121
6122 Utf8Str idStr = mData->mUuid.toString();
6123 /* Leave space for "--capture" arg. */
6124 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0, 0 };
6125 if (strType == "capture")
6126 {
6127 unsigned pos = RT_ELEMENTS(args) - 2;
6128 args[pos] = "--capture";
6129 }
6130 vrc = RTProcCreate(szPath, args, env, 0, &pid);
6131 }
6132#else /* !VBOX_WITH_HEADLESS */
6133 if (0)
6134 ;
6135#endif /* !VBOX_WITH_HEADLESS */
6136 else
6137 {
6138 RTEnvDestroy(env);
6139 return setError(E_INVALIDARG,
6140 tr("Invalid session type: '%s'"),
6141 strType.c_str());
6142 }
6143
6144 RTEnvDestroy(env);
6145
6146 if (RT_FAILURE(vrc))
6147 return setError(VBOX_E_IPRT_ERROR,
6148 tr("Could not launch a process for the machine '%s' (%Rrc)"),
6149 mUserData->s.strName.c_str(), vrc);
6150
6151 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
6152
6153 /*
6154 * Note that we don't leave the lock here before calling the client,
6155 * because it doesn't need to call us back if called with a NULL argument.
6156 * Leaving the lock here is dangerous because we didn't prepare the
6157 * launch data yet, but the client we've just started may happen to be
6158 * too fast and call openSession() that will fail (because of PID, etc.),
6159 * so that the Machine will never get out of the Spawning session state.
6160 */
6161
6162 /* inform the session that it will be a remote one */
6163 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
6164 HRESULT rc = aControl->AssignMachine(NULL);
6165 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
6166
6167 if (FAILED(rc))
6168 {
6169 /* restore the session state */
6170 mData->mSession.mState = SessionState_Unlocked;
6171 /* The failure may occur w/o any error info (from RPC), so provide one */
6172 return setError(VBOX_E_VM_ERROR,
6173 tr("Failed to assign the machine to the session (%Rrc)"), rc);
6174 }
6175
6176 /* attach launch data to the machine */
6177 Assert(mData->mSession.mPid == NIL_RTPROCESS);
6178 mData->mSession.mRemoteControls.push_back (aControl);
6179 mData->mSession.mProgress = aProgress;
6180 mData->mSession.mPid = pid;
6181 mData->mSession.mState = SessionState_Spawning;
6182 mData->mSession.mType = strType;
6183
6184 LogFlowThisFuncLeave();
6185 return S_OK;
6186}
6187
6188/**
6189 * Returns @c true if the given machine has an open direct session and returns
6190 * the session machine instance and additional session data (on some platforms)
6191 * if so.
6192 *
6193 * Note that when the method returns @c false, the arguments remain unchanged.
6194 *
6195 * @param aMachine Session machine object.
6196 * @param aControl Direct session control object (optional).
6197 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
6198 *
6199 * @note locks this object for reading.
6200 */
6201#if defined(RT_OS_WINDOWS)
6202bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
6203 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
6204 HANDLE *aIPCSem /*= NULL*/,
6205 bool aAllowClosing /*= false*/)
6206#elif defined(RT_OS_OS2)
6207bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
6208 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
6209 HMTX *aIPCSem /*= NULL*/,
6210 bool aAllowClosing /*= false*/)
6211#else
6212bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
6213 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
6214 bool aAllowClosing /*= false*/)
6215#endif
6216{
6217 AutoLimitedCaller autoCaller(this);
6218 AssertComRCReturn(autoCaller.rc(), false);
6219
6220 /* just return false for inaccessible machines */
6221 if (autoCaller.state() != Ready)
6222 return false;
6223
6224 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6225
6226 if ( mData->mSession.mState == SessionState_Locked
6227 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
6228 )
6229 {
6230 AssertReturn(!mData->mSession.mMachine.isNull(), false);
6231
6232 aMachine = mData->mSession.mMachine;
6233
6234 if (aControl != NULL)
6235 *aControl = mData->mSession.mDirectControl;
6236
6237#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
6238 /* Additional session data */
6239 if (aIPCSem != NULL)
6240 *aIPCSem = aMachine->mIPCSem;
6241#endif
6242 return true;
6243 }
6244
6245 return false;
6246}
6247
6248/**
6249 * Returns @c true if the given machine has an spawning direct session and
6250 * returns and additional session data (on some platforms) if so.
6251 *
6252 * Note that when the method returns @c false, the arguments remain unchanged.
6253 *
6254 * @param aPID PID of the spawned direct session process.
6255 *
6256 * @note locks this object for reading.
6257 */
6258#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
6259bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
6260#else
6261bool Machine::isSessionSpawning()
6262#endif
6263{
6264 AutoLimitedCaller autoCaller(this);
6265 AssertComRCReturn(autoCaller.rc(), false);
6266
6267 /* just return false for inaccessible machines */
6268 if (autoCaller.state() != Ready)
6269 return false;
6270
6271 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6272
6273 if (mData->mSession.mState == SessionState_Spawning)
6274 {
6275#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
6276 /* Additional session data */
6277 if (aPID != NULL)
6278 {
6279 AssertReturn(mData->mSession.mPid != NIL_RTPROCESS, false);
6280 *aPID = mData->mSession.mPid;
6281 }
6282#endif
6283 return true;
6284 }
6285
6286 return false;
6287}
6288
6289/**
6290 * Called from the client watcher thread to check for unexpected client process
6291 * death during Session_Spawning state (e.g. before it successfully opened a
6292 * direct session).
6293 *
6294 * On Win32 and on OS/2, this method is called only when we've got the
6295 * direct client's process termination notification, so it always returns @c
6296 * true.
6297 *
6298 * On other platforms, this method returns @c true if the client process is
6299 * terminated and @c false if it's still alive.
6300 *
6301 * @note Locks this object for writing.
6302 */
6303bool Machine::checkForSpawnFailure()
6304{
6305 AutoCaller autoCaller(this);
6306 if (!autoCaller.isOk())
6307 {
6308 /* nothing to do */
6309 LogFlowThisFunc(("Already uninitialized!\n"));
6310 return true;
6311 }
6312
6313 /* VirtualBox::addProcessToReap() needs a write lock */
6314 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
6315
6316 if (mData->mSession.mState != SessionState_Spawning)
6317 {
6318 /* nothing to do */
6319 LogFlowThisFunc(("Not spawning any more!\n"));
6320 return true;
6321 }
6322
6323 HRESULT rc = S_OK;
6324
6325#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
6326
6327 /* the process was already unexpectedly terminated, we just need to set an
6328 * error and finalize session spawning */
6329 rc = setError(E_FAIL,
6330 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
6331 getName().c_str());
6332#else
6333
6334 /* PID not yet initialized, skip check. */
6335 if (mData->mSession.mPid == NIL_RTPROCESS)
6336 return false;
6337
6338 RTPROCSTATUS status;
6339 int vrc = ::RTProcWait(mData->mSession.mPid, RTPROCWAIT_FLAGS_NOBLOCK,
6340 &status);
6341
6342 if (vrc != VERR_PROCESS_RUNNING)
6343 {
6344 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
6345 rc = setError(E_FAIL,
6346 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
6347 getName().c_str(), status.iStatus);
6348 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
6349 rc = setError(E_FAIL,
6350 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
6351 getName().c_str(), status.iStatus);
6352 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
6353 rc = setError(E_FAIL,
6354 tr("The virtual machine '%s' has terminated abnormally"),
6355 getName().c_str(), status.iStatus);
6356 else
6357 rc = setError(E_FAIL,
6358 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
6359 getName().c_str(), rc);
6360 }
6361
6362#endif
6363
6364 if (FAILED(rc))
6365 {
6366 /* Close the remote session, remove the remote control from the list
6367 * and reset session state to Closed (@note keep the code in sync with
6368 * the relevant part in checkForSpawnFailure()). */
6369
6370 Assert(mData->mSession.mRemoteControls.size() == 1);
6371 if (mData->mSession.mRemoteControls.size() == 1)
6372 {
6373 ErrorInfoKeeper eik;
6374 mData->mSession.mRemoteControls.front()->Uninitialize();
6375 }
6376
6377 mData->mSession.mRemoteControls.clear();
6378 mData->mSession.mState = SessionState_Unlocked;
6379
6380 /* finalize the progress after setting the state */
6381 if (!mData->mSession.mProgress.isNull())
6382 {
6383 mData->mSession.mProgress->notifyComplete(rc);
6384 mData->mSession.mProgress.setNull();
6385 }
6386
6387 mParent->addProcessToReap(mData->mSession.mPid);
6388 mData->mSession.mPid = NIL_RTPROCESS;
6389
6390 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
6391 return true;
6392 }
6393
6394 return false;
6395}
6396
6397/**
6398 * Checks whether the machine can be registered. If so, commits and saves
6399 * all settings.
6400 *
6401 * @note Must be called from mParent's write lock. Locks this object and
6402 * children for writing.
6403 */
6404HRESULT Machine::prepareRegister()
6405{
6406 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
6407
6408 AutoLimitedCaller autoCaller(this);
6409 AssertComRCReturnRC(autoCaller.rc());
6410
6411 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6412
6413 /* wait for state dependents to drop to zero */
6414 ensureNoStateDependencies();
6415
6416 if (!mData->mAccessible)
6417 return setError(VBOX_E_INVALID_OBJECT_STATE,
6418 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
6419 mUserData->s.strName.c_str(),
6420 mData->mUuid.toString().c_str());
6421
6422 AssertReturn(autoCaller.state() == Ready, E_FAIL);
6423
6424 if (mData->mRegistered)
6425 return setError(VBOX_E_INVALID_OBJECT_STATE,
6426 tr("The machine '%s' with UUID {%s} is already registered"),
6427 mUserData->s.strName.c_str(),
6428 mData->mUuid.toString().c_str());
6429
6430 HRESULT rc = S_OK;
6431
6432 // Ensure the settings are saved. If we are going to be registered and
6433 // no config file exists yet, create it by calling saveSettings() too.
6434 if ( (mData->flModifications)
6435 || (!mData->pMachineConfigFile->fileExists())
6436 )
6437 {
6438 rc = saveSettings(NULL);
6439 // no need to check whether VirtualBox.xml needs saving too since
6440 // we can't have a machine XML file rename pending
6441 if (FAILED(rc)) return rc;
6442 }
6443
6444 /* more config checking goes here */
6445
6446 if (SUCCEEDED(rc))
6447 {
6448 /* we may have had implicit modifications we want to fix on success */
6449 commit();
6450
6451 mData->mRegistered = true;
6452 }
6453 else
6454 {
6455 /* we may have had implicit modifications we want to cancel on failure*/
6456 rollback(false /* aNotify */);
6457 }
6458
6459 return rc;
6460}
6461
6462/**
6463 * Increases the number of objects dependent on the machine state or on the
6464 * registered state. Guarantees that these two states will not change at least
6465 * until #releaseStateDependency() is called.
6466 *
6467 * Depending on the @a aDepType value, additional state checks may be made.
6468 * These checks will set extended error info on failure. See
6469 * #checkStateDependency() for more info.
6470 *
6471 * If this method returns a failure, the dependency is not added and the caller
6472 * is not allowed to rely on any particular machine state or registration state
6473 * value and may return the failed result code to the upper level.
6474 *
6475 * @param aDepType Dependency type to add.
6476 * @param aState Current machine state (NULL if not interested).
6477 * @param aRegistered Current registered state (NULL if not interested).
6478 *
6479 * @note Locks this object for writing.
6480 */
6481HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
6482 MachineState_T *aState /* = NULL */,
6483 BOOL *aRegistered /* = NULL */)
6484{
6485 AutoCaller autoCaller(this);
6486 AssertComRCReturnRC(autoCaller.rc());
6487
6488 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6489
6490 HRESULT rc = checkStateDependency(aDepType);
6491 if (FAILED(rc)) return rc;
6492
6493 {
6494 if (mData->mMachineStateChangePending != 0)
6495 {
6496 /* ensureNoStateDependencies() is waiting for state dependencies to
6497 * drop to zero so don't add more. It may make sense to wait a bit
6498 * and retry before reporting an error (since the pending state
6499 * transition should be really quick) but let's just assert for
6500 * now to see if it ever happens on practice. */
6501
6502 AssertFailed();
6503
6504 return setError(E_ACCESSDENIED,
6505 tr("Machine state change is in progress. Please retry the operation later."));
6506 }
6507
6508 ++mData->mMachineStateDeps;
6509 Assert(mData->mMachineStateDeps != 0 /* overflow */);
6510 }
6511
6512 if (aState)
6513 *aState = mData->mMachineState;
6514 if (aRegistered)
6515 *aRegistered = mData->mRegistered;
6516
6517 return S_OK;
6518}
6519
6520/**
6521 * Decreases the number of objects dependent on the machine state.
6522 * Must always complete the #addStateDependency() call after the state
6523 * dependency is no more necessary.
6524 */
6525void Machine::releaseStateDependency()
6526{
6527 AutoCaller autoCaller(this);
6528 AssertComRCReturnVoid(autoCaller.rc());
6529
6530 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6531
6532 /* releaseStateDependency() w/o addStateDependency()? */
6533 AssertReturnVoid(mData->mMachineStateDeps != 0);
6534 -- mData->mMachineStateDeps;
6535
6536 if (mData->mMachineStateDeps == 0)
6537 {
6538 /* inform ensureNoStateDependencies() that there are no more deps */
6539 if (mData->mMachineStateChangePending != 0)
6540 {
6541 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
6542 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
6543 }
6544 }
6545}
6546
6547// protected methods
6548/////////////////////////////////////////////////////////////////////////////
6549
6550/**
6551 * Performs machine state checks based on the @a aDepType value. If a check
6552 * fails, this method will set extended error info, otherwise it will return
6553 * S_OK. It is supposed, that on failure, the caller will immediately return
6554 * the return value of this method to the upper level.
6555 *
6556 * When @a aDepType is AnyStateDep, this method always returns S_OK.
6557 *
6558 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
6559 * current state of this machine object allows to change settings of the
6560 * machine (i.e. the machine is not registered, or registered but not running
6561 * and not saved). It is useful to call this method from Machine setters
6562 * before performing any change.
6563 *
6564 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
6565 * as for MutableStateDep except that if the machine is saved, S_OK is also
6566 * returned. This is useful in setters which allow changing machine
6567 * properties when it is in the saved state.
6568 *
6569 * @param aDepType Dependency type to check.
6570 *
6571 * @note Non Machine based classes should use #addStateDependency() and
6572 * #releaseStateDependency() methods or the smart AutoStateDependency
6573 * template.
6574 *
6575 * @note This method must be called from under this object's read or write
6576 * lock.
6577 */
6578HRESULT Machine::checkStateDependency(StateDependency aDepType)
6579{
6580 switch (aDepType)
6581 {
6582 case AnyStateDep:
6583 {
6584 break;
6585 }
6586 case MutableStateDep:
6587 {
6588 if ( mData->mRegistered
6589 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
6590 || ( mData->mMachineState != MachineState_Paused
6591 && mData->mMachineState != MachineState_Running
6592 && mData->mMachineState != MachineState_Aborted
6593 && mData->mMachineState != MachineState_Teleported
6594 && mData->mMachineState != MachineState_PoweredOff
6595 )
6596 )
6597 )
6598 return setError(VBOX_E_INVALID_VM_STATE,
6599 tr("The machine is not mutable (state is %s)"),
6600 Global::stringifyMachineState(mData->mMachineState));
6601 break;
6602 }
6603 case MutableOrSavedStateDep:
6604 {
6605 if ( mData->mRegistered
6606 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
6607 || ( mData->mMachineState != MachineState_Paused
6608 && mData->mMachineState != MachineState_Running
6609 && mData->mMachineState != MachineState_Aborted
6610 && mData->mMachineState != MachineState_Teleported
6611 && mData->mMachineState != MachineState_Saved
6612 && mData->mMachineState != MachineState_PoweredOff
6613 )
6614 )
6615 )
6616 return setError(VBOX_E_INVALID_VM_STATE,
6617 tr("The machine is not mutable (state is %s)"),
6618 Global::stringifyMachineState(mData->mMachineState));
6619 break;
6620 }
6621 }
6622
6623 return S_OK;
6624}
6625
6626/**
6627 * Helper to initialize all associated child objects and allocate data
6628 * structures.
6629 *
6630 * This method must be called as a part of the object's initialization procedure
6631 * (usually done in the #init() method).
6632 *
6633 * @note Must be called only from #init() or from #registeredInit().
6634 */
6635HRESULT Machine::initDataAndChildObjects()
6636{
6637 AutoCaller autoCaller(this);
6638 AssertComRCReturnRC(autoCaller.rc());
6639 AssertComRCReturn(autoCaller.state() == InInit ||
6640 autoCaller.state() == Limited, E_FAIL);
6641
6642 AssertReturn(!mData->mAccessible, E_FAIL);
6643
6644 /* allocate data structures */
6645 mSSData.allocate();
6646 mUserData.allocate();
6647 mHWData.allocate();
6648 mMediaData.allocate();
6649 mStorageControllers.allocate();
6650
6651 /* initialize mOSTypeId */
6652 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
6653
6654 /* create associated BIOS settings object */
6655 unconst(mBIOSSettings).createObject();
6656 mBIOSSettings->init(this);
6657
6658 /* create an associated VRDE object (default is disabled) */
6659 unconst(mVRDEServer).createObject();
6660 mVRDEServer->init(this);
6661
6662 /* create associated serial port objects */
6663 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
6664 {
6665 unconst(mSerialPorts[slot]).createObject();
6666 mSerialPorts[slot]->init(this, slot);
6667 }
6668
6669 /* create associated parallel port objects */
6670 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
6671 {
6672 unconst(mParallelPorts[slot]).createObject();
6673 mParallelPorts[slot]->init(this, slot);
6674 }
6675
6676 /* create the audio adapter object (always present, default is disabled) */
6677 unconst(mAudioAdapter).createObject();
6678 mAudioAdapter->init(this);
6679
6680 /* create the USB controller object (always present, default is disabled) */
6681 unconst(mUSBController).createObject();
6682 mUSBController->init(this);
6683
6684 /* create associated network adapter objects */
6685 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot ++)
6686 {
6687 unconst(mNetworkAdapters[slot]).createObject();
6688 mNetworkAdapters[slot]->init(this, slot);
6689 }
6690
6691 /* create the bandwidth control */
6692 unconst(mBandwidthControl).createObject();
6693 mBandwidthControl->init(this);
6694
6695 return S_OK;
6696}
6697
6698/**
6699 * Helper to uninitialize all associated child objects and to free all data
6700 * structures.
6701 *
6702 * This method must be called as a part of the object's uninitialization
6703 * procedure (usually done in the #uninit() method).
6704 *
6705 * @note Must be called only from #uninit() or from #registeredInit().
6706 */
6707void Machine::uninitDataAndChildObjects()
6708{
6709 AutoCaller autoCaller(this);
6710 AssertComRCReturnVoid(autoCaller.rc());
6711 AssertComRCReturnVoid( autoCaller.state() == InUninit
6712 || autoCaller.state() == Limited);
6713
6714 /* tell all our other child objects we've been uninitialized */
6715 if (mBandwidthControl)
6716 {
6717 mBandwidthControl->uninit();
6718 unconst(mBandwidthControl).setNull();
6719 }
6720
6721 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
6722 {
6723 if (mNetworkAdapters[slot])
6724 {
6725 mNetworkAdapters[slot]->uninit();
6726 unconst(mNetworkAdapters[slot]).setNull();
6727 }
6728 }
6729
6730 if (mUSBController)
6731 {
6732 mUSBController->uninit();
6733 unconst(mUSBController).setNull();
6734 }
6735
6736 if (mAudioAdapter)
6737 {
6738 mAudioAdapter->uninit();
6739 unconst(mAudioAdapter).setNull();
6740 }
6741
6742 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
6743 {
6744 if (mParallelPorts[slot])
6745 {
6746 mParallelPorts[slot]->uninit();
6747 unconst(mParallelPorts[slot]).setNull();
6748 }
6749 }
6750
6751 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
6752 {
6753 if (mSerialPorts[slot])
6754 {
6755 mSerialPorts[slot]->uninit();
6756 unconst(mSerialPorts[slot]).setNull();
6757 }
6758 }
6759
6760 if (mVRDEServer)
6761 {
6762 mVRDEServer->uninit();
6763 unconst(mVRDEServer).setNull();
6764 }
6765
6766 if (mBIOSSettings)
6767 {
6768 mBIOSSettings->uninit();
6769 unconst(mBIOSSettings).setNull();
6770 }
6771
6772 /* Deassociate hard disks (only when a real Machine or a SnapshotMachine
6773 * instance is uninitialized; SessionMachine instances refer to real
6774 * Machine hard disks). This is necessary for a clean re-initialization of
6775 * the VM after successfully re-checking the accessibility state. Note
6776 * that in case of normal Machine or SnapshotMachine uninitialization (as
6777 * a result of unregistering or deleting the snapshot), outdated hard
6778 * disk attachments will already be uninitialized and deleted, so this
6779 * code will not affect them. */
6780 if ( !!mMediaData
6781 && (!isSessionMachine())
6782 )
6783 {
6784 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
6785 it != mMediaData->mAttachments.end();
6786 ++it)
6787 {
6788 ComObjPtr<Medium> hd = (*it)->getMedium();
6789 if (hd.isNull())
6790 continue;
6791 HRESULT rc = hd->removeBackReference(mData->mUuid, getSnapshotId());
6792 AssertComRC(rc);
6793 }
6794 }
6795
6796 if (!isSessionMachine() && !isSnapshotMachine())
6797 {
6798 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
6799 if (mData->mFirstSnapshot)
6800 {
6801 // snapshots tree is protected by media write lock; strictly
6802 // this isn't necessary here since we're deleting the entire
6803 // machine, but otherwise we assert in Snapshot::uninit()
6804 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6805 mData->mFirstSnapshot->uninit();
6806 mData->mFirstSnapshot.setNull();
6807 }
6808
6809 mData->mCurrentSnapshot.setNull();
6810 }
6811
6812 /* free data structures (the essential mData structure is not freed here
6813 * since it may be still in use) */
6814 mMediaData.free();
6815 mStorageControllers.free();
6816 mHWData.free();
6817 mUserData.free();
6818 mSSData.free();
6819}
6820
6821/**
6822 * Returns a pointer to the Machine object for this machine that acts like a
6823 * parent for complex machine data objects such as shared folders, etc.
6824 *
6825 * For primary Machine objects and for SnapshotMachine objects, returns this
6826 * object's pointer itself. For SessionMachine objects, returns the peer
6827 * (primary) machine pointer.
6828 */
6829Machine* Machine::getMachine()
6830{
6831 if (isSessionMachine())
6832 return (Machine*)mPeer;
6833 return this;
6834}
6835
6836/**
6837 * Makes sure that there are no machine state dependents. If necessary, waits
6838 * for the number of dependents to drop to zero.
6839 *
6840 * Make sure this method is called from under this object's write lock to
6841 * guarantee that no new dependents may be added when this method returns
6842 * control to the caller.
6843 *
6844 * @note Locks this object for writing. The lock will be released while waiting
6845 * (if necessary).
6846 *
6847 * @warning To be used only in methods that change the machine state!
6848 */
6849void Machine::ensureNoStateDependencies()
6850{
6851 AssertReturnVoid(isWriteLockOnCurrentThread());
6852
6853 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6854
6855 /* Wait for all state dependents if necessary */
6856 if (mData->mMachineStateDeps != 0)
6857 {
6858 /* lazy semaphore creation */
6859 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
6860 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
6861
6862 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
6863 mData->mMachineStateDeps));
6864
6865 ++mData->mMachineStateChangePending;
6866
6867 /* reset the semaphore before waiting, the last dependent will signal
6868 * it */
6869 RTSemEventMultiReset(mData->mMachineStateDepsSem);
6870
6871 alock.leave();
6872
6873 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
6874
6875 alock.enter();
6876
6877 -- mData->mMachineStateChangePending;
6878 }
6879}
6880
6881/**
6882 * Changes the machine state and informs callbacks.
6883 *
6884 * This method is not intended to fail so it either returns S_OK or asserts (and
6885 * returns a failure).
6886 *
6887 * @note Locks this object for writing.
6888 */
6889HRESULT Machine::setMachineState(MachineState_T aMachineState)
6890{
6891 LogFlowThisFuncEnter();
6892 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
6893
6894 AutoCaller autoCaller(this);
6895 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
6896
6897 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6898
6899 /* wait for state dependents to drop to zero */
6900 ensureNoStateDependencies();
6901
6902 if (mData->mMachineState != aMachineState)
6903 {
6904 mData->mMachineState = aMachineState;
6905
6906 RTTimeNow(&mData->mLastStateChange);
6907
6908 mParent->onMachineStateChange(mData->mUuid, aMachineState);
6909 }
6910
6911 LogFlowThisFuncLeave();
6912 return S_OK;
6913}
6914
6915/**
6916 * Searches for a shared folder with the given logical name
6917 * in the collection of shared folders.
6918 *
6919 * @param aName logical name of the shared folder
6920 * @param aSharedFolder where to return the found object
6921 * @param aSetError whether to set the error info if the folder is
6922 * not found
6923 * @return
6924 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
6925 *
6926 * @note
6927 * must be called from under the object's lock!
6928 */
6929HRESULT Machine::findSharedFolder(CBSTR aName,
6930 ComObjPtr<SharedFolder> &aSharedFolder,
6931 bool aSetError /* = false */)
6932{
6933 bool found = false;
6934 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
6935 !found && it != mHWData->mSharedFolders.end();
6936 ++it)
6937 {
6938 AutoWriteLock alock(*it COMMA_LOCKVAL_SRC_POS);
6939 found = (*it)->getName() == aName;
6940 if (found)
6941 aSharedFolder = *it;
6942 }
6943
6944 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
6945
6946 if (aSetError && !found)
6947 setError(rc, tr("Could not find a shared folder named '%ls'"), aName);
6948
6949 return rc;
6950}
6951
6952/**
6953 * Initializes all machine instance data from the given settings structures
6954 * from XML. The exception is the machine UUID which needs special handling
6955 * depending on the caller's use case, so the caller needs to set that herself.
6956 *
6957 * This gets called in several contexts during machine initialization:
6958 *
6959 * -- When machine XML exists on disk already and needs to be loaded into memory,
6960 * for example, from registeredInit() to load all registered machines on
6961 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
6962 * attached to the machine should be part of some media registry already.
6963 *
6964 * -- During OVF import, when a machine config has been constructed from an
6965 * OVF file. In this case, puuidRegistry is set to the machine UUID to
6966 * ensure that the media listed as attachments in the config (which have
6967 * been imported from the OVF) receive the correct registry ID.
6968 *
6969 * @param config Machine settings from XML.
6970 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
6971 * @return
6972 */
6973HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
6974 const Guid *puuidRegistry)
6975{
6976 // copy name, description, OS type, teleporter, UTC etc.
6977 mUserData->s = config.machineUserData;
6978
6979 // look up the object by Id to check it is valid
6980 ComPtr<IGuestOSType> guestOSType;
6981 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
6982 guestOSType.asOutParam());
6983 if (FAILED(rc)) return rc;
6984
6985 // stateFile (optional)
6986 if (config.strStateFile.isEmpty())
6987 mSSData->mStateFilePath.setNull();
6988 else
6989 {
6990 Utf8Str stateFilePathFull(config.strStateFile);
6991 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
6992 if (RT_FAILURE(vrc))
6993 return setError(E_FAIL,
6994 tr("Invalid saved state file path '%s' (%Rrc)"),
6995 config.strStateFile.c_str(),
6996 vrc);
6997 mSSData->mStateFilePath = stateFilePathFull;
6998 }
6999
7000 // snapshot folder needs special processing so set it again
7001 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
7002 if (FAILED(rc)) return rc;
7003
7004 /* currentStateModified (optional, default is true) */
7005 mData->mCurrentStateModified = config.fCurrentStateModified;
7006
7007 mData->mLastStateChange = config.timeLastStateChange;
7008
7009 /*
7010 * note: all mUserData members must be assigned prior this point because
7011 * we need to commit changes in order to let mUserData be shared by all
7012 * snapshot machine instances.
7013 */
7014 mUserData.commitCopy();
7015
7016 // machine registry, if present (must be loaded before snapshots)
7017 if (config.canHaveOwnMediaRegistry())
7018 {
7019 // determine machine folder
7020 Utf8Str strMachineFolder = getSettingsFileFull();
7021 strMachineFolder.stripFilename();
7022 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
7023 config.mediaRegistry,
7024 strMachineFolder);
7025 if (FAILED(rc)) return rc;
7026 }
7027
7028 /* Snapshot node (optional) */
7029 size_t cRootSnapshots;
7030 if ((cRootSnapshots = config.llFirstSnapshot.size()))
7031 {
7032 // there must be only one root snapshot
7033 Assert(cRootSnapshots == 1);
7034
7035 const settings::Snapshot &snap = config.llFirstSnapshot.front();
7036
7037 rc = loadSnapshot(snap,
7038 config.uuidCurrentSnapshot,
7039 NULL); // no parent == first snapshot
7040 if (FAILED(rc)) return rc;
7041 }
7042
7043 // hardware data
7044 rc = loadHardware(config.hardwareMachine);
7045 if (FAILED(rc)) return rc;
7046
7047 // load storage controllers
7048 rc = loadStorageControllers(config.storageMachine,
7049 puuidRegistry,
7050 NULL /* puuidSnapshot */);
7051 if (FAILED(rc)) return rc;
7052
7053 /*
7054 * NOTE: the assignment below must be the last thing to do,
7055 * otherwise it will be not possible to change the settings
7056 * somewhere in the code above because all setters will be
7057 * blocked by checkStateDependency(MutableStateDep).
7058 */
7059
7060 /* set the machine state to Aborted or Saved when appropriate */
7061 if (config.fAborted)
7062 {
7063 Assert(!mSSData->mStateFilePath.isEmpty());
7064 mSSData->mStateFilePath.setNull();
7065
7066 /* no need to use setMachineState() during init() */
7067 mData->mMachineState = MachineState_Aborted;
7068 }
7069 else if (!mSSData->mStateFilePath.isEmpty())
7070 {
7071 /* no need to use setMachineState() during init() */
7072 mData->mMachineState = MachineState_Saved;
7073 }
7074
7075 // after loading settings, we are no longer different from the XML on disk
7076 mData->flModifications = 0;
7077
7078 return S_OK;
7079}
7080
7081/**
7082 * Recursively loads all snapshots starting from the given.
7083 *
7084 * @param aNode <Snapshot> node.
7085 * @param aCurSnapshotId Current snapshot ID from the settings file.
7086 * @param aParentSnapshot Parent snapshot.
7087 */
7088HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
7089 const Guid &aCurSnapshotId,
7090 Snapshot *aParentSnapshot)
7091{
7092 AssertReturn(!isSnapshotMachine(), E_FAIL);
7093 AssertReturn(!isSessionMachine(), E_FAIL);
7094
7095 HRESULT rc = S_OK;
7096
7097 Utf8Str strStateFile;
7098 if (!data.strStateFile.isEmpty())
7099 {
7100 /* optional */
7101 strStateFile = data.strStateFile;
7102 int vrc = calculateFullPath(strStateFile, strStateFile);
7103 if (RT_FAILURE(vrc))
7104 return setError(E_FAIL,
7105 tr("Invalid saved state file path '%s' (%Rrc)"),
7106 strStateFile.c_str(),
7107 vrc);
7108 }
7109
7110 /* create a snapshot machine object */
7111 ComObjPtr<SnapshotMachine> pSnapshotMachine;
7112 pSnapshotMachine.createObject();
7113 rc = pSnapshotMachine->init(this,
7114 data.hardware,
7115 data.storage,
7116 data.uuid.ref(),
7117 strStateFile);
7118 if (FAILED(rc)) return rc;
7119
7120 /* create a snapshot object */
7121 ComObjPtr<Snapshot> pSnapshot;
7122 pSnapshot.createObject();
7123 /* initialize the snapshot */
7124 rc = pSnapshot->init(mParent, // VirtualBox object
7125 data.uuid,
7126 data.strName,
7127 data.strDescription,
7128 data.timestamp,
7129 pSnapshotMachine,
7130 aParentSnapshot);
7131 if (FAILED(rc)) return rc;
7132
7133 /* memorize the first snapshot if necessary */
7134 if (!mData->mFirstSnapshot)
7135 mData->mFirstSnapshot = pSnapshot;
7136
7137 /* memorize the current snapshot when appropriate */
7138 if ( !mData->mCurrentSnapshot
7139 && pSnapshot->getId() == aCurSnapshotId
7140 )
7141 mData->mCurrentSnapshot = pSnapshot;
7142
7143 // now create the children
7144 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
7145 it != data.llChildSnapshots.end();
7146 ++it)
7147 {
7148 const settings::Snapshot &childData = *it;
7149 // recurse
7150 rc = loadSnapshot(childData,
7151 aCurSnapshotId,
7152 pSnapshot); // parent = the one we created above
7153 if (FAILED(rc)) return rc;
7154 }
7155
7156 return rc;
7157}
7158
7159/**
7160 * @param aNode <Hardware> node.
7161 */
7162HRESULT Machine::loadHardware(const settings::Hardware &data)
7163{
7164 AssertReturn(!isSessionMachine(), E_FAIL);
7165
7166 HRESULT rc = S_OK;
7167
7168 try
7169 {
7170 /* The hardware version attribute (optional). */
7171 mHWData->mHWVersion = data.strVersion;
7172 mHWData->mHardwareUUID = data.uuid;
7173
7174 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
7175 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
7176 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
7177 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
7178 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
7179 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
7180 mHWData->mPAEEnabled = data.fPAE;
7181 mHWData->mSyntheticCpu = data.fSyntheticCpu;
7182
7183 mHWData->mCPUCount = data.cCPUs;
7184 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
7185 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
7186
7187 // cpu
7188 if (mHWData->mCPUHotPlugEnabled)
7189 {
7190 for (settings::CpuList::const_iterator it = data.llCpus.begin();
7191 it != data.llCpus.end();
7192 ++it)
7193 {
7194 const settings::Cpu &cpu = *it;
7195
7196 mHWData->mCPUAttached[cpu.ulId] = true;
7197 }
7198 }
7199
7200 // cpuid leafs
7201 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
7202 it != data.llCpuIdLeafs.end();
7203 ++it)
7204 {
7205 const settings::CpuIdLeaf &leaf = *it;
7206
7207 switch (leaf.ulId)
7208 {
7209 case 0x0:
7210 case 0x1:
7211 case 0x2:
7212 case 0x3:
7213 case 0x4:
7214 case 0x5:
7215 case 0x6:
7216 case 0x7:
7217 case 0x8:
7218 case 0x9:
7219 case 0xA:
7220 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
7221 break;
7222
7223 case 0x80000000:
7224 case 0x80000001:
7225 case 0x80000002:
7226 case 0x80000003:
7227 case 0x80000004:
7228 case 0x80000005:
7229 case 0x80000006:
7230 case 0x80000007:
7231 case 0x80000008:
7232 case 0x80000009:
7233 case 0x8000000A:
7234 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
7235 break;
7236
7237 default:
7238 /* just ignore */
7239 break;
7240 }
7241 }
7242
7243 mHWData->mMemorySize = data.ulMemorySizeMB;
7244 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
7245
7246 // boot order
7247 for (size_t i = 0;
7248 i < RT_ELEMENTS(mHWData->mBootOrder);
7249 i++)
7250 {
7251 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
7252 if (it == data.mapBootOrder.end())
7253 mHWData->mBootOrder[i] = DeviceType_Null;
7254 else
7255 mHWData->mBootOrder[i] = it->second;
7256 }
7257
7258 mHWData->mVRAMSize = data.ulVRAMSizeMB;
7259 mHWData->mMonitorCount = data.cMonitors;
7260 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
7261 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
7262 mHWData->mFirmwareType = data.firmwareType;
7263 mHWData->mPointingHidType = data.pointingHidType;
7264 mHWData->mKeyboardHidType = data.keyboardHidType;
7265 mHWData->mChipsetType = data.chipsetType;
7266 mHWData->mHpetEnabled = data.fHpetEnabled;
7267
7268 /* VRDEServer */
7269 rc = mVRDEServer->loadSettings(data.vrdeSettings);
7270 if (FAILED(rc)) return rc;
7271
7272 /* BIOS */
7273 rc = mBIOSSettings->loadSettings(data.biosSettings);
7274 if (FAILED(rc)) return rc;
7275
7276 /* USB Controller */
7277 rc = mUSBController->loadSettings(data.usbController);
7278 if (FAILED(rc)) return rc;
7279
7280 // network adapters
7281 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
7282 it != data.llNetworkAdapters.end();
7283 ++it)
7284 {
7285 const settings::NetworkAdapter &nic = *it;
7286
7287 /* slot unicity is guaranteed by XML Schema */
7288 AssertBreak(nic.ulSlot < RT_ELEMENTS(mNetworkAdapters));
7289 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(nic);
7290 if (FAILED(rc)) return rc;
7291 }
7292
7293 // serial ports
7294 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
7295 it != data.llSerialPorts.end();
7296 ++it)
7297 {
7298 const settings::SerialPort &s = *it;
7299
7300 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
7301 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
7302 if (FAILED(rc)) return rc;
7303 }
7304
7305 // parallel ports (optional)
7306 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
7307 it != data.llParallelPorts.end();
7308 ++it)
7309 {
7310 const settings::ParallelPort &p = *it;
7311
7312 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
7313 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
7314 if (FAILED(rc)) return rc;
7315 }
7316
7317 /* AudioAdapter */
7318 rc = mAudioAdapter->loadSettings(data.audioAdapter);
7319 if (FAILED(rc)) return rc;
7320
7321 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
7322 it != data.llSharedFolders.end();
7323 ++it)
7324 {
7325 const settings::SharedFolder &sf = *it;
7326 rc = CreateSharedFolder(Bstr(sf.strName).raw(),
7327 Bstr(sf.strHostPath).raw(),
7328 sf.fWritable, sf.fAutoMount);
7329 if (FAILED(rc)) return rc;
7330 }
7331
7332 // Clipboard
7333 mHWData->mClipboardMode = data.clipboardMode;
7334
7335 // guest settings
7336 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
7337
7338 // IO settings
7339 mHWData->mIoCacheEnabled = data.ioSettings.fIoCacheEnabled;
7340 mHWData->mIoCacheSize = data.ioSettings.ulIoCacheSize;
7341
7342 // Bandwidth control
7343 rc = mBandwidthControl->loadSettings(data.ioSettings);
7344 if (FAILED(rc)) return rc;
7345
7346#ifdef VBOX_WITH_GUEST_PROPS
7347 /* Guest properties (optional) */
7348 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
7349 it != data.llGuestProperties.end();
7350 ++it)
7351 {
7352 const settings::GuestProperty &prop = *it;
7353 uint32_t fFlags = guestProp::NILFLAG;
7354 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
7355 HWData::GuestProperty property = { prop.strName, prop.strValue, prop.timestamp, fFlags };
7356 mHWData->mGuestProperties.push_back(property);
7357 }
7358
7359 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
7360#endif /* VBOX_WITH_GUEST_PROPS defined */
7361 }
7362 catch(std::bad_alloc &)
7363 {
7364 return E_OUTOFMEMORY;
7365 }
7366
7367 AssertComRC(rc);
7368 return rc;
7369}
7370
7371/**
7372 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
7373 *
7374 * @param data
7375 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
7376 * @param puuidSnapshot
7377 * @return
7378 */
7379HRESULT Machine::loadStorageControllers(const settings::Storage &data,
7380 const Guid *puuidRegistry,
7381 const Guid *puuidSnapshot)
7382{
7383 AssertReturn(!isSessionMachine(), E_FAIL);
7384
7385 HRESULT rc = S_OK;
7386
7387 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
7388 it != data.llStorageControllers.end();
7389 ++it)
7390 {
7391 const settings::StorageController &ctlData = *it;
7392
7393 ComObjPtr<StorageController> pCtl;
7394 /* Try to find one with the name first. */
7395 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
7396 if (SUCCEEDED(rc))
7397 return setError(VBOX_E_OBJECT_IN_USE,
7398 tr("Storage controller named '%s' already exists"),
7399 ctlData.strName.c_str());
7400
7401 pCtl.createObject();
7402 rc = pCtl->init(this,
7403 ctlData.strName,
7404 ctlData.storageBus,
7405 ctlData.ulInstance,
7406 ctlData.fBootable);
7407 if (FAILED(rc)) return rc;
7408
7409 mStorageControllers->push_back(pCtl);
7410
7411 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
7412 if (FAILED(rc)) return rc;
7413
7414 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
7415 if (FAILED(rc)) return rc;
7416
7417 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
7418 if (FAILED(rc)) return rc;
7419
7420 /* Set IDE emulation settings (only for AHCI controller). */
7421 if (ctlData.controllerType == StorageControllerType_IntelAhci)
7422 {
7423 if ( (FAILED(rc = pCtl->SetIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
7424 || (FAILED(rc = pCtl->SetIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
7425 || (FAILED(rc = pCtl->SetIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
7426 || (FAILED(rc = pCtl->SetIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
7427 )
7428 return rc;
7429 }
7430
7431 /* Load the attached devices now. */
7432 rc = loadStorageDevices(pCtl,
7433 ctlData,
7434 puuidRegistry,
7435 puuidSnapshot);
7436 if (FAILED(rc)) return rc;
7437 }
7438
7439 return S_OK;
7440}
7441
7442/**
7443 * Called from loadStorageControllers for a controller's devices.
7444 *
7445 * @param aStorageController
7446 * @param data
7447 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
7448 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
7449 * @return
7450 */
7451HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
7452 const settings::StorageController &data,
7453 const Guid *puuidRegistry,
7454 const Guid *puuidSnapshot)
7455{
7456 HRESULT rc = S_OK;
7457
7458 /* paranoia: detect duplicate attachments */
7459 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
7460 it != data.llAttachedDevices.end();
7461 ++it)
7462 {
7463 const settings::AttachedDevice &ad = *it;
7464
7465 for (settings::AttachedDevicesList::const_iterator it2 = it;
7466 it2 != data.llAttachedDevices.end();
7467 ++it2)
7468 {
7469 if (it == it2)
7470 continue;
7471
7472 const settings::AttachedDevice &ad2 = *it2;
7473
7474 if ( ad.lPort == ad2.lPort
7475 && ad.lDevice == ad2.lDevice)
7476 {
7477 return setError(E_FAIL,
7478 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
7479 aStorageController->getName().c_str(),
7480 ad.lPort,
7481 ad.lDevice,
7482 mUserData->s.strName.c_str());
7483 }
7484 }
7485 }
7486
7487 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
7488 it != data.llAttachedDevices.end();
7489 ++it)
7490 {
7491 const settings::AttachedDevice &dev = *it;
7492 ComObjPtr<Medium> medium;
7493
7494 switch (dev.deviceType)
7495 {
7496 case DeviceType_Floppy:
7497 case DeviceType_DVD:
7498 rc = mParent->findRemoveableMedium(dev.deviceType, dev.uuid, false /* fRefresh */, medium);
7499 if (FAILED(rc))
7500 return rc;
7501 break;
7502
7503 case DeviceType_HardDisk:
7504 {
7505 /* find a hard disk by UUID */
7506 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
7507 if (FAILED(rc))
7508 {
7509 if (isSnapshotMachine())
7510 {
7511 // wrap another error message around the "cannot find hard disk" set by findHardDisk
7512 // so the user knows that the bad disk is in a snapshot somewhere
7513 com::ErrorInfo info;
7514 return setError(E_FAIL,
7515 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
7516 puuidSnapshot->raw(),
7517 info.getText().raw());
7518 }
7519 else
7520 return rc;
7521 }
7522
7523 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
7524
7525 if (medium->getType() == MediumType_Immutable)
7526 {
7527 if (isSnapshotMachine())
7528 return setError(E_FAIL,
7529 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
7530 "of the virtual machine '%s' ('%s')"),
7531 medium->getLocationFull().c_str(),
7532 dev.uuid.raw(),
7533 puuidSnapshot->raw(),
7534 mUserData->s.strName.c_str(),
7535 mData->m_strConfigFileFull.c_str());
7536
7537 return setError(E_FAIL,
7538 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
7539 medium->getLocationFull().c_str(),
7540 dev.uuid.raw(),
7541 mUserData->s.strName.c_str(),
7542 mData->m_strConfigFileFull.c_str());
7543 }
7544
7545 if ( !isSnapshotMachine()
7546 && medium->getChildren().size() != 0
7547 )
7548 return setError(E_FAIL,
7549 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
7550 "because it has %d differencing child hard disks"),
7551 medium->getLocationFull().c_str(),
7552 dev.uuid.raw(),
7553 mUserData->s.strName.c_str(),
7554 mData->m_strConfigFileFull.c_str(),
7555 medium->getChildren().size());
7556
7557 if (findAttachment(mMediaData->mAttachments,
7558 medium))
7559 return setError(E_FAIL,
7560 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
7561 medium->getLocationFull().c_str(),
7562 dev.uuid.raw(),
7563 mUserData->s.strName.c_str(),
7564 mData->m_strConfigFileFull.c_str());
7565
7566 break;
7567 }
7568
7569 default:
7570 return setError(E_FAIL,
7571 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
7572 medium->getLocationFull().c_str(),
7573 mUserData->s.strName.c_str(),
7574 mData->m_strConfigFileFull.c_str());
7575 }
7576
7577 if (FAILED(rc))
7578 break;
7579
7580 /* Bandwidth groups are loaded at this point. */
7581 ComObjPtr<BandwidthGroup> pBwGroup;
7582
7583 if (!dev.strBwGroup.isEmpty())
7584 {
7585 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
7586 if (FAILED(rc))
7587 return setError(E_FAIL,
7588 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
7589 medium->getLocationFull().c_str(),
7590 dev.strBwGroup.c_str(),
7591 mUserData->s.strName.c_str(),
7592 mData->m_strConfigFileFull.c_str());
7593 }
7594
7595 const Bstr controllerName = aStorageController->getName();
7596 ComObjPtr<MediumAttachment> pAttachment;
7597 pAttachment.createObject();
7598 rc = pAttachment->init(this,
7599 medium,
7600 controllerName,
7601 dev.lPort,
7602 dev.lDevice,
7603 dev.deviceType,
7604 dev.fPassThrough,
7605 pBwGroup);
7606 if (FAILED(rc)) break;
7607
7608 /* associate the medium with this machine and snapshot */
7609 if (!medium.isNull())
7610 {
7611 if (isSnapshotMachine())
7612 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
7613 else
7614 rc = medium->addBackReference(mData->mUuid);
7615
7616 if (puuidRegistry)
7617 // caller wants registry ID to be set on all attached media (OVF import case)
7618 medium->addRegistry(*puuidRegistry);
7619 }
7620
7621 if (FAILED(rc))
7622 break;
7623
7624 /* back up mMediaData to let registeredInit() properly rollback on failure
7625 * (= limited accessibility) */
7626 setModified(IsModified_Storage);
7627 mMediaData.backup();
7628 mMediaData->mAttachments.push_back(pAttachment);
7629 }
7630
7631 return rc;
7632}
7633
7634/**
7635 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
7636 *
7637 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
7638 * @param aSnapshot where to return the found snapshot
7639 * @param aSetError true to set extended error info on failure
7640 */
7641HRESULT Machine::findSnapshotById(const Guid &aId,
7642 ComObjPtr<Snapshot> &aSnapshot,
7643 bool aSetError /* = false */)
7644{
7645 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
7646
7647 if (!mData->mFirstSnapshot)
7648 {
7649 if (aSetError)
7650 return setError(E_FAIL, tr("This machine does not have any snapshots"));
7651 return E_FAIL;
7652 }
7653
7654 if (aId.isEmpty())
7655 aSnapshot = mData->mFirstSnapshot;
7656 else
7657 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
7658
7659 if (!aSnapshot)
7660 {
7661 if (aSetError)
7662 return setError(E_FAIL,
7663 tr("Could not find a snapshot with UUID {%s}"),
7664 aId.toString().c_str());
7665 return E_FAIL;
7666 }
7667
7668 return S_OK;
7669}
7670
7671/**
7672 * Returns the snapshot with the given name or fails of no such snapshot.
7673 *
7674 * @param aName snapshot name to find
7675 * @param aSnapshot where to return the found snapshot
7676 * @param aSetError true to set extended error info on failure
7677 */
7678HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
7679 ComObjPtr<Snapshot> &aSnapshot,
7680 bool aSetError /* = false */)
7681{
7682 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
7683
7684 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
7685
7686 if (!mData->mFirstSnapshot)
7687 {
7688 if (aSetError)
7689 return setError(VBOX_E_OBJECT_NOT_FOUND,
7690 tr("This machine does not have any snapshots"));
7691 return VBOX_E_OBJECT_NOT_FOUND;
7692 }
7693
7694 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
7695
7696 if (!aSnapshot)
7697 {
7698 if (aSetError)
7699 return setError(VBOX_E_OBJECT_NOT_FOUND,
7700 tr("Could not find a snapshot named '%s'"), strName.c_str());
7701 return VBOX_E_OBJECT_NOT_FOUND;
7702 }
7703
7704 return S_OK;
7705}
7706
7707/**
7708 * Returns a storage controller object with the given name.
7709 *
7710 * @param aName storage controller name to find
7711 * @param aStorageController where to return the found storage controller
7712 * @param aSetError true to set extended error info on failure
7713 */
7714HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
7715 ComObjPtr<StorageController> &aStorageController,
7716 bool aSetError /* = false */)
7717{
7718 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
7719
7720 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
7721 it != mStorageControllers->end();
7722 ++it)
7723 {
7724 if ((*it)->getName() == aName)
7725 {
7726 aStorageController = (*it);
7727 return S_OK;
7728 }
7729 }
7730
7731 if (aSetError)
7732 return setError(VBOX_E_OBJECT_NOT_FOUND,
7733 tr("Could not find a storage controller named '%s'"),
7734 aName.c_str());
7735 return VBOX_E_OBJECT_NOT_FOUND;
7736}
7737
7738HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
7739 MediaData::AttachmentList &atts)
7740{
7741 AutoCaller autoCaller(this);
7742 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7743
7744 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7745
7746 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
7747 it != mMediaData->mAttachments.end();
7748 ++it)
7749 {
7750 const ComObjPtr<MediumAttachment> &pAtt = *it;
7751
7752 // should never happen, but deal with NULL pointers in the list.
7753 AssertStmt(!pAtt.isNull(), continue);
7754
7755 // getControllerName() needs caller+read lock
7756 AutoCaller autoAttCaller(pAtt);
7757 if (FAILED(autoAttCaller.rc()))
7758 {
7759 atts.clear();
7760 return autoAttCaller.rc();
7761 }
7762 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
7763
7764 if (pAtt->getControllerName() == aName)
7765 atts.push_back(pAtt);
7766 }
7767
7768 return S_OK;
7769}
7770
7771/**
7772 * Helper for #saveSettings. Cares about renaming the settings directory and
7773 * file if the machine name was changed and about creating a new settings file
7774 * if this is a new machine.
7775 *
7776 * @note Must be never called directly but only from #saveSettings().
7777 */
7778HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
7779{
7780 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7781
7782 HRESULT rc = S_OK;
7783
7784 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
7785
7786 /* attempt to rename the settings file if machine name is changed */
7787 if ( mUserData->s.fNameSync
7788 && mUserData.isBackedUp()
7789 && mUserData.backedUpData()->s.strName != mUserData->s.strName
7790 )
7791 {
7792 bool dirRenamed = false;
7793 bool fileRenamed = false;
7794
7795 Utf8Str configFile, newConfigFile;
7796 Utf8Str configDir, newConfigDir;
7797
7798 do
7799 {
7800 int vrc = VINF_SUCCESS;
7801
7802 Utf8Str name = mUserData.backedUpData()->s.strName;
7803 Utf8Str newName = mUserData->s.strName;
7804
7805 configFile = mData->m_strConfigFileFull;
7806
7807 /* first, rename the directory if it matches the machine name */
7808 configDir = configFile;
7809 configDir.stripFilename();
7810 newConfigDir = configDir;
7811 if (!strcmp(RTPathFilename(configDir.c_str()), name.c_str()))
7812 {
7813 newConfigDir.stripFilename();
7814 newConfigDir.append(RTPATH_DELIMITER);
7815 newConfigDir.append(newName);
7816 /* new dir and old dir cannot be equal here because of 'if'
7817 * above and because name != newName */
7818 Assert(configDir != newConfigDir);
7819 if (!fSettingsFileIsNew)
7820 {
7821 /* perform real rename only if the machine is not new */
7822 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
7823 if (RT_FAILURE(vrc))
7824 {
7825 rc = setError(E_FAIL,
7826 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
7827 configDir.c_str(),
7828 newConfigDir.c_str(),
7829 vrc);
7830 break;
7831 }
7832 dirRenamed = true;
7833 }
7834 }
7835
7836 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
7837 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
7838
7839 /* then try to rename the settings file itself */
7840 if (newConfigFile != configFile)
7841 {
7842 /* get the path to old settings file in renamed directory */
7843 configFile = Utf8StrFmt("%s%c%s",
7844 newConfigDir.c_str(),
7845 RTPATH_DELIMITER,
7846 RTPathFilename(configFile.c_str()));
7847 if (!fSettingsFileIsNew)
7848 {
7849 /* perform real rename only if the machine is not new */
7850 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
7851 if (RT_FAILURE(vrc))
7852 {
7853 rc = setError(E_FAIL,
7854 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
7855 configFile.c_str(),
7856 newConfigFile.c_str(),
7857 vrc);
7858 break;
7859 }
7860 fileRenamed = true;
7861 }
7862 }
7863
7864 // update m_strConfigFileFull amd mConfigFile
7865 mData->m_strConfigFileFull = newConfigFile;
7866 // compute the relative path too
7867 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
7868
7869 // store the old and new so that VirtualBox::saveSettings() can update
7870 // the media registry
7871 if ( mData->mRegistered
7872 && configDir != newConfigDir)
7873 {
7874 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
7875
7876 if (pfNeedsGlobalSaveSettings)
7877 *pfNeedsGlobalSaveSettings = true;
7878 }
7879
7880 /* update the saved state file path */
7881 Utf8Str path = mSSData->mStateFilePath;
7882 if (RTPathStartsWith(path.c_str(), configDir.c_str()))
7883 mSSData->mStateFilePath = Utf8StrFmt("%s%s",
7884 newConfigDir.c_str(),
7885 path.c_str() + configDir.length());
7886
7887 /* Update saved state file paths of all online snapshots.
7888 * Note that saveSettings() will recognize name change
7889 * and will save all snapshots in this case. */
7890 if (mData->mFirstSnapshot)
7891 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
7892 newConfigDir.c_str());
7893 }
7894 while (0);
7895
7896 if (FAILED(rc))
7897 {
7898 /* silently try to rename everything back */
7899 if (fileRenamed)
7900 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
7901 if (dirRenamed)
7902 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
7903 }
7904
7905 if (FAILED(rc)) return rc;
7906 }
7907
7908 if (fSettingsFileIsNew)
7909 {
7910 /* create a virgin config file */
7911 int vrc = VINF_SUCCESS;
7912
7913 /* ensure the settings directory exists */
7914 Utf8Str path(mData->m_strConfigFileFull);
7915 path.stripFilename();
7916 if (!RTDirExists(path.c_str()))
7917 {
7918 vrc = RTDirCreateFullPath(path.c_str(), 0777);
7919 if (RT_FAILURE(vrc))
7920 {
7921 return setError(E_FAIL,
7922 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
7923 path.c_str(),
7924 vrc);
7925 }
7926 }
7927
7928 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
7929 path = Utf8Str(mData->m_strConfigFileFull);
7930 RTFILE f = NIL_RTFILE;
7931 vrc = RTFileOpen(&f, path.c_str(),
7932 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
7933 if (RT_FAILURE(vrc))
7934 return setError(E_FAIL,
7935 tr("Could not create the settings file '%s' (%Rrc)"),
7936 path.c_str(),
7937 vrc);
7938 RTFileClose(f);
7939 }
7940
7941 return rc;
7942}
7943
7944/**
7945 * Saves and commits machine data, user data and hardware data.
7946 *
7947 * Note that on failure, the data remains uncommitted.
7948 *
7949 * @a aFlags may combine the following flags:
7950 *
7951 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
7952 * Used when saving settings after an operation that makes them 100%
7953 * correspond to the settings from the current snapshot.
7954 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
7955 * #isReallyModified() returns false. This is necessary for cases when we
7956 * change machine data directly, not through the backup()/commit() mechanism.
7957 * - SaveS_Force: settings will be saved without doing a deep compare of the
7958 * settings structures. This is used when this is called because snapshots
7959 * have changed to avoid the overhead of the deep compare.
7960 *
7961 * @note Must be called from under this object's write lock. Locks children for
7962 * writing.
7963 *
7964 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
7965 * initialized to false and that will be set to true by this function if
7966 * the caller must invoke VirtualBox::saveSettings() because the global
7967 * settings have changed. This will happen if a machine rename has been
7968 * saved and the global machine and media registries will therefore need
7969 * updating.
7970 */
7971HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
7972 int aFlags /*= 0*/)
7973{
7974 LogFlowThisFuncEnter();
7975
7976 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7977
7978 /* make sure child objects are unable to modify the settings while we are
7979 * saving them */
7980 ensureNoStateDependencies();
7981
7982 AssertReturn(!isSnapshotMachine(),
7983 E_FAIL);
7984
7985 HRESULT rc = S_OK;
7986 bool fNeedsWrite = false;
7987
7988 /* First, prepare to save settings. It will care about renaming the
7989 * settings directory and file if the machine name was changed and about
7990 * creating a new settings file if this is a new machine. */
7991 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
7992 if (FAILED(rc)) return rc;
7993
7994 // keep a pointer to the current settings structures
7995 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
7996 settings::MachineConfigFile *pNewConfig = NULL;
7997
7998 try
7999 {
8000 // make a fresh one to have everyone write stuff into
8001 pNewConfig = new settings::MachineConfigFile(NULL);
8002 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
8003
8004 // now go and copy all the settings data from COM to the settings structures
8005 // (this calles saveSettings() on all the COM objects in the machine)
8006 copyMachineDataToSettings(*pNewConfig);
8007
8008 if (aFlags & SaveS_ResetCurStateModified)
8009 {
8010 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
8011 mData->mCurrentStateModified = FALSE;
8012 fNeedsWrite = true; // always, no need to compare
8013 }
8014 else if (aFlags & SaveS_Force)
8015 {
8016 fNeedsWrite = true; // always, no need to compare
8017 }
8018 else
8019 {
8020 if (!mData->mCurrentStateModified)
8021 {
8022 // do a deep compare of the settings that we just saved with the settings
8023 // previously stored in the config file; this invokes MachineConfigFile::operator==
8024 // which does a deep compare of all the settings, which is expensive but less expensive
8025 // than writing out XML in vain
8026 bool fAnySettingsChanged = (*pNewConfig == *pOldConfig);
8027
8028 // could still be modified if any settings changed
8029 mData->mCurrentStateModified = fAnySettingsChanged;
8030
8031 fNeedsWrite = fAnySettingsChanged;
8032 }
8033 else
8034 fNeedsWrite = true;
8035 }
8036
8037 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
8038
8039 if (fNeedsWrite)
8040 // now spit it all out!
8041 pNewConfig->write(mData->m_strConfigFileFull);
8042
8043 mData->pMachineConfigFile = pNewConfig;
8044 delete pOldConfig;
8045 commit();
8046
8047 // after saving settings, we are no longer different from the XML on disk
8048 mData->flModifications = 0;
8049 }
8050 catch (HRESULT err)
8051 {
8052 // we assume that error info is set by the thrower
8053 rc = err;
8054
8055 // restore old config
8056 delete pNewConfig;
8057 mData->pMachineConfigFile = pOldConfig;
8058 }
8059 catch (...)
8060 {
8061 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
8062 }
8063
8064 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
8065 {
8066 /* Fire the data change event, even on failure (since we've already
8067 * committed all data). This is done only for SessionMachines because
8068 * mutable Machine instances are always not registered (i.e. private
8069 * to the client process that creates them) and thus don't need to
8070 * inform callbacks. */
8071 if (isSessionMachine())
8072 mParent->onMachineDataChange(mData->mUuid);
8073 }
8074
8075 LogFlowThisFunc(("rc=%08X\n", rc));
8076 LogFlowThisFuncLeave();
8077 return rc;
8078}
8079
8080/**
8081 * Implementation for saving the machine settings into the given
8082 * settings::MachineConfigFile instance. This copies machine extradata
8083 * from the previous machine config file in the instance data, if any.
8084 *
8085 * This gets called from two locations:
8086 *
8087 * -- Machine::saveSettings(), during the regular XML writing;
8088 *
8089 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
8090 * exported to OVF and we write the VirtualBox proprietary XML
8091 * into a <vbox:Machine> tag.
8092 *
8093 * This routine fills all the fields in there, including snapshots, *except*
8094 * for the following:
8095 *
8096 * -- fCurrentStateModified. There is some special logic associated with that.
8097 *
8098 * The caller can then call MachineConfigFile::write() or do something else
8099 * with it.
8100 *
8101 * Caller must hold the machine lock!
8102 *
8103 * This throws XML errors and HRESULT, so the caller must have a catch block!
8104 */
8105void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
8106{
8107 // deep copy extradata
8108 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
8109
8110 config.uuid = mData->mUuid;
8111
8112 // copy name, description, OS type, teleport, UTC etc.
8113 config.machineUserData = mUserData->s;
8114
8115 if ( mData->mMachineState == MachineState_Saved
8116 || mData->mMachineState == MachineState_Restoring
8117 // when deleting a snapshot we may or may not have a saved state in the current state,
8118 // so let's not assert here please
8119 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
8120 || mData->mMachineState == MachineState_DeletingSnapshotOnline
8121 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
8122 && (!mSSData->mStateFilePath.isEmpty())
8123 )
8124 )
8125 {
8126 Assert(!mSSData->mStateFilePath.isEmpty());
8127 /* try to make the file name relative to the settings file dir */
8128 copyPathRelativeToMachine(mSSData->mStateFilePath, config.strStateFile);
8129 }
8130 else
8131 {
8132 Assert(mSSData->mStateFilePath.isEmpty());
8133 config.strStateFile.setNull();
8134 }
8135
8136 if (mData->mCurrentSnapshot)
8137 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
8138 else
8139 config.uuidCurrentSnapshot.clear();
8140
8141 config.timeLastStateChange = mData->mLastStateChange;
8142 config.fAborted = (mData->mMachineState == MachineState_Aborted);
8143 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
8144
8145 HRESULT rc = saveHardware(config.hardwareMachine);
8146 if (FAILED(rc)) throw rc;
8147
8148 rc = saveStorageControllers(config.storageMachine);
8149 if (FAILED(rc)) throw rc;
8150
8151 // save machine's media registry if this is VirtualBox 4.0 or later
8152 if (config.canHaveOwnMediaRegistry())
8153 {
8154 // determine machine folder
8155 Utf8Str strMachineFolder = getSettingsFileFull();
8156 strMachineFolder.stripFilename();
8157 mParent->saveMediaRegistry(config.mediaRegistry,
8158 getId(), // only media with registry ID == machine UUID
8159 strMachineFolder);
8160 // this throws HRESULT
8161 }
8162
8163 // save snapshots
8164 rc = saveAllSnapshots(config);
8165 if (FAILED(rc)) throw rc;
8166}
8167
8168/**
8169 * Saves all snapshots of the machine into the given machine config file. Called
8170 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
8171 * @param config
8172 * @return
8173 */
8174HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
8175{
8176 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
8177
8178 HRESULT rc = S_OK;
8179
8180 try
8181 {
8182 config.llFirstSnapshot.clear();
8183
8184 if (mData->mFirstSnapshot)
8185 {
8186 settings::Snapshot snapNew;
8187 config.llFirstSnapshot.push_back(snapNew);
8188
8189 // get reference to the fresh copy of the snapshot on the list and
8190 // work on that copy directly to avoid excessive copying later
8191 settings::Snapshot &snap = config.llFirstSnapshot.front();
8192
8193 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
8194 if (FAILED(rc)) throw rc;
8195 }
8196
8197// if (mType == IsSessionMachine)
8198// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
8199
8200 }
8201 catch (HRESULT err)
8202 {
8203 /* we assume that error info is set by the thrower */
8204 rc = err;
8205 }
8206 catch (...)
8207 {
8208 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
8209 }
8210
8211 return rc;
8212}
8213
8214/**
8215 * Saves the VM hardware configuration. It is assumed that the
8216 * given node is empty.
8217 *
8218 * @param aNode <Hardware> node to save the VM hardware configuration to.
8219 */
8220HRESULT Machine::saveHardware(settings::Hardware &data)
8221{
8222 HRESULT rc = S_OK;
8223
8224 try
8225 {
8226 /* The hardware version attribute (optional).
8227 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
8228 if ( mHWData->mHWVersion == "1"
8229 && mSSData->mStateFilePath.isEmpty()
8230 )
8231 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. */
8232
8233 data.strVersion = mHWData->mHWVersion;
8234 data.uuid = mHWData->mHardwareUUID;
8235
8236 // CPU
8237 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
8238 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
8239 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
8240 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
8241 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
8242 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
8243 data.fPAE = !!mHWData->mPAEEnabled;
8244 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
8245
8246 /* Standard and Extended CPUID leafs. */
8247 data.llCpuIdLeafs.clear();
8248 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
8249 {
8250 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
8251 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
8252 }
8253 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
8254 {
8255 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
8256 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
8257 }
8258
8259 data.cCPUs = mHWData->mCPUCount;
8260 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
8261 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
8262
8263 data.llCpus.clear();
8264 if (data.fCpuHotPlug)
8265 {
8266 for (unsigned idx = 0; idx < data.cCPUs; idx++)
8267 {
8268 if (mHWData->mCPUAttached[idx])
8269 {
8270 settings::Cpu cpu;
8271 cpu.ulId = idx;
8272 data.llCpus.push_back(cpu);
8273 }
8274 }
8275 }
8276
8277 // memory
8278 data.ulMemorySizeMB = mHWData->mMemorySize;
8279 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
8280
8281 // firmware
8282 data.firmwareType = mHWData->mFirmwareType;
8283
8284 // HID
8285 data.pointingHidType = mHWData->mPointingHidType;
8286 data.keyboardHidType = mHWData->mKeyboardHidType;
8287
8288 // chipset
8289 data.chipsetType = mHWData->mChipsetType;
8290
8291 // HPET
8292 data.fHpetEnabled = !!mHWData->mHpetEnabled;
8293
8294 // boot order
8295 data.mapBootOrder.clear();
8296 for (size_t i = 0;
8297 i < RT_ELEMENTS(mHWData->mBootOrder);
8298 ++i)
8299 data.mapBootOrder[i] = mHWData->mBootOrder[i];
8300
8301 // display
8302 data.ulVRAMSizeMB = mHWData->mVRAMSize;
8303 data.cMonitors = mHWData->mMonitorCount;
8304 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
8305 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
8306
8307 /* VRDEServer settings (optional) */
8308 rc = mVRDEServer->saveSettings(data.vrdeSettings);
8309 if (FAILED(rc)) throw rc;
8310
8311 /* BIOS (required) */
8312 rc = mBIOSSettings->saveSettings(data.biosSettings);
8313 if (FAILED(rc)) throw rc;
8314
8315 /* USB Controller (required) */
8316 rc = mUSBController->saveSettings(data.usbController);
8317 if (FAILED(rc)) throw rc;
8318
8319 /* Network adapters (required) */
8320 data.llNetworkAdapters.clear();
8321 for (ULONG slot = 0;
8322 slot < RT_ELEMENTS(mNetworkAdapters);
8323 ++slot)
8324 {
8325 settings::NetworkAdapter nic;
8326 nic.ulSlot = slot;
8327 rc = mNetworkAdapters[slot]->saveSettings(nic);
8328 if (FAILED(rc)) throw rc;
8329
8330 data.llNetworkAdapters.push_back(nic);
8331 }
8332
8333 /* Serial ports */
8334 data.llSerialPorts.clear();
8335 for (ULONG slot = 0;
8336 slot < RT_ELEMENTS(mSerialPorts);
8337 ++slot)
8338 {
8339 settings::SerialPort s;
8340 s.ulSlot = slot;
8341 rc = mSerialPorts[slot]->saveSettings(s);
8342 if (FAILED(rc)) return rc;
8343
8344 data.llSerialPorts.push_back(s);
8345 }
8346
8347 /* Parallel ports */
8348 data.llParallelPorts.clear();
8349 for (ULONG slot = 0;
8350 slot < RT_ELEMENTS(mParallelPorts);
8351 ++slot)
8352 {
8353 settings::ParallelPort p;
8354 p.ulSlot = slot;
8355 rc = mParallelPorts[slot]->saveSettings(p);
8356 if (FAILED(rc)) return rc;
8357
8358 data.llParallelPorts.push_back(p);
8359 }
8360
8361 /* Audio adapter */
8362 rc = mAudioAdapter->saveSettings(data.audioAdapter);
8363 if (FAILED(rc)) return rc;
8364
8365 /* Shared folders */
8366 data.llSharedFolders.clear();
8367 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8368 it != mHWData->mSharedFolders.end();
8369 ++it)
8370 {
8371 ComObjPtr<SharedFolder> pFolder = *it;
8372 settings::SharedFolder sf;
8373 sf.strName = pFolder->getName();
8374 sf.strHostPath = pFolder->getHostPath();
8375 sf.fWritable = !!pFolder->isWritable();
8376 sf.fAutoMount = !!pFolder->isAutoMounted();
8377
8378 data.llSharedFolders.push_back(sf);
8379 }
8380
8381 // clipboard
8382 data.clipboardMode = mHWData->mClipboardMode;
8383
8384 /* Guest */
8385 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
8386
8387 // IO settings
8388 data.ioSettings.fIoCacheEnabled = !!mHWData->mIoCacheEnabled;
8389 data.ioSettings.ulIoCacheSize = mHWData->mIoCacheSize;
8390
8391 /* BandwidthControl (required) */
8392 rc = mBandwidthControl->saveSettings(data.ioSettings);
8393 if (FAILED(rc)) throw rc;
8394
8395 // guest properties
8396 data.llGuestProperties.clear();
8397#ifdef VBOX_WITH_GUEST_PROPS
8398 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
8399 it != mHWData->mGuestProperties.end();
8400 ++it)
8401 {
8402 HWData::GuestProperty property = *it;
8403
8404 /* Remove transient guest properties at shutdown unless we
8405 * are saving state */
8406 if ( ( mData->mMachineState == MachineState_PoweredOff
8407 || mData->mMachineState == MachineState_Aborted
8408 || mData->mMachineState == MachineState_Teleported)
8409 && property.mFlags & guestProp::TRANSIENT)
8410 continue;
8411 settings::GuestProperty prop;
8412 prop.strName = property.strName;
8413 prop.strValue = property.strValue;
8414 prop.timestamp = property.mTimestamp;
8415 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
8416 guestProp::writeFlags(property.mFlags, szFlags);
8417 prop.strFlags = szFlags;
8418
8419 data.llGuestProperties.push_back(prop);
8420 }
8421
8422 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
8423 /* I presume this doesn't require a backup(). */
8424 mData->mGuestPropertiesModified = FALSE;
8425#endif /* VBOX_WITH_GUEST_PROPS defined */
8426 }
8427 catch(std::bad_alloc &)
8428 {
8429 return E_OUTOFMEMORY;
8430 }
8431
8432 AssertComRC(rc);
8433 return rc;
8434}
8435
8436/**
8437 * Saves the storage controller configuration.
8438 *
8439 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
8440 */
8441HRESULT Machine::saveStorageControllers(settings::Storage &data)
8442{
8443 data.llStorageControllers.clear();
8444
8445 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
8446 it != mStorageControllers->end();
8447 ++it)
8448 {
8449 HRESULT rc;
8450 ComObjPtr<StorageController> pCtl = *it;
8451
8452 settings::StorageController ctl;
8453 ctl.strName = pCtl->getName();
8454 ctl.controllerType = pCtl->getControllerType();
8455 ctl.storageBus = pCtl->getStorageBus();
8456 ctl.ulInstance = pCtl->getInstance();
8457 ctl.fBootable = pCtl->getBootable();
8458
8459 /* Save the port count. */
8460 ULONG portCount;
8461 rc = pCtl->COMGETTER(PortCount)(&portCount);
8462 ComAssertComRCRet(rc, rc);
8463 ctl.ulPortCount = portCount;
8464
8465 /* Save fUseHostIOCache */
8466 BOOL fUseHostIOCache;
8467 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
8468 ComAssertComRCRet(rc, rc);
8469 ctl.fUseHostIOCache = !!fUseHostIOCache;
8470
8471 /* Save IDE emulation settings. */
8472 if (ctl.controllerType == StorageControllerType_IntelAhci)
8473 {
8474 if ( (FAILED(rc = pCtl->GetIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
8475 || (FAILED(rc = pCtl->GetIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
8476 || (FAILED(rc = pCtl->GetIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
8477 || (FAILED(rc = pCtl->GetIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
8478 )
8479 ComAssertComRCRet(rc, rc);
8480 }
8481
8482 /* save the devices now. */
8483 rc = saveStorageDevices(pCtl, ctl);
8484 ComAssertComRCRet(rc, rc);
8485
8486 data.llStorageControllers.push_back(ctl);
8487 }
8488
8489 return S_OK;
8490}
8491
8492/**
8493 * Saves the hard disk configuration.
8494 */
8495HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
8496 settings::StorageController &data)
8497{
8498 MediaData::AttachmentList atts;
8499
8500 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
8501 if (FAILED(rc)) return rc;
8502
8503 data.llAttachedDevices.clear();
8504 for (MediaData::AttachmentList::const_iterator it = atts.begin();
8505 it != atts.end();
8506 ++it)
8507 {
8508 settings::AttachedDevice dev;
8509
8510 MediumAttachment *pAttach = *it;
8511 Medium *pMedium = pAttach->getMedium();
8512 BandwidthGroup *pBwGroup = pAttach->getBandwidthGroup();
8513
8514 dev.deviceType = pAttach->getType();
8515 dev.lPort = pAttach->getPort();
8516 dev.lDevice = pAttach->getDevice();
8517 if (pMedium)
8518 {
8519 if (pMedium->isHostDrive())
8520 dev.strHostDriveSrc = pMedium->getLocationFull();
8521 else
8522 dev.uuid = pMedium->getId();
8523 dev.fPassThrough = pAttach->getPassthrough();
8524 }
8525
8526 if (pBwGroup)
8527 {
8528 dev.strBwGroup = pBwGroup->getName();
8529 }
8530
8531 data.llAttachedDevices.push_back(dev);
8532 }
8533
8534 return S_OK;
8535}
8536
8537/**
8538 * Saves machine state settings as defined by aFlags
8539 * (SaveSTS_* values).
8540 *
8541 * @param aFlags Combination of SaveSTS_* flags.
8542 *
8543 * @note Locks objects for writing.
8544 */
8545HRESULT Machine::saveStateSettings(int aFlags)
8546{
8547 if (aFlags == 0)
8548 return S_OK;
8549
8550 AutoCaller autoCaller(this);
8551 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8552
8553 /* This object's write lock is also necessary to serialize file access
8554 * (prevent concurrent reads and writes) */
8555 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8556
8557 HRESULT rc = S_OK;
8558
8559 Assert(mData->pMachineConfigFile);
8560
8561 try
8562 {
8563 if (aFlags & SaveSTS_CurStateModified)
8564 mData->pMachineConfigFile->fCurrentStateModified = true;
8565
8566 if (aFlags & SaveSTS_StateFilePath)
8567 {
8568 if (!mSSData->mStateFilePath.isEmpty())
8569 /* try to make the file name relative to the settings file dir */
8570 copyPathRelativeToMachine(mSSData->mStateFilePath, mData->pMachineConfigFile->strStateFile);
8571 else
8572 mData->pMachineConfigFile->strStateFile.setNull();
8573 }
8574
8575 if (aFlags & SaveSTS_StateTimeStamp)
8576 {
8577 Assert( mData->mMachineState != MachineState_Aborted
8578 || mSSData->mStateFilePath.isEmpty());
8579
8580 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
8581
8582 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
8583//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
8584 }
8585
8586 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
8587 }
8588 catch (...)
8589 {
8590 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
8591 }
8592
8593 return rc;
8594}
8595
8596/**
8597 * Creates differencing hard disks for all normal hard disks attached to this
8598 * machine and a new set of attachments to refer to created disks.
8599 *
8600 * Used when taking a snapshot or when deleting the current state. Gets called
8601 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
8602 *
8603 * This method assumes that mMediaData contains the original hard disk attachments
8604 * it needs to create diffs for. On success, these attachments will be replaced
8605 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
8606 * called to delete created diffs which will also rollback mMediaData and restore
8607 * whatever was backed up before calling this method.
8608 *
8609 * Attachments with non-normal hard disks are left as is.
8610 *
8611 * If @a aOnline is @c false then the original hard disks that require implicit
8612 * diffs will be locked for reading. Otherwise it is assumed that they are
8613 * already locked for writing (when the VM was started). Note that in the latter
8614 * case it is responsibility of the caller to lock the newly created diffs for
8615 * writing if this method succeeds.
8616 *
8617 * @param aProgress Progress object to run (must contain at least as
8618 * many operations left as the number of hard disks
8619 * attached).
8620 * @param aOnline Whether the VM was online prior to this operation.
8621 * @param pllRegistriesThatNeedSaving Optional pointer to a list of UUIDs to receive the registry IDs that need saving
8622 *
8623 * @note The progress object is not marked as completed, neither on success nor
8624 * on failure. This is a responsibility of the caller.
8625 *
8626 * @note Locks this object for writing.
8627 */
8628HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
8629 ULONG aWeight,
8630 bool aOnline,
8631 GuidList *pllRegistriesThatNeedSaving)
8632{
8633 LogFlowThisFunc(("aOnline=%d\n", aOnline));
8634
8635 AutoCaller autoCaller(this);
8636 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8637
8638 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8639
8640 /* must be in a protective state because we leave the lock below */
8641 AssertReturn( mData->mMachineState == MachineState_Saving
8642 || mData->mMachineState == MachineState_LiveSnapshotting
8643 || mData->mMachineState == MachineState_RestoringSnapshot
8644 || mData->mMachineState == MachineState_DeletingSnapshot
8645 , E_FAIL);
8646
8647 HRESULT rc = S_OK;
8648
8649 MediumLockListMap lockedMediaOffline;
8650 MediumLockListMap *lockedMediaMap;
8651 if (aOnline)
8652 lockedMediaMap = &mData->mSession.mLockedMedia;
8653 else
8654 lockedMediaMap = &lockedMediaOffline;
8655
8656 try
8657 {
8658 if (!aOnline)
8659 {
8660 /* lock all attached hard disks early to detect "in use"
8661 * situations before creating actual diffs */
8662 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8663 it != mMediaData->mAttachments.end();
8664 ++it)
8665 {
8666 MediumAttachment* pAtt = *it;
8667 if (pAtt->getType() == DeviceType_HardDisk)
8668 {
8669 Medium* pMedium = pAtt->getMedium();
8670 Assert(pMedium);
8671
8672 MediumLockList *pMediumLockList(new MediumLockList());
8673 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
8674 false /* fMediumLockWrite */,
8675 NULL,
8676 *pMediumLockList);
8677 if (FAILED(rc))
8678 {
8679 delete pMediumLockList;
8680 throw rc;
8681 }
8682 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
8683 if (FAILED(rc))
8684 {
8685 throw setError(rc,
8686 tr("Collecting locking information for all attached media failed"));
8687 }
8688 }
8689 }
8690
8691 /* Now lock all media. If this fails, nothing is locked. */
8692 rc = lockedMediaMap->Lock();
8693 if (FAILED(rc))
8694 {
8695 throw setError(rc,
8696 tr("Locking of attached media failed"));
8697 }
8698 }
8699
8700 /* remember the current list (note that we don't use backup() since
8701 * mMediaData may be already backed up) */
8702 MediaData::AttachmentList atts = mMediaData->mAttachments;
8703
8704 /* start from scratch */
8705 mMediaData->mAttachments.clear();
8706
8707 /* go through remembered attachments and create diffs for normal hard
8708 * disks and attach them */
8709 for (MediaData::AttachmentList::const_iterator it = atts.begin();
8710 it != atts.end();
8711 ++it)
8712 {
8713 MediumAttachment* pAtt = *it;
8714
8715 DeviceType_T devType = pAtt->getType();
8716 Medium* pMedium = pAtt->getMedium();
8717
8718 if ( devType != DeviceType_HardDisk
8719 || pMedium == NULL
8720 || pMedium->getType() != MediumType_Normal)
8721 {
8722 /* copy the attachment as is */
8723
8724 /** @todo the progress object created in Console::TakeSnaphot
8725 * only expects operations for hard disks. Later other
8726 * device types need to show up in the progress as well. */
8727 if (devType == DeviceType_HardDisk)
8728 {
8729 if (pMedium == NULL)
8730 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
8731 aWeight); // weight
8732 else
8733 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
8734 pMedium->getBase()->getName().c_str()).raw(),
8735 aWeight); // weight
8736 }
8737
8738 mMediaData->mAttachments.push_back(pAtt);
8739 continue;
8740 }
8741
8742 /* need a diff */
8743 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
8744 pMedium->getBase()->getName().c_str()).raw(),
8745 aWeight); // weight
8746
8747 Utf8Str strFullSnapshotFolder;
8748 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
8749
8750 ComObjPtr<Medium> diff;
8751 diff.createObject();
8752 rc = diff->init(mParent,
8753 pMedium->getPreferredDiffFormat(),
8754 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
8755 pMedium->getFirstRegistryMachineId(), // store the diff in the same registry as the parent
8756 pllRegistriesThatNeedSaving);
8757 if (FAILED(rc)) throw rc;
8758
8759 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
8760 * the push_back? Looks like we're going to leave medium with the
8761 * wrong kind of lock (general issue with if we fail anywhere at all)
8762 * and an orphaned VDI in the snapshots folder. */
8763
8764 /* update the appropriate lock list */
8765 MediumLockList *pMediumLockList;
8766 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
8767 AssertComRCThrowRC(rc);
8768 if (aOnline)
8769 {
8770 rc = pMediumLockList->Update(pMedium, false);
8771 AssertComRCThrowRC(rc);
8772 }
8773
8774 /* leave the lock before the potentially lengthy operation */
8775 alock.leave();
8776 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
8777 pMediumLockList,
8778 NULL /* aProgress */,
8779 true /* aWait */,
8780 pllRegistriesThatNeedSaving);
8781 alock.enter();
8782 if (FAILED(rc)) throw rc;
8783
8784 rc = lockedMediaMap->Unlock();
8785 AssertComRCThrowRC(rc);
8786 rc = pMediumLockList->Append(diff, true);
8787 AssertComRCThrowRC(rc);
8788 rc = lockedMediaMap->Lock();
8789 AssertComRCThrowRC(rc);
8790
8791 rc = diff->addBackReference(mData->mUuid);
8792 AssertComRCThrowRC(rc);
8793
8794 /* add a new attachment */
8795 ComObjPtr<MediumAttachment> attachment;
8796 attachment.createObject();
8797 rc = attachment->init(this,
8798 diff,
8799 pAtt->getControllerName(),
8800 pAtt->getPort(),
8801 pAtt->getDevice(),
8802 DeviceType_HardDisk,
8803 true /* aImplicit */,
8804 pAtt->getBandwidthGroup());
8805 if (FAILED(rc)) throw rc;
8806
8807 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
8808 AssertComRCThrowRC(rc);
8809 mMediaData->mAttachments.push_back(attachment);
8810 }
8811 }
8812 catch (HRESULT aRC) { rc = aRC; }
8813
8814 /* unlock all hard disks we locked */
8815 if (!aOnline)
8816 {
8817 ErrorInfoKeeper eik;
8818
8819 rc = lockedMediaMap->Clear();
8820 AssertComRC(rc);
8821 }
8822
8823 if (FAILED(rc))
8824 {
8825 MultiResult mrc = rc;
8826
8827 mrc = deleteImplicitDiffs(pllRegistriesThatNeedSaving);
8828 }
8829
8830 return rc;
8831}
8832
8833/**
8834 * Deletes implicit differencing hard disks created either by
8835 * #createImplicitDiffs() or by #AttachMedium() and rolls back mMediaData.
8836 *
8837 * Note that to delete hard disks created by #AttachMedium() this method is
8838 * called from #fixupMedia() when the changes are rolled back.
8839 *
8840 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
8841 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
8842 *
8843 * @note Locks this object for writing.
8844 */
8845HRESULT Machine::deleteImplicitDiffs(GuidList *pllRegistriesThatNeedSaving)
8846{
8847 AutoCaller autoCaller(this);
8848 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8849
8850 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8851 LogFlowThisFuncEnter();
8852
8853 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
8854
8855 HRESULT rc = S_OK;
8856
8857 MediaData::AttachmentList implicitAtts;
8858
8859 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
8860
8861 /* enumerate new attachments */
8862 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8863 it != mMediaData->mAttachments.end();
8864 ++it)
8865 {
8866 ComObjPtr<Medium> hd = (*it)->getMedium();
8867 if (hd.isNull())
8868 continue;
8869
8870 if ((*it)->isImplicit())
8871 {
8872 /* deassociate and mark for deletion */
8873 LogFlowThisFunc(("Detaching '%s', pending deletion\n", (*it)->getLogName()));
8874 rc = hd->removeBackReference(mData->mUuid);
8875 AssertComRC(rc);
8876 implicitAtts.push_back(*it);
8877 continue;
8878 }
8879
8880 /* was this hard disk attached before? */
8881 if (!findAttachment(oldAtts, hd))
8882 {
8883 /* no: de-associate */
8884 LogFlowThisFunc(("Detaching '%s', no deletion\n", (*it)->getLogName()));
8885 rc = hd->removeBackReference(mData->mUuid);
8886 AssertComRC(rc);
8887 continue;
8888 }
8889 LogFlowThisFunc(("Not detaching '%s'\n", (*it)->getLogName()));
8890 }
8891
8892 /* rollback hard disk changes */
8893 mMediaData.rollback();
8894
8895 MultiResult mrc(S_OK);
8896
8897 /* delete unused implicit diffs */
8898 if (implicitAtts.size() != 0)
8899 {
8900 /* will leave the lock before the potentially lengthy
8901 * operation, so protect with the special state (unless already
8902 * protected) */
8903 MachineState_T oldState = mData->mMachineState;
8904 if ( oldState != MachineState_Saving
8905 && oldState != MachineState_LiveSnapshotting
8906 && oldState != MachineState_RestoringSnapshot
8907 && oldState != MachineState_DeletingSnapshot
8908 && oldState != MachineState_DeletingSnapshotOnline
8909 && oldState != MachineState_DeletingSnapshotPaused
8910 )
8911 setMachineState(MachineState_SettingUp);
8912
8913 alock.leave();
8914
8915 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
8916 it != implicitAtts.end();
8917 ++it)
8918 {
8919 LogFlowThisFunc(("Deleting '%s'\n", (*it)->getLogName()));
8920 ComObjPtr<Medium> hd = (*it)->getMedium();
8921
8922 rc = hd->deleteStorage(NULL /*aProgress*/, true /*aWait*/,
8923 pllRegistriesThatNeedSaving);
8924 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, (*it)->getLogName(), hd->getLocationFull().c_str() ));
8925 mrc = rc;
8926 }
8927
8928 alock.enter();
8929
8930 if (mData->mMachineState == MachineState_SettingUp)
8931 setMachineState(oldState);
8932 }
8933
8934 return mrc;
8935}
8936
8937/**
8938 * Looks through the given list of media attachments for one with the given parameters
8939 * and returns it, or NULL if not found. The list is a parameter so that backup lists
8940 * can be searched as well if needed.
8941 *
8942 * @param list
8943 * @param aControllerName
8944 * @param aControllerPort
8945 * @param aDevice
8946 * @return
8947 */
8948MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
8949 IN_BSTR aControllerName,
8950 LONG aControllerPort,
8951 LONG aDevice)
8952{
8953 for (MediaData::AttachmentList::const_iterator it = ll.begin();
8954 it != ll.end();
8955 ++it)
8956 {
8957 MediumAttachment *pAttach = *it;
8958 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
8959 return pAttach;
8960 }
8961
8962 return NULL;
8963}
8964
8965/**
8966 * Looks through the given list of media attachments for one with the given parameters
8967 * and returns it, or NULL if not found. The list is a parameter so that backup lists
8968 * can be searched as well if needed.
8969 *
8970 * @param list
8971 * @param aControllerName
8972 * @param aControllerPort
8973 * @param aDevice
8974 * @return
8975 */
8976MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
8977 ComObjPtr<Medium> pMedium)
8978{
8979 for (MediaData::AttachmentList::const_iterator it = ll.begin();
8980 it != ll.end();
8981 ++it)
8982 {
8983 MediumAttachment *pAttach = *it;
8984 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
8985 if (pMediumThis == pMedium)
8986 return pAttach;
8987 }
8988
8989 return NULL;
8990}
8991
8992/**
8993 * Looks through the given list of media attachments for one with the given parameters
8994 * and returns it, or NULL if not found. The list is a parameter so that backup lists
8995 * can be searched as well if needed.
8996 *
8997 * @param list
8998 * @param aControllerName
8999 * @param aControllerPort
9000 * @param aDevice
9001 * @return
9002 */
9003MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
9004 Guid &id)
9005{
9006 for (MediaData::AttachmentList::const_iterator it = ll.begin();
9007 it != ll.end();
9008 ++it)
9009 {
9010 MediumAttachment *pAttach = *it;
9011 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
9012 if (pMediumThis->getId() == id)
9013 return pAttach;
9014 }
9015
9016 return NULL;
9017}
9018
9019/**
9020 * Main implementation for Machine::DetachDevice. This also gets called
9021 * from Machine::prepareUnregister() so it has been taken out for simplicity.
9022 *
9023 * @param pAttach Medium attachment to detach.
9024 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
9025 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
9026 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
9027 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
9028 * @return
9029 */
9030HRESULT Machine::detachDevice(MediumAttachment *pAttach,
9031 AutoWriteLock &writeLock,
9032 Snapshot *pSnapshot,
9033 GuidList *pllRegistriesThatNeedSaving)
9034{
9035 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
9036 DeviceType_T mediumType = pAttach->getType();
9037
9038 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
9039
9040 if (pAttach->isImplicit())
9041 {
9042 /* attempt to implicitly delete the implicitly created diff */
9043
9044 /// @todo move the implicit flag from MediumAttachment to Medium
9045 /// and forbid any hard disk operation when it is implicit. Or maybe
9046 /// a special media state for it to make it even more simple.
9047
9048 Assert(mMediaData.isBackedUp());
9049
9050 /* will leave the lock before the potentially lengthy operation, so
9051 * protect with the special state */
9052 MachineState_T oldState = mData->mMachineState;
9053 setMachineState(MachineState_SettingUp);
9054
9055 writeLock.release();
9056
9057 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/,
9058 pllRegistriesThatNeedSaving);
9059
9060 writeLock.acquire();
9061
9062 setMachineState(oldState);
9063
9064 if (FAILED(rc)) return rc;
9065 }
9066
9067 setModified(IsModified_Storage);
9068 mMediaData.backup();
9069
9070 // we cannot use erase (it) below because backup() above will create
9071 // a copy of the list and make this copy active, but the iterator
9072 // still refers to the original and is not valid for the copy
9073 mMediaData->mAttachments.remove(pAttach);
9074
9075 if (!oldmedium.isNull())
9076 {
9077 // if this is from a snapshot, do not defer detachment to commitMedia()
9078 if (pSnapshot)
9079 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
9080 // else if non-hard disk media, do not defer detachment to commitMedia() either
9081 else if (mediumType != DeviceType_HardDisk)
9082 oldmedium->removeBackReference(mData->mUuid);
9083 }
9084
9085 return S_OK;
9086}
9087
9088/**
9089 * Goes thru all medium attachments of the list and calls detachDevice() on each
9090 * of them and attaches all Medium objects found in the process to the given list,
9091 * depending on cleanupMode.
9092 *
9093 * This gets called from Machine::Unregister, both for the actual Machine and
9094 * the SnapshotMachine objects that might be found in the snapshots.
9095 *
9096 * Requires caller and locking.
9097 *
9098 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
9099 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
9100 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
9101 * otherwise no media get added.
9102 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
9103 * @return
9104 */
9105HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
9106 Snapshot *pSnapshot,
9107 CleanupMode_T cleanupMode,
9108 MediaList &llMedia)
9109{
9110 Assert(isWriteLockOnCurrentThread());
9111
9112 HRESULT rc;
9113
9114 // make a temporary list because detachDevice invalidates iterators into
9115 // mMediaData->mAttachments
9116 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
9117
9118 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
9119 it != llAttachments2.end();
9120 ++it)
9121 {
9122 ComObjPtr<MediumAttachment> pAttach = *it;
9123 ComObjPtr<Medium> pMedium = pAttach->getMedium();
9124
9125 if (!pMedium.isNull())
9126 {
9127 DeviceType_T devType = pMedium->getDeviceType();
9128 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
9129 && devType == DeviceType_HardDisk)
9130 || (cleanupMode == CleanupMode_Full)
9131 )
9132 llMedia.push_back(pMedium);
9133 }
9134
9135 // real machine: then we need to use the proper method
9136 rc = detachDevice(pAttach,
9137 writeLock,
9138 pSnapshot,
9139 NULL /* pfNeedsSaveSettings */);
9140
9141 if (FAILED(rc))
9142 return rc;
9143 }
9144
9145 return S_OK;
9146}
9147
9148/**
9149 * Perform deferred hard disk detachments.
9150 *
9151 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
9152 * backed up).
9153 *
9154 * If @a aOnline is @c true then this method will also unlock the old hard disks
9155 * for which the new implicit diffs were created and will lock these new diffs for
9156 * writing.
9157 *
9158 * @param aOnline Whether the VM was online prior to this operation.
9159 *
9160 * @note Locks this object for writing!
9161 */
9162void Machine::commitMedia(bool aOnline /*= false*/)
9163{
9164 AutoCaller autoCaller(this);
9165 AssertComRCReturnVoid(autoCaller.rc());
9166
9167 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9168
9169 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
9170
9171 HRESULT rc = S_OK;
9172
9173 /* no attach/detach operations -- nothing to do */
9174 if (!mMediaData.isBackedUp())
9175 return;
9176
9177 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
9178 bool fMediaNeedsLocking = false;
9179
9180 /* enumerate new attachments */
9181 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
9182 it != mMediaData->mAttachments.end();
9183 ++it)
9184 {
9185 MediumAttachment *pAttach = *it;
9186
9187 pAttach->commit();
9188
9189 Medium* pMedium = pAttach->getMedium();
9190 bool fImplicit = pAttach->isImplicit();
9191
9192 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
9193 (pMedium) ? pMedium->getName().c_str() : "NULL",
9194 fImplicit));
9195
9196 /** @todo convert all this Machine-based voodoo to MediumAttachment
9197 * based commit logic. */
9198 if (fImplicit)
9199 {
9200 /* convert implicit attachment to normal */
9201 pAttach->setImplicit(false);
9202
9203 if ( aOnline
9204 && pMedium
9205 && pAttach->getType() == DeviceType_HardDisk
9206 )
9207 {
9208 ComObjPtr<Medium> parent = pMedium->getParent();
9209 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
9210
9211 /* update the appropriate lock list */
9212 MediumLockList *pMediumLockList;
9213 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
9214 AssertComRC(rc);
9215 if (pMediumLockList)
9216 {
9217 /* unlock if there's a need to change the locking */
9218 if (!fMediaNeedsLocking)
9219 {
9220 rc = mData->mSession.mLockedMedia.Unlock();
9221 AssertComRC(rc);
9222 fMediaNeedsLocking = true;
9223 }
9224 rc = pMediumLockList->Update(parent, false);
9225 AssertComRC(rc);
9226 rc = pMediumLockList->Append(pMedium, true);
9227 AssertComRC(rc);
9228 }
9229 }
9230
9231 continue;
9232 }
9233
9234 if (pMedium)
9235 {
9236 /* was this medium attached before? */
9237 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
9238 oldIt != oldAtts.end();
9239 ++oldIt)
9240 {
9241 MediumAttachment *pOldAttach = *oldIt;
9242 if (pOldAttach->getMedium() == pMedium)
9243 {
9244 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
9245
9246 /* yes: remove from old to avoid de-association */
9247 oldAtts.erase(oldIt);
9248 break;
9249 }
9250 }
9251 }
9252 }
9253
9254 /* enumerate remaining old attachments and de-associate from the
9255 * current machine state */
9256 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
9257 it != oldAtts.end();
9258 ++it)
9259 {
9260 MediumAttachment *pAttach = *it;
9261 Medium* pMedium = pAttach->getMedium();
9262
9263 /* Detach only hard disks, since DVD/floppy media is detached
9264 * instantly in MountMedium. */
9265 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
9266 {
9267 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
9268
9269 /* now de-associate from the current machine state */
9270 rc = pMedium->removeBackReference(mData->mUuid);
9271 AssertComRC(rc);
9272
9273 if (aOnline)
9274 {
9275 /* unlock since medium is not used anymore */
9276 MediumLockList *pMediumLockList;
9277 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
9278 AssertComRC(rc);
9279 if (pMediumLockList)
9280 {
9281 rc = mData->mSession.mLockedMedia.Remove(pAttach);
9282 AssertComRC(rc);
9283 }
9284 }
9285 }
9286 }
9287
9288 /* take media locks again so that the locking state is consistent */
9289 if (fMediaNeedsLocking)
9290 {
9291 Assert(aOnline);
9292 rc = mData->mSession.mLockedMedia.Lock();
9293 AssertComRC(rc);
9294 }
9295
9296 /* commit the hard disk changes */
9297 mMediaData.commit();
9298
9299 if (isSessionMachine())
9300 {
9301 /* attach new data to the primary machine and reshare it */
9302 mPeer->mMediaData.attach(mMediaData);
9303 }
9304
9305 return;
9306}
9307
9308/**
9309 * Perform deferred deletion of implicitly created diffs.
9310 *
9311 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
9312 * backed up).
9313 *
9314 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
9315 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
9316 *
9317 * @note Locks this object for writing!
9318 */
9319void Machine::rollbackMedia()
9320{
9321 AutoCaller autoCaller(this);
9322 AssertComRCReturnVoid (autoCaller.rc());
9323
9324 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9325
9326 LogFlowThisFunc(("Entering\n"));
9327
9328 HRESULT rc = S_OK;
9329
9330 /* no attach/detach operations -- nothing to do */
9331 if (!mMediaData.isBackedUp())
9332 return;
9333
9334 /* enumerate new attachments */
9335 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
9336 it != mMediaData->mAttachments.end();
9337 ++it)
9338 {
9339 MediumAttachment *pAttach = *it;
9340 /* Fix up the backrefs for DVD/floppy media. */
9341 if (pAttach->getType() != DeviceType_HardDisk)
9342 {
9343 Medium* pMedium = pAttach->getMedium();
9344 if (pMedium)
9345 {
9346 rc = pMedium->removeBackReference(mData->mUuid);
9347 AssertComRC(rc);
9348 }
9349 }
9350
9351 (*it)->rollback();
9352
9353 pAttach = *it;
9354 /* Fix up the backrefs for DVD/floppy media. */
9355 if (pAttach->getType() != DeviceType_HardDisk)
9356 {
9357 Medium* pMedium = pAttach->getMedium();
9358 if (pMedium)
9359 {
9360 rc = pMedium->addBackReference(mData->mUuid);
9361 AssertComRC(rc);
9362 }
9363 }
9364 }
9365
9366 /** @todo convert all this Machine-based voodoo to MediumAttachment
9367 * based rollback logic. */
9368 // @todo r=dj the below totally fails if this gets called from Machine::rollback(),
9369 // which gets called if Machine::registeredInit() fails...
9370 deleteImplicitDiffs(NULL /*pfNeedsSaveSettings*/);
9371
9372 return;
9373}
9374
9375/**
9376 * Returns true if the settings file is located in the directory named exactly
9377 * as the machine; this means, among other things, that the machine directory
9378 * should be auto-renamed.
9379 *
9380 * @param aSettingsDir if not NULL, the full machine settings file directory
9381 * name will be assigned there.
9382 *
9383 * @note Doesn't lock anything.
9384 * @note Not thread safe (must be called from this object's lock).
9385 */
9386bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
9387{
9388 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
9389 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
9390 if (aSettingsDir)
9391 *aSettingsDir = strMachineDirName;
9392 strMachineDirName.stripPath(); // vmname
9393 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
9394 strConfigFileOnly.stripPath() // vmname.vbox
9395 .stripExt(); // vmname
9396
9397 AssertReturn(!strMachineDirName.isEmpty(), false);
9398 AssertReturn(!strConfigFileOnly.isEmpty(), false);
9399
9400 return strMachineDirName == strConfigFileOnly;
9401}
9402
9403/**
9404 * Discards all changes to machine settings.
9405 *
9406 * @param aNotify Whether to notify the direct session about changes or not.
9407 *
9408 * @note Locks objects for writing!
9409 */
9410void Machine::rollback(bool aNotify)
9411{
9412 AutoCaller autoCaller(this);
9413 AssertComRCReturn(autoCaller.rc(), (void)0);
9414
9415 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9416
9417 if (!mStorageControllers.isNull())
9418 {
9419 if (mStorageControllers.isBackedUp())
9420 {
9421 /* unitialize all new devices (absent in the backed up list). */
9422 StorageControllerList::const_iterator it = mStorageControllers->begin();
9423 StorageControllerList *backedList = mStorageControllers.backedUpData();
9424 while (it != mStorageControllers->end())
9425 {
9426 if ( std::find(backedList->begin(), backedList->end(), *it)
9427 == backedList->end()
9428 )
9429 {
9430 (*it)->uninit();
9431 }
9432 ++it;
9433 }
9434
9435 /* restore the list */
9436 mStorageControllers.rollback();
9437 }
9438
9439 /* rollback any changes to devices after restoring the list */
9440 if (mData->flModifications & IsModified_Storage)
9441 {
9442 StorageControllerList::const_iterator it = mStorageControllers->begin();
9443 while (it != mStorageControllers->end())
9444 {
9445 (*it)->rollback();
9446 ++it;
9447 }
9448 }
9449 }
9450
9451 mUserData.rollback();
9452
9453 mHWData.rollback();
9454
9455 if (mData->flModifications & IsModified_Storage)
9456 rollbackMedia();
9457
9458 if (mBIOSSettings)
9459 mBIOSSettings->rollback();
9460
9461 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
9462 mVRDEServer->rollback();
9463
9464 if (mAudioAdapter)
9465 mAudioAdapter->rollback();
9466
9467 if (mUSBController && (mData->flModifications & IsModified_USB))
9468 mUSBController->rollback();
9469
9470 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
9471 mBandwidthControl->rollback();
9472
9473 ComPtr<INetworkAdapter> networkAdapters[RT_ELEMENTS(mNetworkAdapters)];
9474 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
9475 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
9476
9477 if (mData->flModifications & IsModified_NetworkAdapters)
9478 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
9479 if ( mNetworkAdapters[slot]
9480 && mNetworkAdapters[slot]->isModified())
9481 {
9482 mNetworkAdapters[slot]->rollback();
9483 networkAdapters[slot] = mNetworkAdapters[slot];
9484 }
9485
9486 if (mData->flModifications & IsModified_SerialPorts)
9487 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
9488 if ( mSerialPorts[slot]
9489 && mSerialPorts[slot]->isModified())
9490 {
9491 mSerialPorts[slot]->rollback();
9492 serialPorts[slot] = mSerialPorts[slot];
9493 }
9494
9495 if (mData->flModifications & IsModified_ParallelPorts)
9496 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
9497 if ( mParallelPorts[slot]
9498 && mParallelPorts[slot]->isModified())
9499 {
9500 mParallelPorts[slot]->rollback();
9501 parallelPorts[slot] = mParallelPorts[slot];
9502 }
9503
9504 if (aNotify)
9505 {
9506 /* inform the direct session about changes */
9507
9508 ComObjPtr<Machine> that = this;
9509 uint32_t flModifications = mData->flModifications;
9510 alock.leave();
9511
9512 if (flModifications & IsModified_SharedFolders)
9513 that->onSharedFolderChange();
9514
9515 if (flModifications & IsModified_VRDEServer)
9516 that->onVRDEServerChange(/* aRestart */ TRUE);
9517 if (flModifications & IsModified_USB)
9518 that->onUSBControllerChange();
9519
9520 for (ULONG slot = 0; slot < RT_ELEMENTS(networkAdapters); slot ++)
9521 if (networkAdapters[slot])
9522 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
9523 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot ++)
9524 if (serialPorts[slot])
9525 that->onSerialPortChange(serialPorts[slot]);
9526 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot ++)
9527 if (parallelPorts[slot])
9528 that->onParallelPortChange(parallelPorts[slot]);
9529
9530 if (flModifications & IsModified_Storage)
9531 that->onStorageControllerChange();
9532
9533#if 0
9534 if (flModifications & IsModified_BandwidthControl)
9535 that->onBandwidthControlChange();
9536#endif
9537 }
9538}
9539
9540/**
9541 * Commits all the changes to machine settings.
9542 *
9543 * Note that this operation is supposed to never fail.
9544 *
9545 * @note Locks this object and children for writing.
9546 */
9547void Machine::commit()
9548{
9549 AutoCaller autoCaller(this);
9550 AssertComRCReturnVoid(autoCaller.rc());
9551
9552 AutoCaller peerCaller(mPeer);
9553 AssertComRCReturnVoid(peerCaller.rc());
9554
9555 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
9556
9557 /*
9558 * use safe commit to ensure Snapshot machines (that share mUserData)
9559 * will still refer to a valid memory location
9560 */
9561 mUserData.commitCopy();
9562
9563 mHWData.commit();
9564
9565 if (mMediaData.isBackedUp())
9566 commitMedia();
9567
9568 mBIOSSettings->commit();
9569 mVRDEServer->commit();
9570 mAudioAdapter->commit();
9571 mUSBController->commit();
9572 mBandwidthControl->commit();
9573
9574 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
9575 mNetworkAdapters[slot]->commit();
9576 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
9577 mSerialPorts[slot]->commit();
9578 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
9579 mParallelPorts[slot]->commit();
9580
9581 bool commitStorageControllers = false;
9582
9583 if (mStorageControllers.isBackedUp())
9584 {
9585 mStorageControllers.commit();
9586
9587 if (mPeer)
9588 {
9589 AutoWriteLock peerlock(mPeer COMMA_LOCKVAL_SRC_POS);
9590
9591 /* Commit all changes to new controllers (this will reshare data with
9592 * peers for those who have peers) */
9593 StorageControllerList *newList = new StorageControllerList();
9594 StorageControllerList::const_iterator it = mStorageControllers->begin();
9595 while (it != mStorageControllers->end())
9596 {
9597 (*it)->commit();
9598
9599 /* look if this controller has a peer device */
9600 ComObjPtr<StorageController> peer = (*it)->getPeer();
9601 if (!peer)
9602 {
9603 /* no peer means the device is a newly created one;
9604 * create a peer owning data this device share it with */
9605 peer.createObject();
9606 peer->init(mPeer, *it, true /* aReshare */);
9607 }
9608 else
9609 {
9610 /* remove peer from the old list */
9611 mPeer->mStorageControllers->remove(peer);
9612 }
9613 /* and add it to the new list */
9614 newList->push_back(peer);
9615
9616 ++it;
9617 }
9618
9619 /* uninit old peer's controllers that are left */
9620 it = mPeer->mStorageControllers->begin();
9621 while (it != mPeer->mStorageControllers->end())
9622 {
9623 (*it)->uninit();
9624 ++it;
9625 }
9626
9627 /* attach new list of controllers to our peer */
9628 mPeer->mStorageControllers.attach(newList);
9629 }
9630 else
9631 {
9632 /* we have no peer (our parent is the newly created machine);
9633 * just commit changes to devices */
9634 commitStorageControllers = true;
9635 }
9636 }
9637 else
9638 {
9639 /* the list of controllers itself is not changed,
9640 * just commit changes to controllers themselves */
9641 commitStorageControllers = true;
9642 }
9643
9644 if (commitStorageControllers)
9645 {
9646 StorageControllerList::const_iterator it = mStorageControllers->begin();
9647 while (it != mStorageControllers->end())
9648 {
9649 (*it)->commit();
9650 ++it;
9651 }
9652 }
9653
9654 if (isSessionMachine())
9655 {
9656 /* attach new data to the primary machine and reshare it */
9657 mPeer->mUserData.attach(mUserData);
9658 mPeer->mHWData.attach(mHWData);
9659 /* mMediaData is reshared by fixupMedia */
9660 // mPeer->mMediaData.attach(mMediaData);
9661 Assert(mPeer->mMediaData.data() == mMediaData.data());
9662 }
9663}
9664
9665/**
9666 * Copies all the hardware data from the given machine.
9667 *
9668 * Currently, only called when the VM is being restored from a snapshot. In
9669 * particular, this implies that the VM is not running during this method's
9670 * call.
9671 *
9672 * @note This method must be called from under this object's lock.
9673 *
9674 * @note This method doesn't call #commit(), so all data remains backed up and
9675 * unsaved.
9676 */
9677void Machine::copyFrom(Machine *aThat)
9678{
9679 AssertReturnVoid(!isSnapshotMachine());
9680 AssertReturnVoid(aThat->isSnapshotMachine());
9681
9682 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
9683
9684 mHWData.assignCopy(aThat->mHWData);
9685
9686 // create copies of all shared folders (mHWData after attaching a copy
9687 // contains just references to original objects)
9688 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
9689 it != mHWData->mSharedFolders.end();
9690 ++it)
9691 {
9692 ComObjPtr<SharedFolder> folder;
9693 folder.createObject();
9694 HRESULT rc = folder->initCopy(getMachine(), *it);
9695 AssertComRC(rc);
9696 *it = folder;
9697 }
9698
9699 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
9700 mVRDEServer->copyFrom(aThat->mVRDEServer);
9701 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
9702 mUSBController->copyFrom(aThat->mUSBController);
9703 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
9704
9705 /* create private copies of all controllers */
9706 mStorageControllers.backup();
9707 mStorageControllers->clear();
9708 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
9709 it != aThat->mStorageControllers->end();
9710 ++it)
9711 {
9712 ComObjPtr<StorageController> ctrl;
9713 ctrl.createObject();
9714 ctrl->initCopy(this, *it);
9715 mStorageControllers->push_back(ctrl);
9716 }
9717
9718 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
9719 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
9720 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
9721 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
9722 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
9723 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
9724}
9725
9726#ifdef VBOX_WITH_RESOURCE_USAGE_API
9727
9728void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
9729{
9730 AssertReturnVoid(isWriteLockOnCurrentThread());
9731 AssertPtrReturnVoid(aCollector);
9732
9733 pm::CollectorHAL *hal = aCollector->getHAL();
9734 /* Create sub metrics */
9735 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
9736 "Percentage of processor time spent in user mode by the VM process.");
9737 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
9738 "Percentage of processor time spent in kernel mode by the VM process.");
9739 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
9740 "Size of resident portion of VM process in memory.");
9741 /* Create and register base metrics */
9742 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
9743 cpuLoadUser, cpuLoadKernel);
9744 aCollector->registerBaseMetric(cpuLoad);
9745 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
9746 ramUsageUsed);
9747 aCollector->registerBaseMetric(ramUsage);
9748
9749 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
9750 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
9751 new pm::AggregateAvg()));
9752 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
9753 new pm::AggregateMin()));
9754 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
9755 new pm::AggregateMax()));
9756 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
9757 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
9758 new pm::AggregateAvg()));
9759 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
9760 new pm::AggregateMin()));
9761 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
9762 new pm::AggregateMax()));
9763
9764 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
9765 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
9766 new pm::AggregateAvg()));
9767 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
9768 new pm::AggregateMin()));
9769 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
9770 new pm::AggregateMax()));
9771
9772
9773 /* Guest metrics */
9774 mGuestHAL = new pm::CollectorGuestHAL(this, hal);
9775
9776 /* Create sub metrics */
9777 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
9778 "Percentage of processor time spent in user mode as seen by the guest.");
9779 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
9780 "Percentage of processor time spent in kernel mode as seen by the guest.");
9781 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
9782 "Percentage of processor time spent idling as seen by the guest.");
9783
9784 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
9785 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
9786 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
9787 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
9788 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
9789 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
9790
9791 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
9792
9793 /* Create and register base metrics */
9794 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mGuestHAL, aMachine, guestLoadUser, guestLoadKernel, guestLoadIdle);
9795 aCollector->registerBaseMetric(guestCpuLoad);
9796
9797 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mGuestHAL, aMachine, guestMemTotal, guestMemFree, guestMemBalloon, guestMemShared,
9798 guestMemCache, guestPagedTotal);
9799 aCollector->registerBaseMetric(guestCpuMem);
9800
9801 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
9802 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
9803 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
9804 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
9805
9806 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
9807 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
9808 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
9809 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
9810
9811 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
9812 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
9813 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
9814 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
9815
9816 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
9817 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
9818 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
9819 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
9820
9821 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
9822 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
9823 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
9824 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
9825
9826 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
9827 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
9828 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
9829 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
9830
9831 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
9832 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
9833 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
9834 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
9835
9836 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
9837 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
9838 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
9839 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
9840
9841 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
9842 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
9843 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
9844 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
9845}
9846
9847void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
9848{
9849 AssertReturnVoid(isWriteLockOnCurrentThread());
9850
9851 if (aCollector)
9852 {
9853 aCollector->unregisterMetricsFor(aMachine);
9854 aCollector->unregisterBaseMetricsFor(aMachine);
9855 }
9856
9857 if (mGuestHAL)
9858 {
9859 delete mGuestHAL;
9860 mGuestHAL = NULL;
9861 }
9862}
9863
9864#endif /* VBOX_WITH_RESOURCE_USAGE_API */
9865
9866
9867////////////////////////////////////////////////////////////////////////////////
9868
9869DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
9870
9871HRESULT SessionMachine::FinalConstruct()
9872{
9873 LogFlowThisFunc(("\n"));
9874
9875#if defined(RT_OS_WINDOWS)
9876 mIPCSem = NULL;
9877#elif defined(RT_OS_OS2)
9878 mIPCSem = NULLHANDLE;
9879#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
9880 mIPCSem = -1;
9881#else
9882# error "Port me!"
9883#endif
9884
9885 return S_OK;
9886}
9887
9888void SessionMachine::FinalRelease()
9889{
9890 LogFlowThisFunc(("\n"));
9891
9892 uninit(Uninit::Unexpected);
9893}
9894
9895/**
9896 * @note Must be called only by Machine::openSession() from its own write lock.
9897 */
9898HRESULT SessionMachine::init(Machine *aMachine)
9899{
9900 LogFlowThisFuncEnter();
9901 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
9902
9903 AssertReturn(aMachine, E_INVALIDARG);
9904
9905 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
9906
9907 /* Enclose the state transition NotReady->InInit->Ready */
9908 AutoInitSpan autoInitSpan(this);
9909 AssertReturn(autoInitSpan.isOk(), E_FAIL);
9910
9911 /* create the interprocess semaphore */
9912#if defined(RT_OS_WINDOWS)
9913 mIPCSemName = aMachine->mData->m_strConfigFileFull;
9914 for (size_t i = 0; i < mIPCSemName.length(); i++)
9915 if (mIPCSemName.raw()[i] == '\\')
9916 mIPCSemName.raw()[i] = '/';
9917 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
9918 ComAssertMsgRet(mIPCSem,
9919 ("Cannot create IPC mutex '%ls', err=%d",
9920 mIPCSemName.raw(), ::GetLastError()),
9921 E_FAIL);
9922#elif defined(RT_OS_OS2)
9923 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
9924 aMachine->mData->mUuid.raw());
9925 mIPCSemName = ipcSem;
9926 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
9927 ComAssertMsgRet(arc == NO_ERROR,
9928 ("Cannot create IPC mutex '%s', arc=%ld",
9929 ipcSem.c_str(), arc),
9930 E_FAIL);
9931#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
9932# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
9933# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
9934 /** @todo Check that this still works correctly. */
9935 AssertCompileSize(key_t, 8);
9936# else
9937 AssertCompileSize(key_t, 4);
9938# endif
9939 key_t key;
9940 mIPCSem = -1;
9941 mIPCKey = "0";
9942 for (uint32_t i = 0; i < 1 << 24; i++)
9943 {
9944 key = ((uint32_t)'V' << 24) | i;
9945 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
9946 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
9947 {
9948 mIPCSem = sem;
9949 if (sem >= 0)
9950 mIPCKey = BstrFmt("%u", key);
9951 break;
9952 }
9953 }
9954# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
9955 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
9956 char *pszSemName = NULL;
9957 RTStrUtf8ToCurrentCP(&pszSemName, semName);
9958 key_t key = ::ftok(pszSemName, 'V');
9959 RTStrFree(pszSemName);
9960
9961 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
9962# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
9963
9964 int errnoSave = errno;
9965 if (mIPCSem < 0 && errnoSave == ENOSYS)
9966 {
9967 setError(E_FAIL,
9968 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
9969 "support for SysV IPC. Check the host kernel configuration for "
9970 "CONFIG_SYSVIPC=y"));
9971 return E_FAIL;
9972 }
9973 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
9974 * the IPC semaphores */
9975 if (mIPCSem < 0 && errnoSave == ENOSPC)
9976 {
9977#ifdef RT_OS_LINUX
9978 setError(E_FAIL,
9979 tr("Cannot create IPC semaphore because the system limit for the "
9980 "maximum number of semaphore sets (SEMMNI), or the system wide "
9981 "maximum number of semaphores (SEMMNS) would be exceeded. The "
9982 "current set of SysV IPC semaphores can be determined from "
9983 "the file /proc/sysvipc/sem"));
9984#else
9985 setError(E_FAIL,
9986 tr("Cannot create IPC semaphore because the system-imposed limit "
9987 "on the maximum number of allowed semaphores or semaphore "
9988 "identifiers system-wide would be exceeded"));
9989#endif
9990 return E_FAIL;
9991 }
9992 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
9993 E_FAIL);
9994 /* set the initial value to 1 */
9995 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
9996 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
9997 E_FAIL);
9998#else
9999# error "Port me!"
10000#endif
10001
10002 /* memorize the peer Machine */
10003 unconst(mPeer) = aMachine;
10004 /* share the parent pointer */
10005 unconst(mParent) = aMachine->mParent;
10006
10007 /* take the pointers to data to share */
10008 mData.share(aMachine->mData);
10009 mSSData.share(aMachine->mSSData);
10010
10011 mUserData.share(aMachine->mUserData);
10012 mHWData.share(aMachine->mHWData);
10013 mMediaData.share(aMachine->mMediaData);
10014
10015 mStorageControllers.allocate();
10016 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
10017 it != aMachine->mStorageControllers->end();
10018 ++it)
10019 {
10020 ComObjPtr<StorageController> ctl;
10021 ctl.createObject();
10022 ctl->init(this, *it);
10023 mStorageControllers->push_back(ctl);
10024 }
10025
10026 unconst(mBIOSSettings).createObject();
10027 mBIOSSettings->init(this, aMachine->mBIOSSettings);
10028 /* create another VRDEServer object that will be mutable */
10029 unconst(mVRDEServer).createObject();
10030 mVRDEServer->init(this, aMachine->mVRDEServer);
10031 /* create another audio adapter object that will be mutable */
10032 unconst(mAudioAdapter).createObject();
10033 mAudioAdapter->init(this, aMachine->mAudioAdapter);
10034 /* create a list of serial ports that will be mutable */
10035 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
10036 {
10037 unconst(mSerialPorts[slot]).createObject();
10038 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
10039 }
10040 /* create a list of parallel ports that will be mutable */
10041 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
10042 {
10043 unconst(mParallelPorts[slot]).createObject();
10044 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
10045 }
10046 /* create another USB controller object that will be mutable */
10047 unconst(mUSBController).createObject();
10048 mUSBController->init(this, aMachine->mUSBController);
10049
10050 /* create a list of network adapters that will be mutable */
10051 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
10052 {
10053 unconst(mNetworkAdapters[slot]).createObject();
10054 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
10055 }
10056
10057 /* create another bandwidth control object that will be mutable */
10058 unconst(mBandwidthControl).createObject();
10059 mBandwidthControl->init(this, aMachine->mBandwidthControl);
10060
10061 /* default is to delete saved state on Saved -> PoweredOff transition */
10062 mRemoveSavedState = true;
10063
10064 /* Confirm a successful initialization when it's the case */
10065 autoInitSpan.setSucceeded();
10066
10067 LogFlowThisFuncLeave();
10068 return S_OK;
10069}
10070
10071/**
10072 * Uninitializes this session object. If the reason is other than
10073 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
10074 *
10075 * @param aReason uninitialization reason
10076 *
10077 * @note Locks mParent + this object for writing.
10078 */
10079void SessionMachine::uninit(Uninit::Reason aReason)
10080{
10081 LogFlowThisFuncEnter();
10082 LogFlowThisFunc(("reason=%d\n", aReason));
10083
10084 /*
10085 * Strongly reference ourselves to prevent this object deletion after
10086 * mData->mSession.mMachine.setNull() below (which can release the last
10087 * reference and call the destructor). Important: this must be done before
10088 * accessing any members (and before AutoUninitSpan that does it as well).
10089 * This self reference will be released as the very last step on return.
10090 */
10091 ComObjPtr<SessionMachine> selfRef = this;
10092
10093 /* Enclose the state transition Ready->InUninit->NotReady */
10094 AutoUninitSpan autoUninitSpan(this);
10095 if (autoUninitSpan.uninitDone())
10096 {
10097 LogFlowThisFunc(("Already uninitialized\n"));
10098 LogFlowThisFuncLeave();
10099 return;
10100 }
10101
10102 if (autoUninitSpan.initFailed())
10103 {
10104 /* We've been called by init() because it's failed. It's not really
10105 * necessary (nor it's safe) to perform the regular uninit sequence
10106 * below, the following is enough.
10107 */
10108 LogFlowThisFunc(("Initialization failed.\n"));
10109#if defined(RT_OS_WINDOWS)
10110 if (mIPCSem)
10111 ::CloseHandle(mIPCSem);
10112 mIPCSem = NULL;
10113#elif defined(RT_OS_OS2)
10114 if (mIPCSem != NULLHANDLE)
10115 ::DosCloseMutexSem(mIPCSem);
10116 mIPCSem = NULLHANDLE;
10117#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
10118 if (mIPCSem >= 0)
10119 ::semctl(mIPCSem, 0, IPC_RMID);
10120 mIPCSem = -1;
10121# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
10122 mIPCKey = "0";
10123# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
10124#else
10125# error "Port me!"
10126#endif
10127 uninitDataAndChildObjects();
10128 mData.free();
10129 unconst(mParent) = NULL;
10130 unconst(mPeer) = NULL;
10131 LogFlowThisFuncLeave();
10132 return;
10133 }
10134
10135 MachineState_T lastState;
10136 {
10137 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
10138 lastState = mData->mMachineState;
10139 }
10140 NOREF(lastState);
10141
10142#ifdef VBOX_WITH_USB
10143 // release all captured USB devices, but do this before requesting the locks below
10144 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
10145 {
10146 /* Console::captureUSBDevices() is called in the VM process only after
10147 * setting the machine state to Starting or Restoring.
10148 * Console::detachAllUSBDevices() will be called upon successful
10149 * termination. So, we need to release USB devices only if there was
10150 * an abnormal termination of a running VM.
10151 *
10152 * This is identical to SessionMachine::DetachAllUSBDevices except
10153 * for the aAbnormal argument. */
10154 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
10155 AssertComRC(rc);
10156 NOREF(rc);
10157
10158 USBProxyService *service = mParent->host()->usbProxyService();
10159 if (service)
10160 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
10161 }
10162#endif /* VBOX_WITH_USB */
10163
10164 // we need to lock this object in uninit() because the lock is shared
10165 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
10166 // and others need mParent lock, and USB needs host lock.
10167 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
10168
10169 // Trigger async cleanup tasks, avoid doing things here which are not
10170 // vital to be done immediately and maybe need more locks. This calls
10171 // Machine::unregisterMetrics().
10172 mParent->onMachineUninit(mPeer);
10173
10174 if (aReason == Uninit::Abnormal)
10175 {
10176 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
10177 Global::IsOnlineOrTransient(lastState)));
10178
10179 /* reset the state to Aborted */
10180 if (mData->mMachineState != MachineState_Aborted)
10181 setMachineState(MachineState_Aborted);
10182 }
10183
10184 // any machine settings modified?
10185 if (mData->flModifications)
10186 {
10187 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
10188 rollback(false /* aNotify */);
10189 }
10190
10191 Assert(mSnapshotData.mStateFilePath.isEmpty() || !mSnapshotData.mSnapshot);
10192 if (!mSnapshotData.mStateFilePath.isEmpty())
10193 {
10194 LogWarningThisFunc(("canceling failed save state request!\n"));
10195 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
10196 }
10197 else if (!mSnapshotData.mSnapshot.isNull())
10198 {
10199 LogWarningThisFunc(("canceling untaken snapshot!\n"));
10200
10201 /* delete all differencing hard disks created (this will also attach
10202 * their parents back by rolling back mMediaData) */
10203 rollbackMedia();
10204 /* delete the saved state file (it might have been already created) */
10205 if (mSnapshotData.mSnapshot->stateFilePath().length())
10206 RTFileDelete(mSnapshotData.mSnapshot->stateFilePath().c_str());
10207
10208 mSnapshotData.mSnapshot->uninit();
10209 }
10210
10211 if (!mData->mSession.mType.isEmpty())
10212 {
10213 /* mType is not null when this machine's process has been started by
10214 * Machine::launchVMProcess(), therefore it is our child. We
10215 * need to queue the PID to reap the process (and avoid zombies on
10216 * Linux). */
10217 Assert(mData->mSession.mPid != NIL_RTPROCESS);
10218 mParent->addProcessToReap(mData->mSession.mPid);
10219 }
10220
10221 mData->mSession.mPid = NIL_RTPROCESS;
10222
10223 if (aReason == Uninit::Unexpected)
10224 {
10225 /* Uninitialization didn't come from #checkForDeath(), so tell the
10226 * client watcher thread to update the set of machines that have open
10227 * sessions. */
10228 mParent->updateClientWatcher();
10229 }
10230
10231 /* uninitialize all remote controls */
10232 if (mData->mSession.mRemoteControls.size())
10233 {
10234 LogFlowThisFunc(("Closing remote sessions (%d):\n",
10235 mData->mSession.mRemoteControls.size()));
10236
10237 Data::Session::RemoteControlList::iterator it =
10238 mData->mSession.mRemoteControls.begin();
10239 while (it != mData->mSession.mRemoteControls.end())
10240 {
10241 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
10242 HRESULT rc = (*it)->Uninitialize();
10243 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
10244 if (FAILED(rc))
10245 LogWarningThisFunc(("Forgot to close the remote session?\n"));
10246 ++it;
10247 }
10248 mData->mSession.mRemoteControls.clear();
10249 }
10250
10251 /*
10252 * An expected uninitialization can come only from #checkForDeath().
10253 * Otherwise it means that something's gone really wrong (for example,
10254 * the Session implementation has released the VirtualBox reference
10255 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
10256 * etc). However, it's also possible, that the client releases the IPC
10257 * semaphore correctly (i.e. before it releases the VirtualBox reference),
10258 * but the VirtualBox release event comes first to the server process.
10259 * This case is practically possible, so we should not assert on an
10260 * unexpected uninit, just log a warning.
10261 */
10262
10263 if ((aReason == Uninit::Unexpected))
10264 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
10265
10266 if (aReason != Uninit::Normal)
10267 {
10268 mData->mSession.mDirectControl.setNull();
10269 }
10270 else
10271 {
10272 /* this must be null here (see #OnSessionEnd()) */
10273 Assert(mData->mSession.mDirectControl.isNull());
10274 Assert(mData->mSession.mState == SessionState_Unlocking);
10275 Assert(!mData->mSession.mProgress.isNull());
10276 }
10277 if (mData->mSession.mProgress)
10278 {
10279 if (aReason == Uninit::Normal)
10280 mData->mSession.mProgress->notifyComplete(S_OK);
10281 else
10282 mData->mSession.mProgress->notifyComplete(E_FAIL,
10283 COM_IIDOF(ISession),
10284 getComponentName(),
10285 tr("The VM session was aborted"));
10286 mData->mSession.mProgress.setNull();
10287 }
10288
10289 /* remove the association between the peer machine and this session machine */
10290 Assert( (SessionMachine*)mData->mSession.mMachine == this
10291 || aReason == Uninit::Unexpected);
10292
10293 /* reset the rest of session data */
10294 mData->mSession.mMachine.setNull();
10295 mData->mSession.mState = SessionState_Unlocked;
10296 mData->mSession.mType.setNull();
10297
10298 /* close the interprocess semaphore before leaving the exclusive lock */
10299#if defined(RT_OS_WINDOWS)
10300 if (mIPCSem)
10301 ::CloseHandle(mIPCSem);
10302 mIPCSem = NULL;
10303#elif defined(RT_OS_OS2)
10304 if (mIPCSem != NULLHANDLE)
10305 ::DosCloseMutexSem(mIPCSem);
10306 mIPCSem = NULLHANDLE;
10307#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
10308 if (mIPCSem >= 0)
10309 ::semctl(mIPCSem, 0, IPC_RMID);
10310 mIPCSem = -1;
10311# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
10312 mIPCKey = "0";
10313# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
10314#else
10315# error "Port me!"
10316#endif
10317
10318 /* fire an event */
10319 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
10320
10321 uninitDataAndChildObjects();
10322
10323 /* free the essential data structure last */
10324 mData.free();
10325
10326#if 1 /** @todo Please review this change! (bird) */
10327 /* drop the exclusive lock before setting the below two to NULL */
10328 multilock.release();
10329#else
10330 /* leave the exclusive lock before setting the below two to NULL */
10331 multilock.leave();
10332#endif
10333
10334 unconst(mParent) = NULL;
10335 unconst(mPeer) = NULL;
10336
10337 LogFlowThisFuncLeave();
10338}
10339
10340// util::Lockable interface
10341////////////////////////////////////////////////////////////////////////////////
10342
10343/**
10344 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
10345 * with the primary Machine instance (mPeer).
10346 */
10347RWLockHandle *SessionMachine::lockHandle() const
10348{
10349 AssertReturn(mPeer != NULL, NULL);
10350 return mPeer->lockHandle();
10351}
10352
10353// IInternalMachineControl methods
10354////////////////////////////////////////////////////////////////////////////////
10355
10356/**
10357 * @note Locks this object for writing.
10358 */
10359STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
10360{
10361 AutoCaller autoCaller(this);
10362 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10363
10364 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10365
10366 mRemoveSavedState = aRemove;
10367
10368 return S_OK;
10369}
10370
10371/**
10372 * @note Locks the same as #setMachineState() does.
10373 */
10374STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
10375{
10376 return setMachineState(aMachineState);
10377}
10378
10379/**
10380 * @note Locks this object for reading.
10381 */
10382STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
10383{
10384 AutoCaller autoCaller(this);
10385 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10386
10387 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10388
10389#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
10390 mIPCSemName.cloneTo(aId);
10391 return S_OK;
10392#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
10393# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
10394 mIPCKey.cloneTo(aId);
10395# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
10396 mData->m_strConfigFileFull.cloneTo(aId);
10397# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
10398 return S_OK;
10399#else
10400# error "Port me!"
10401#endif
10402}
10403
10404/**
10405 * @note Locks this object for writing.
10406 */
10407STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
10408{
10409 LogFlowThisFunc(("aProgress=%p\n", aProgress));
10410 AutoCaller autoCaller(this);
10411 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10412
10413 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10414
10415 if (mData->mSession.mState != SessionState_Locked)
10416 return VBOX_E_INVALID_OBJECT_STATE;
10417
10418 if (!mData->mSession.mProgress.isNull())
10419 mData->mSession.mProgress->setOtherProgressObject(aProgress);
10420
10421 LogFlowThisFunc(("returns S_OK.\n"));
10422 return S_OK;
10423}
10424
10425
10426/**
10427 * @note Locks this object for writing.
10428 */
10429STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
10430{
10431 AutoCaller autoCaller(this);
10432 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10433
10434 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10435
10436 if (mData->mSession.mState != SessionState_Locked)
10437 return VBOX_E_INVALID_OBJECT_STATE;
10438
10439 /* Finalize the openRemoteSession progress object. */
10440 if (mData->mSession.mProgress)
10441 {
10442 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
10443 mData->mSession.mProgress.setNull();
10444 }
10445
10446 if (SUCCEEDED((HRESULT)iResult))
10447 {
10448#ifdef VBOX_WITH_RESOURCE_USAGE_API
10449 /* The VM has been powered up successfully, so it makes sense
10450 * now to offer the performance metrics for a running machine
10451 * object. Doing it earlier wouldn't be safe. */
10452 registerMetrics(mParent->performanceCollector(), mPeer,
10453 mData->mSession.mPid);
10454#endif /* VBOX_WITH_RESOURCE_USAGE_API */
10455 }
10456
10457 return S_OK;
10458}
10459
10460/**
10461 * Goes through the USB filters of the given machine to see if the given
10462 * device matches any filter or not.
10463 *
10464 * @note Locks the same as USBController::hasMatchingFilter() does.
10465 */
10466STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
10467 BOOL *aMatched,
10468 ULONG *aMaskedIfs)
10469{
10470 LogFlowThisFunc(("\n"));
10471
10472 CheckComArgNotNull(aUSBDevice);
10473 CheckComArgOutPointerValid(aMatched);
10474
10475 AutoCaller autoCaller(this);
10476 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10477
10478#ifdef VBOX_WITH_USB
10479 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
10480#else
10481 NOREF(aUSBDevice);
10482 NOREF(aMaskedIfs);
10483 *aMatched = FALSE;
10484#endif
10485
10486 return S_OK;
10487}
10488
10489/**
10490 * @note Locks the same as Host::captureUSBDevice() does.
10491 */
10492STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
10493{
10494 LogFlowThisFunc(("\n"));
10495
10496 AutoCaller autoCaller(this);
10497 AssertComRCReturnRC(autoCaller.rc());
10498
10499#ifdef VBOX_WITH_USB
10500 /* if captureDeviceForVM() fails, it must have set extended error info */
10501 MultiResult rc = mParent->host()->checkUSBProxyService();
10502 if (FAILED(rc)) return rc;
10503
10504 USBProxyService *service = mParent->host()->usbProxyService();
10505 AssertReturn(service, E_FAIL);
10506 return service->captureDeviceForVM(this, Guid(aId).ref());
10507#else
10508 NOREF(aId);
10509 return E_NOTIMPL;
10510#endif
10511}
10512
10513/**
10514 * @note Locks the same as Host::detachUSBDevice() does.
10515 */
10516STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
10517{
10518 LogFlowThisFunc(("\n"));
10519
10520 AutoCaller autoCaller(this);
10521 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10522
10523#ifdef VBOX_WITH_USB
10524 USBProxyService *service = mParent->host()->usbProxyService();
10525 AssertReturn(service, E_FAIL);
10526 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
10527#else
10528 NOREF(aId);
10529 NOREF(aDone);
10530 return E_NOTIMPL;
10531#endif
10532}
10533
10534/**
10535 * Inserts all machine filters to the USB proxy service and then calls
10536 * Host::autoCaptureUSBDevices().
10537 *
10538 * Called by Console from the VM process upon VM startup.
10539 *
10540 * @note Locks what called methods lock.
10541 */
10542STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
10543{
10544 LogFlowThisFunc(("\n"));
10545
10546 AutoCaller autoCaller(this);
10547 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10548
10549#ifdef VBOX_WITH_USB
10550 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
10551 AssertComRC(rc);
10552 NOREF(rc);
10553
10554 USBProxyService *service = mParent->host()->usbProxyService();
10555 AssertReturn(service, E_FAIL);
10556 return service->autoCaptureDevicesForVM(this);
10557#else
10558 return S_OK;
10559#endif
10560}
10561
10562/**
10563 * Removes all machine filters from the USB proxy service and then calls
10564 * Host::detachAllUSBDevices().
10565 *
10566 * Called by Console from the VM process upon normal VM termination or by
10567 * SessionMachine::uninit() upon abnormal VM termination (from under the
10568 * Machine/SessionMachine lock).
10569 *
10570 * @note Locks what called methods lock.
10571 */
10572STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
10573{
10574 LogFlowThisFunc(("\n"));
10575
10576 AutoCaller autoCaller(this);
10577 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10578
10579#ifdef VBOX_WITH_USB
10580 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
10581 AssertComRC(rc);
10582 NOREF(rc);
10583
10584 USBProxyService *service = mParent->host()->usbProxyService();
10585 AssertReturn(service, E_FAIL);
10586 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
10587#else
10588 NOREF(aDone);
10589 return S_OK;
10590#endif
10591}
10592
10593/**
10594 * @note Locks this object for writing.
10595 */
10596STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
10597 IProgress **aProgress)
10598{
10599 LogFlowThisFuncEnter();
10600
10601 AssertReturn(aSession, E_INVALIDARG);
10602 AssertReturn(aProgress, E_INVALIDARG);
10603
10604 AutoCaller autoCaller(this);
10605
10606 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
10607 /*
10608 * We don't assert below because it might happen that a non-direct session
10609 * informs us it is closed right after we've been uninitialized -- it's ok.
10610 */
10611 if (FAILED(autoCaller.rc())) return autoCaller.rc();
10612
10613 /* get IInternalSessionControl interface */
10614 ComPtr<IInternalSessionControl> control(aSession);
10615
10616 ComAssertRet(!control.isNull(), E_INVALIDARG);
10617
10618 /* Creating a Progress object requires the VirtualBox lock, and
10619 * thus locking it here is required by the lock order rules. */
10620 AutoMultiWriteLock2 alock(mParent->lockHandle(), this->lockHandle() COMMA_LOCKVAL_SRC_POS);
10621
10622 if (control == mData->mSession.mDirectControl)
10623 {
10624 ComAssertRet(aProgress, E_POINTER);
10625
10626 /* The direct session is being normally closed by the client process
10627 * ----------------------------------------------------------------- */
10628
10629 /* go to the closing state (essential for all open*Session() calls and
10630 * for #checkForDeath()) */
10631 Assert(mData->mSession.mState == SessionState_Locked);
10632 mData->mSession.mState = SessionState_Unlocking;
10633
10634 /* set direct control to NULL to release the remote instance */
10635 mData->mSession.mDirectControl.setNull();
10636 LogFlowThisFunc(("Direct control is set to NULL\n"));
10637
10638 if (mData->mSession.mProgress)
10639 {
10640 /* finalize the progress, someone might wait if a frontend
10641 * closes the session before powering on the VM. */
10642 mData->mSession.mProgress->notifyComplete(E_FAIL,
10643 COM_IIDOF(ISession),
10644 getComponentName(),
10645 tr("The VM session was closed before any attempt to power it on"));
10646 mData->mSession.mProgress.setNull();
10647 }
10648
10649 /* Create the progress object the client will use to wait until
10650 * #checkForDeath() is called to uninitialize this session object after
10651 * it releases the IPC semaphore.
10652 * Note! Because we're "reusing" mProgress here, this must be a proxy
10653 * object just like for openRemoteSession. */
10654 Assert(mData->mSession.mProgress.isNull());
10655 ComObjPtr<ProgressProxy> progress;
10656 progress.createObject();
10657 ComPtr<IUnknown> pPeer(mPeer);
10658 progress->init(mParent, pPeer,
10659 Bstr(tr("Closing session")).raw(),
10660 FALSE /* aCancelable */);
10661 progress.queryInterfaceTo(aProgress);
10662 mData->mSession.mProgress = progress;
10663 }
10664 else
10665 {
10666 /* the remote session is being normally closed */
10667 Data::Session::RemoteControlList::iterator it =
10668 mData->mSession.mRemoteControls.begin();
10669 while (it != mData->mSession.mRemoteControls.end())
10670 {
10671 if (control == *it)
10672 break;
10673 ++it;
10674 }
10675 BOOL found = it != mData->mSession.mRemoteControls.end();
10676 ComAssertMsgRet(found, ("The session is not found in the session list!"),
10677 E_INVALIDARG);
10678 mData->mSession.mRemoteControls.remove(*it);
10679 }
10680
10681 LogFlowThisFuncLeave();
10682 return S_OK;
10683}
10684
10685/**
10686 * @note Locks this object for writing.
10687 */
10688STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
10689{
10690 LogFlowThisFuncEnter();
10691
10692 CheckComArgOutPointerValid(aProgress);
10693 CheckComArgOutPointerValid(aStateFilePath);
10694
10695 AutoCaller autoCaller(this);
10696 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10697
10698 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10699
10700 AssertReturn( mData->mMachineState == MachineState_Paused
10701 && mSnapshotData.mLastState == MachineState_Null
10702 && mSnapshotData.mStateFilePath.isEmpty(),
10703 E_FAIL);
10704
10705 /* create a progress object to track operation completion */
10706 ComObjPtr<Progress> pProgress;
10707 pProgress.createObject();
10708 pProgress->init(getVirtualBox(),
10709 static_cast<IMachine *>(this) /* aInitiator */,
10710 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
10711 FALSE /* aCancelable */);
10712
10713 Bstr stateFilePath;
10714 /* stateFilePath is null when the machine is not running */
10715 if (mData->mMachineState == MachineState_Paused)
10716 {
10717 Utf8Str strFullSnapshotFolder;
10718 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10719 stateFilePath = Utf8StrFmt("%s%c{%RTuuid}.sav",
10720 strFullSnapshotFolder.c_str(),
10721 RTPATH_DELIMITER,
10722 mData->mUuid.raw());
10723 }
10724
10725 /* fill in the snapshot data */
10726 mSnapshotData.mLastState = mData->mMachineState;
10727 mSnapshotData.mStateFilePath = stateFilePath;
10728 mSnapshotData.mProgress = pProgress;
10729
10730 /* set the state to Saving (this is expected by Console::SaveState()) */
10731 setMachineState(MachineState_Saving);
10732
10733 stateFilePath.cloneTo(aStateFilePath);
10734 pProgress.queryInterfaceTo(aProgress);
10735
10736 return S_OK;
10737}
10738
10739/**
10740 * @note Locks mParent + this object for writing.
10741 */
10742STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
10743{
10744 LogFlowThisFunc(("\n"));
10745
10746 AutoCaller autoCaller(this);
10747 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10748
10749 /* endSavingState() need mParent lock */
10750 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
10751
10752 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
10753 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
10754 && mSnapshotData.mLastState != MachineState_Null
10755 && !mSnapshotData.mStateFilePath.isEmpty(),
10756 E_FAIL);
10757
10758 /*
10759 * On failure, set the state to the state we had when BeginSavingState()
10760 * was called (this is expected by Console::SaveState() and the associated
10761 * task). On success the VM process already changed the state to
10762 * MachineState_Saved, so no need to do anything.
10763 */
10764 if (FAILED(iResult))
10765 setMachineState(mSnapshotData.mLastState);
10766
10767 return endSavingState(iResult, aErrMsg);
10768}
10769
10770/**
10771 * @note Locks this object for writing.
10772 */
10773STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
10774{
10775 LogFlowThisFunc(("\n"));
10776
10777 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
10778
10779 AutoCaller autoCaller(this);
10780 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10781
10782 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10783
10784 AssertReturn( mData->mMachineState == MachineState_PoweredOff
10785 || mData->mMachineState == MachineState_Teleported
10786 || mData->mMachineState == MachineState_Aborted
10787 , E_FAIL); /** @todo setError. */
10788
10789 Utf8Str stateFilePathFull = aSavedStateFile;
10790 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
10791 if (RT_FAILURE(vrc))
10792 return setError(VBOX_E_FILE_ERROR,
10793 tr("Invalid saved state file path '%ls' (%Rrc)"),
10794 aSavedStateFile,
10795 vrc);
10796
10797 mSSData->mStateFilePath = stateFilePathFull;
10798
10799 /* The below setMachineState() will detect the state transition and will
10800 * update the settings file */
10801
10802 return setMachineState(MachineState_Saved);
10803}
10804
10805STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
10806 ComSafeArrayOut(BSTR, aValues),
10807 ComSafeArrayOut(LONG64, aTimestamps),
10808 ComSafeArrayOut(BSTR, aFlags))
10809{
10810 LogFlowThisFunc(("\n"));
10811
10812#ifdef VBOX_WITH_GUEST_PROPS
10813 using namespace guestProp;
10814
10815 AutoCaller autoCaller(this);
10816 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10817
10818 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10819
10820 AssertReturn(!ComSafeArrayOutIsNull(aNames), E_POINTER);
10821 AssertReturn(!ComSafeArrayOutIsNull(aValues), E_POINTER);
10822 AssertReturn(!ComSafeArrayOutIsNull(aTimestamps), E_POINTER);
10823 AssertReturn(!ComSafeArrayOutIsNull(aFlags), E_POINTER);
10824
10825 size_t cEntries = mHWData->mGuestProperties.size();
10826 com::SafeArray<BSTR> names(cEntries);
10827 com::SafeArray<BSTR> values(cEntries);
10828 com::SafeArray<LONG64> timestamps(cEntries);
10829 com::SafeArray<BSTR> flags(cEntries);
10830 unsigned i = 0;
10831 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
10832 it != mHWData->mGuestProperties.end();
10833 ++it)
10834 {
10835 char szFlags[MAX_FLAGS_LEN + 1];
10836 it->strName.cloneTo(&names[i]);
10837 it->strValue.cloneTo(&values[i]);
10838 timestamps[i] = it->mTimestamp;
10839 /* If it is NULL, keep it NULL. */
10840 if (it->mFlags)
10841 {
10842 writeFlags(it->mFlags, szFlags);
10843 Bstr(szFlags).cloneTo(&flags[i]);
10844 }
10845 else
10846 flags[i] = NULL;
10847 ++i;
10848 }
10849 names.detachTo(ComSafeArrayOutArg(aNames));
10850 values.detachTo(ComSafeArrayOutArg(aValues));
10851 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
10852 flags.detachTo(ComSafeArrayOutArg(aFlags));
10853 return S_OK;
10854#else
10855 ReturnComNotImplemented();
10856#endif
10857}
10858
10859STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
10860 IN_BSTR aValue,
10861 LONG64 aTimestamp,
10862 IN_BSTR aFlags)
10863{
10864 LogFlowThisFunc(("\n"));
10865
10866#ifdef VBOX_WITH_GUEST_PROPS
10867 using namespace guestProp;
10868
10869 CheckComArgStrNotEmptyOrNull(aName);
10870 if (aValue != NULL && (!VALID_PTR(aValue) || !VALID_PTR(aFlags)))
10871 return E_POINTER; /* aValue can be NULL to indicate deletion */
10872
10873 try
10874 {
10875 /*
10876 * Convert input up front.
10877 */
10878 Utf8Str utf8Name(aName);
10879 uint32_t fFlags = NILFLAG;
10880 if (aFlags)
10881 {
10882 Utf8Str utf8Flags(aFlags);
10883 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
10884 AssertRCReturn(vrc, E_INVALIDARG);
10885 }
10886
10887 /*
10888 * Now grab the object lock, validate the state and do the update.
10889 */
10890 AutoCaller autoCaller(this);
10891 if (FAILED(autoCaller.rc())) return autoCaller.rc();
10892
10893 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10894
10895 switch (mData->mMachineState)
10896 {
10897 case MachineState_Paused:
10898 case MachineState_Running:
10899 case MachineState_Teleporting:
10900 case MachineState_TeleportingPausedVM:
10901 case MachineState_LiveSnapshotting:
10902 case MachineState_DeletingSnapshotOnline:
10903 case MachineState_DeletingSnapshotPaused:
10904 case MachineState_Saving:
10905 break;
10906
10907 default:
10908#ifndef DEBUG_sunlover
10909 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
10910 VBOX_E_INVALID_VM_STATE);
10911#else
10912 return VBOX_E_INVALID_VM_STATE;
10913#endif
10914 }
10915
10916 setModified(IsModified_MachineData);
10917 mHWData.backup();
10918
10919 /** @todo r=bird: The careful memory handling doesn't work out here because
10920 * the catch block won't undo any damage we've done. So, if push_back throws
10921 * bad_alloc then you've lost the value.
10922 *
10923 * Another thing. Doing a linear search here isn't extremely efficient, esp.
10924 * since values that changes actually bubbles to the end of the list. Using
10925 * something that has an efficient lookup and can tolerate a bit of updates
10926 * would be nice. RTStrSpace is one suggestion (it's not perfect). Some
10927 * combination of RTStrCache (for sharing names and getting uniqueness into
10928 * the bargain) and hash/tree is another. */
10929 for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();
10930 iter != mHWData->mGuestProperties.end();
10931 ++iter)
10932 if (utf8Name == iter->strName)
10933 {
10934 mHWData->mGuestProperties.erase(iter);
10935 mData->mGuestPropertiesModified = TRUE;
10936 break;
10937 }
10938 if (aValue != NULL)
10939 {
10940 HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };
10941 mHWData->mGuestProperties.push_back(property);
10942 mData->mGuestPropertiesModified = TRUE;
10943 }
10944
10945 /*
10946 * Send a callback notification if appropriate
10947 */
10948 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
10949 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
10950 RTSTR_MAX,
10951 utf8Name.c_str(),
10952 RTSTR_MAX, NULL)
10953 )
10954 {
10955 alock.leave();
10956
10957 mParent->onGuestPropertyChange(mData->mUuid,
10958 aName,
10959 aValue,
10960 aFlags);
10961 }
10962 }
10963 catch (...)
10964 {
10965 return VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
10966 }
10967 return S_OK;
10968#else
10969 ReturnComNotImplemented();
10970#endif
10971}
10972
10973// public methods only for internal purposes
10974/////////////////////////////////////////////////////////////////////////////
10975
10976/**
10977 * Called from the client watcher thread to check for expected or unexpected
10978 * death of the client process that has a direct session to this machine.
10979 *
10980 * On Win32 and on OS/2, this method is called only when we've got the
10981 * mutex (i.e. the client has either died or terminated normally) so it always
10982 * returns @c true (the client is terminated, the session machine is
10983 * uninitialized).
10984 *
10985 * On other platforms, the method returns @c true if the client process has
10986 * terminated normally or abnormally and the session machine was uninitialized,
10987 * and @c false if the client process is still alive.
10988 *
10989 * @note Locks this object for writing.
10990 */
10991bool SessionMachine::checkForDeath()
10992{
10993 Uninit::Reason reason;
10994 bool terminated = false;
10995
10996 /* Enclose autoCaller with a block because calling uninit() from under it
10997 * will deadlock. */
10998 {
10999 AutoCaller autoCaller(this);
11000 if (!autoCaller.isOk())
11001 {
11002 /* return true if not ready, to cause the client watcher to exclude
11003 * the corresponding session from watching */
11004 LogFlowThisFunc(("Already uninitialized!\n"));
11005 return true;
11006 }
11007
11008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11009
11010 /* Determine the reason of death: if the session state is Closing here,
11011 * everything is fine. Otherwise it means that the client did not call
11012 * OnSessionEnd() before it released the IPC semaphore. This may happen
11013 * either because the client process has abnormally terminated, or
11014 * because it simply forgot to call ISession::Close() before exiting. We
11015 * threat the latter also as an abnormal termination (see
11016 * Session::uninit() for details). */
11017 reason = mData->mSession.mState == SessionState_Unlocking ?
11018 Uninit::Normal :
11019 Uninit::Abnormal;
11020
11021#if defined(RT_OS_WINDOWS)
11022
11023 AssertMsg(mIPCSem, ("semaphore must be created"));
11024
11025 /* release the IPC mutex */
11026 ::ReleaseMutex(mIPCSem);
11027
11028 terminated = true;
11029
11030#elif defined(RT_OS_OS2)
11031
11032 AssertMsg(mIPCSem, ("semaphore must be created"));
11033
11034 /* release the IPC mutex */
11035 ::DosReleaseMutexSem(mIPCSem);
11036
11037 terminated = true;
11038
11039#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11040
11041 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
11042
11043 int val = ::semctl(mIPCSem, 0, GETVAL);
11044 if (val > 0)
11045 {
11046 /* the semaphore is signaled, meaning the session is terminated */
11047 terminated = true;
11048 }
11049
11050#else
11051# error "Port me!"
11052#endif
11053
11054 } /* AutoCaller block */
11055
11056 if (terminated)
11057 uninit(reason);
11058
11059 return terminated;
11060}
11061
11062/**
11063 * @note Locks this object for reading.
11064 */
11065HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
11066{
11067 LogFlowThisFunc(("\n"));
11068
11069 AutoCaller autoCaller(this);
11070 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11071
11072 ComPtr<IInternalSessionControl> directControl;
11073 {
11074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11075 directControl = mData->mSession.mDirectControl;
11076 }
11077
11078 /* ignore notifications sent after #OnSessionEnd() is called */
11079 if (!directControl)
11080 return S_OK;
11081
11082 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
11083}
11084
11085/**
11086 * @note Locks this object for reading.
11087 */
11088HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
11089 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
11090{
11091 LogFlowThisFunc(("\n"));
11092
11093 AutoCaller autoCaller(this);
11094 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11095
11096 ComPtr<IInternalSessionControl> directControl;
11097 {
11098 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11099 directControl = mData->mSession.mDirectControl;
11100 }
11101
11102 /* ignore notifications sent after #OnSessionEnd() is called */
11103 if (!directControl)
11104 return S_OK;
11105 /*
11106 * instead acting like callback we ask IVirtualBox deliver corresponding event
11107 */
11108
11109 mParent->onNatRedirectChange(getId(), ulSlot, aNatRuleRemove, aRuleName, aProto, aHostIp, aHostPort, aGuestIp, aGuestPort);
11110 return S_OK;
11111}
11112
11113/**
11114 * @note Locks this object for reading.
11115 */
11116HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
11117{
11118 LogFlowThisFunc(("\n"));
11119
11120 AutoCaller autoCaller(this);
11121 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11122
11123 ComPtr<IInternalSessionControl> directControl;
11124 {
11125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11126 directControl = mData->mSession.mDirectControl;
11127 }
11128
11129 /* ignore notifications sent after #OnSessionEnd() is called */
11130 if (!directControl)
11131 return S_OK;
11132
11133 return directControl->OnSerialPortChange(serialPort);
11134}
11135
11136/**
11137 * @note Locks this object for reading.
11138 */
11139HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
11140{
11141 LogFlowThisFunc(("\n"));
11142
11143 AutoCaller autoCaller(this);
11144 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11145
11146 ComPtr<IInternalSessionControl> directControl;
11147 {
11148 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11149 directControl = mData->mSession.mDirectControl;
11150 }
11151
11152 /* ignore notifications sent after #OnSessionEnd() is called */
11153 if (!directControl)
11154 return S_OK;
11155
11156 return directControl->OnParallelPortChange(parallelPort);
11157}
11158
11159/**
11160 * @note Locks this object for reading.
11161 */
11162HRESULT SessionMachine::onStorageControllerChange()
11163{
11164 LogFlowThisFunc(("\n"));
11165
11166 AutoCaller autoCaller(this);
11167 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11168
11169 ComPtr<IInternalSessionControl> directControl;
11170 {
11171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11172 directControl = mData->mSession.mDirectControl;
11173 }
11174
11175 /* ignore notifications sent after #OnSessionEnd() is called */
11176 if (!directControl)
11177 return S_OK;
11178
11179 return directControl->OnStorageControllerChange();
11180}
11181
11182/**
11183 * @note Locks this object for reading.
11184 */
11185HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
11186{
11187 LogFlowThisFunc(("\n"));
11188
11189 AutoCaller autoCaller(this);
11190 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11191
11192 ComPtr<IInternalSessionControl> directControl;
11193 {
11194 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11195 directControl = mData->mSession.mDirectControl;
11196 }
11197
11198 /* ignore notifications sent after #OnSessionEnd() is called */
11199 if (!directControl)
11200 return S_OK;
11201
11202 return directControl->OnMediumChange(aAttachment, aForce);
11203}
11204
11205/**
11206 * @note Locks this object for reading.
11207 */
11208HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
11209{
11210 LogFlowThisFunc(("\n"));
11211
11212 AutoCaller autoCaller(this);
11213 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
11214
11215 ComPtr<IInternalSessionControl> directControl;
11216 {
11217 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11218 directControl = mData->mSession.mDirectControl;
11219 }
11220
11221 /* ignore notifications sent after #OnSessionEnd() is called */
11222 if (!directControl)
11223 return S_OK;
11224
11225 return directControl->OnCPUChange(aCPU, aRemove);
11226}
11227
11228HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
11229{
11230 LogFlowThisFunc(("\n"));
11231
11232 AutoCaller autoCaller(this);
11233 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
11234
11235 ComPtr<IInternalSessionControl> directControl;
11236 {
11237 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11238 directControl = mData->mSession.mDirectControl;
11239 }
11240
11241 /* ignore notifications sent after #OnSessionEnd() is called */
11242 if (!directControl)
11243 return S_OK;
11244
11245 return directControl->OnCPUExecutionCapChange(aExecutionCap);
11246}
11247
11248/**
11249 * @note Locks this object for reading.
11250 */
11251HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
11252{
11253 LogFlowThisFunc(("\n"));
11254
11255 AutoCaller autoCaller(this);
11256 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11257
11258 ComPtr<IInternalSessionControl> directControl;
11259 {
11260 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11261 directControl = mData->mSession.mDirectControl;
11262 }
11263
11264 /* ignore notifications sent after #OnSessionEnd() is called */
11265 if (!directControl)
11266 return S_OK;
11267
11268 return directControl->OnVRDEServerChange(aRestart);
11269}
11270
11271/**
11272 * @note Locks this object for reading.
11273 */
11274HRESULT SessionMachine::onUSBControllerChange()
11275{
11276 LogFlowThisFunc(("\n"));
11277
11278 AutoCaller autoCaller(this);
11279 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11280
11281 ComPtr<IInternalSessionControl> directControl;
11282 {
11283 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11284 directControl = mData->mSession.mDirectControl;
11285 }
11286
11287 /* ignore notifications sent after #OnSessionEnd() is called */
11288 if (!directControl)
11289 return S_OK;
11290
11291 return directControl->OnUSBControllerChange();
11292}
11293
11294/**
11295 * @note Locks this object for reading.
11296 */
11297HRESULT SessionMachine::onSharedFolderChange()
11298{
11299 LogFlowThisFunc(("\n"));
11300
11301 AutoCaller autoCaller(this);
11302 AssertComRCReturnRC(autoCaller.rc());
11303
11304 ComPtr<IInternalSessionControl> directControl;
11305 {
11306 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11307 directControl = mData->mSession.mDirectControl;
11308 }
11309
11310 /* ignore notifications sent after #OnSessionEnd() is called */
11311 if (!directControl)
11312 return S_OK;
11313
11314 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
11315}
11316
11317/**
11318 * @note Locks this object for reading.
11319 */
11320HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
11321{
11322 LogFlowThisFunc(("\n"));
11323
11324 AutoCaller autoCaller(this);
11325 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
11326
11327 ComPtr<IInternalSessionControl> directControl;
11328 {
11329 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11330 directControl = mData->mSession.mDirectControl;
11331 }
11332
11333 /* ignore notifications sent after #OnSessionEnd() is called */
11334 if (!directControl)
11335 return S_OK;
11336
11337 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
11338}
11339
11340/**
11341 * Returns @c true if this machine's USB controller reports it has a matching
11342 * filter for the given USB device and @c false otherwise.
11343 *
11344 * @note Caller must have requested machine read lock.
11345 */
11346bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
11347{
11348 AutoCaller autoCaller(this);
11349 /* silently return if not ready -- this method may be called after the
11350 * direct machine session has been called */
11351 if (!autoCaller.isOk())
11352 return false;
11353
11354
11355#ifdef VBOX_WITH_USB
11356 switch (mData->mMachineState)
11357 {
11358 case MachineState_Starting:
11359 case MachineState_Restoring:
11360 case MachineState_TeleportingIn:
11361 case MachineState_Paused:
11362 case MachineState_Running:
11363 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
11364 * elsewhere... */
11365 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
11366 default: break;
11367 }
11368#else
11369 NOREF(aDevice);
11370 NOREF(aMaskedIfs);
11371#endif
11372 return false;
11373}
11374
11375/**
11376 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
11377 */
11378HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
11379 IVirtualBoxErrorInfo *aError,
11380 ULONG aMaskedIfs)
11381{
11382 LogFlowThisFunc(("\n"));
11383
11384 AutoCaller autoCaller(this);
11385
11386 /* This notification may happen after the machine object has been
11387 * uninitialized (the session was closed), so don't assert. */
11388 if (FAILED(autoCaller.rc())) return autoCaller.rc();
11389
11390 ComPtr<IInternalSessionControl> directControl;
11391 {
11392 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11393 directControl = mData->mSession.mDirectControl;
11394 }
11395
11396 /* fail on notifications sent after #OnSessionEnd() is called, it is
11397 * expected by the caller */
11398 if (!directControl)
11399 return E_FAIL;
11400
11401 /* No locks should be held at this point. */
11402 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
11403 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
11404
11405 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
11406}
11407
11408/**
11409 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
11410 */
11411HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
11412 IVirtualBoxErrorInfo *aError)
11413{
11414 LogFlowThisFunc(("\n"));
11415
11416 AutoCaller autoCaller(this);
11417
11418 /* This notification may happen after the machine object has been
11419 * uninitialized (the session was closed), so don't assert. */
11420 if (FAILED(autoCaller.rc())) return autoCaller.rc();
11421
11422 ComPtr<IInternalSessionControl> directControl;
11423 {
11424 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11425 directControl = mData->mSession.mDirectControl;
11426 }
11427
11428 /* fail on notifications sent after #OnSessionEnd() is called, it is
11429 * expected by the caller */
11430 if (!directControl)
11431 return E_FAIL;
11432
11433 /* No locks should be held at this point. */
11434 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
11435 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
11436
11437 return directControl->OnUSBDeviceDetach(aId, aError);
11438}
11439
11440// protected methods
11441/////////////////////////////////////////////////////////////////////////////
11442
11443/**
11444 * Helper method to finalize saving the state.
11445 *
11446 * @note Must be called from under this object's lock.
11447 *
11448 * @param aRc S_OK if the snapshot has been taken successfully
11449 * @param aErrMsg human readable error message for failure
11450 *
11451 * @note Locks mParent + this objects for writing.
11452 */
11453HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
11454{
11455 LogFlowThisFuncEnter();
11456
11457 AutoCaller autoCaller(this);
11458 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11459
11460 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11461
11462 HRESULT rc = S_OK;
11463
11464 if (SUCCEEDED(aRc))
11465 {
11466 mSSData->mStateFilePath = mSnapshotData.mStateFilePath;
11467
11468 /* save all VM settings */
11469 rc = saveSettings(NULL);
11470 // no need to check whether VirtualBox.xml needs saving also since
11471 // we can't have a name change pending at this point
11472 }
11473 else
11474 {
11475 /* delete the saved state file (it might have been already created) */
11476 RTFileDelete(mSnapshotData.mStateFilePath.c_str());
11477 }
11478
11479 /* notify the progress object about operation completion */
11480 Assert(mSnapshotData.mProgress);
11481 if (SUCCEEDED(aRc))
11482 mSnapshotData.mProgress->notifyComplete(S_OK);
11483 else
11484 {
11485 if (aErrMsg.length())
11486 mSnapshotData.mProgress->notifyComplete(aRc,
11487 COM_IIDOF(ISession),
11488 getComponentName(),
11489 aErrMsg.c_str());
11490 else
11491 mSnapshotData.mProgress->notifyComplete(aRc);
11492 }
11493
11494 /* clear out the temporary saved state data */
11495 mSnapshotData.mLastState = MachineState_Null;
11496 mSnapshotData.mStateFilePath.setNull();
11497 mSnapshotData.mProgress.setNull();
11498
11499 LogFlowThisFuncLeave();
11500 return rc;
11501}
11502
11503/**
11504 * Locks the attached media.
11505 *
11506 * All attached hard disks are locked for writing and DVD/floppy are locked for
11507 * reading. Parents of attached hard disks (if any) are locked for reading.
11508 *
11509 * This method also performs accessibility check of all media it locks: if some
11510 * media is inaccessible, the method will return a failure and a bunch of
11511 * extended error info objects per each inaccessible medium.
11512 *
11513 * Note that this method is atomic: if it returns a success, all media are
11514 * locked as described above; on failure no media is locked at all (all
11515 * succeeded individual locks will be undone).
11516 *
11517 * This method is intended to be called when the machine is in Starting or
11518 * Restoring state and asserts otherwise.
11519 *
11520 * The locks made by this method must be undone by calling #unlockMedia() when
11521 * no more needed.
11522 */
11523HRESULT SessionMachine::lockMedia()
11524{
11525 AutoCaller autoCaller(this);
11526 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11527
11528 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11529
11530 AssertReturn( mData->mMachineState == MachineState_Starting
11531 || mData->mMachineState == MachineState_Restoring
11532 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
11533 /* bail out if trying to lock things with already set up locking */
11534 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
11535
11536 MultiResult mrc(S_OK);
11537
11538 /* Collect locking information for all medium objects attached to the VM. */
11539 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11540 it != mMediaData->mAttachments.end();
11541 ++it)
11542 {
11543 MediumAttachment* pAtt = *it;
11544 DeviceType_T devType = pAtt->getType();
11545 Medium *pMedium = pAtt->getMedium();
11546
11547 MediumLockList *pMediumLockList(new MediumLockList());
11548 // There can be attachments without a medium (floppy/dvd), and thus
11549 // it's impossible to create a medium lock list. It still makes sense
11550 // to have the empty medium lock list in the map in case a medium is
11551 // attached later.
11552 if (pMedium != NULL)
11553 {
11554 MediumType_T mediumType = pMedium->getType();
11555 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
11556 || mediumType == MediumType_Shareable;
11557 bool fIsVitalImage = (devType == DeviceType_HardDisk);
11558
11559 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
11560 !fIsReadOnlyLock /* fMediumLockWrite */,
11561 NULL,
11562 *pMediumLockList);
11563 if (FAILED(mrc))
11564 {
11565 delete pMediumLockList;
11566 mData->mSession.mLockedMedia.Clear();
11567 break;
11568 }
11569 }
11570
11571 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
11572 if (FAILED(rc))
11573 {
11574 mData->mSession.mLockedMedia.Clear();
11575 mrc = setError(rc,
11576 tr("Collecting locking information for all attached media failed"));
11577 break;
11578 }
11579 }
11580
11581 if (SUCCEEDED(mrc))
11582 {
11583 /* Now lock all media. If this fails, nothing is locked. */
11584 HRESULT rc = mData->mSession.mLockedMedia.Lock();
11585 if (FAILED(rc))
11586 {
11587 mrc = setError(rc,
11588 tr("Locking of attached media failed"));
11589 }
11590 }
11591
11592 return mrc;
11593}
11594
11595/**
11596 * Undoes the locks made by by #lockMedia().
11597 */
11598void SessionMachine::unlockMedia()
11599{
11600 AutoCaller autoCaller(this);
11601 AssertComRCReturnVoid(autoCaller.rc());
11602
11603 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11604
11605 /* we may be holding important error info on the current thread;
11606 * preserve it */
11607 ErrorInfoKeeper eik;
11608
11609 HRESULT rc = mData->mSession.mLockedMedia.Clear();
11610 AssertComRC(rc);
11611}
11612
11613/**
11614 * Helper to change the machine state (reimplementation).
11615 *
11616 * @note Locks this object for writing.
11617 */
11618HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
11619{
11620 LogFlowThisFuncEnter();
11621 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
11622
11623 AutoCaller autoCaller(this);
11624 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11625
11626 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11627
11628 MachineState_T oldMachineState = mData->mMachineState;
11629
11630 AssertMsgReturn(oldMachineState != aMachineState,
11631 ("oldMachineState=%s, aMachineState=%s\n",
11632 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
11633 E_FAIL);
11634
11635 HRESULT rc = S_OK;
11636
11637 int stsFlags = 0;
11638 bool deleteSavedState = false;
11639
11640 /* detect some state transitions */
11641
11642 if ( ( oldMachineState == MachineState_Saved
11643 && aMachineState == MachineState_Restoring)
11644 || ( ( oldMachineState == MachineState_PoweredOff
11645 || oldMachineState == MachineState_Teleported
11646 || oldMachineState == MachineState_Aborted
11647 )
11648 && ( aMachineState == MachineState_TeleportingIn
11649 || aMachineState == MachineState_Starting
11650 )
11651 )
11652 )
11653 {
11654 /* The EMT thread is about to start */
11655
11656 /* Nothing to do here for now... */
11657
11658 /// @todo NEWMEDIA don't let mDVDDrive and other children
11659 /// change anything when in the Starting/Restoring state
11660 }
11661 else if ( ( oldMachineState == MachineState_Running
11662 || oldMachineState == MachineState_Paused
11663 || oldMachineState == MachineState_Teleporting
11664 || oldMachineState == MachineState_LiveSnapshotting
11665 || oldMachineState == MachineState_Stuck
11666 || oldMachineState == MachineState_Starting
11667 || oldMachineState == MachineState_Stopping
11668 || oldMachineState == MachineState_Saving
11669 || oldMachineState == MachineState_Restoring
11670 || oldMachineState == MachineState_TeleportingPausedVM
11671 || oldMachineState == MachineState_TeleportingIn
11672 )
11673 && ( aMachineState == MachineState_PoweredOff
11674 || aMachineState == MachineState_Saved
11675 || aMachineState == MachineState_Teleported
11676 || aMachineState == MachineState_Aborted
11677 )
11678 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
11679 * snapshot */
11680 && ( mSnapshotData.mSnapshot.isNull()
11681 || mSnapshotData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
11682 )
11683 )
11684 {
11685 /* The EMT thread has just stopped, unlock attached media. Note that as
11686 * opposed to locking that is done from Console, we do unlocking here
11687 * because the VM process may have aborted before having a chance to
11688 * properly unlock all media it locked. */
11689
11690 unlockMedia();
11691 }
11692
11693 if (oldMachineState == MachineState_Restoring)
11694 {
11695 if (aMachineState != MachineState_Saved)
11696 {
11697 /*
11698 * delete the saved state file once the machine has finished
11699 * restoring from it (note that Console sets the state from
11700 * Restoring to Saved if the VM couldn't restore successfully,
11701 * to give the user an ability to fix an error and retry --
11702 * we keep the saved state file in this case)
11703 */
11704 deleteSavedState = true;
11705 }
11706 }
11707 else if ( oldMachineState == MachineState_Saved
11708 && ( aMachineState == MachineState_PoweredOff
11709 || aMachineState == MachineState_Aborted
11710 || aMachineState == MachineState_Teleported
11711 )
11712 )
11713 {
11714 /*
11715 * delete the saved state after Console::ForgetSavedState() is called
11716 * or if the VM process (owning a direct VM session) crashed while the
11717 * VM was Saved
11718 */
11719
11720 /// @todo (dmik)
11721 // Not sure that deleting the saved state file just because of the
11722 // client death before it attempted to restore the VM is a good
11723 // thing. But when it crashes we need to go to the Aborted state
11724 // which cannot have the saved state file associated... The only
11725 // way to fix this is to make the Aborted condition not a VM state
11726 // but a bool flag: i.e., when a crash occurs, set it to true and
11727 // change the state to PoweredOff or Saved depending on the
11728 // saved state presence.
11729
11730 deleteSavedState = true;
11731 mData->mCurrentStateModified = TRUE;
11732 stsFlags |= SaveSTS_CurStateModified;
11733 }
11734
11735 if ( aMachineState == MachineState_Starting
11736 || aMachineState == MachineState_Restoring
11737 || aMachineState == MachineState_TeleportingIn
11738 )
11739 {
11740 /* set the current state modified flag to indicate that the current
11741 * state is no more identical to the state in the
11742 * current snapshot */
11743 if (!mData->mCurrentSnapshot.isNull())
11744 {
11745 mData->mCurrentStateModified = TRUE;
11746 stsFlags |= SaveSTS_CurStateModified;
11747 }
11748 }
11749
11750 if (deleteSavedState)
11751 {
11752 if (mRemoveSavedState)
11753 {
11754 Assert(!mSSData->mStateFilePath.isEmpty());
11755 RTFileDelete(mSSData->mStateFilePath.c_str());
11756 }
11757 mSSData->mStateFilePath.setNull();
11758 stsFlags |= SaveSTS_StateFilePath;
11759 }
11760
11761 /* redirect to the underlying peer machine */
11762 mPeer->setMachineState(aMachineState);
11763
11764 if ( aMachineState == MachineState_PoweredOff
11765 || aMachineState == MachineState_Teleported
11766 || aMachineState == MachineState_Aborted
11767 || aMachineState == MachineState_Saved)
11768 {
11769 /* the machine has stopped execution
11770 * (or the saved state file was adopted) */
11771 stsFlags |= SaveSTS_StateTimeStamp;
11772 }
11773
11774 if ( ( oldMachineState == MachineState_PoweredOff
11775 || oldMachineState == MachineState_Aborted
11776 || oldMachineState == MachineState_Teleported
11777 )
11778 && aMachineState == MachineState_Saved)
11779 {
11780 /* the saved state file was adopted */
11781 Assert(!mSSData->mStateFilePath.isEmpty());
11782 stsFlags |= SaveSTS_StateFilePath;
11783 }
11784
11785#ifdef VBOX_WITH_GUEST_PROPS
11786 if ( aMachineState == MachineState_PoweredOff
11787 || aMachineState == MachineState_Aborted
11788 || aMachineState == MachineState_Teleported)
11789 {
11790 /* Make sure any transient guest properties get removed from the
11791 * property store on shutdown. */
11792
11793 HWData::GuestPropertyList::iterator it;
11794 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
11795 if (!fNeedsSaving)
11796 for (it = mHWData->mGuestProperties.begin();
11797 it != mHWData->mGuestProperties.end(); ++it)
11798 if (it->mFlags & guestProp::TRANSIENT)
11799 {
11800 fNeedsSaving = true;
11801 break;
11802 }
11803 if (fNeedsSaving)
11804 {
11805 mData->mCurrentStateModified = TRUE;
11806 stsFlags |= SaveSTS_CurStateModified;
11807 SaveSettings(); // @todo r=dj why the public method? why first SaveSettings and then saveStateSettings?
11808 }
11809 }
11810#endif
11811
11812 rc = saveStateSettings(stsFlags);
11813
11814 if ( ( oldMachineState != MachineState_PoweredOff
11815 && oldMachineState != MachineState_Aborted
11816 && oldMachineState != MachineState_Teleported
11817 )
11818 && ( aMachineState == MachineState_PoweredOff
11819 || aMachineState == MachineState_Aborted
11820 || aMachineState == MachineState_Teleported
11821 )
11822 )
11823 {
11824 /* we've been shut down for any reason */
11825 /* no special action so far */
11826 }
11827
11828 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
11829 LogFlowThisFuncLeave();
11830 return rc;
11831}
11832
11833/**
11834 * Sends the current machine state value to the VM process.
11835 *
11836 * @note Locks this object for reading, then calls a client process.
11837 */
11838HRESULT SessionMachine::updateMachineStateOnClient()
11839{
11840 AutoCaller autoCaller(this);
11841 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11842
11843 ComPtr<IInternalSessionControl> directControl;
11844 {
11845 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11846 AssertReturn(!!mData, E_FAIL);
11847 directControl = mData->mSession.mDirectControl;
11848
11849 /* directControl may be already set to NULL here in #OnSessionEnd()
11850 * called too early by the direct session process while there is still
11851 * some operation (like deleting the snapshot) in progress. The client
11852 * process in this case is waiting inside Session::close() for the
11853 * "end session" process object to complete, while #uninit() called by
11854 * #checkForDeath() on the Watcher thread is waiting for the pending
11855 * operation to complete. For now, we accept this inconsistent behavior
11856 * and simply do nothing here. */
11857
11858 if (mData->mSession.mState == SessionState_Unlocking)
11859 return S_OK;
11860
11861 AssertReturn(!directControl.isNull(), E_FAIL);
11862 }
11863
11864 return directControl->UpdateMachineState(mData->mMachineState);
11865}
Note: See TracBrowser for help on using the repository browser.

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