VirtualBox

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

Last change on this file since 35814 was 35757, checked in by vboxsync, 14 years ago

Main: do not fail loading machine settings if a shared folder path is not absolute (that breaks importing OVF and machine folders from other hosts since the rules about what constitutes an absolute path differ between host OSes). Instead, issue a runtime warning if a shared folder path is not absolute or does not exist on the host when the VM is powered up.

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

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