VirtualBox

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

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

Main/BandwidthControl: Don't use direct references to bandwidth group objects in the medium attachment object. It will point to the old uninitialized instance if the settings are changed. Uses the name of the bandwidth group now instead without changing the public interface to make a backport to 4.0 possible

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