VirtualBox

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

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

Main/Machine: Reject any attempt to open a VM config with the same UUID as an already existing VM, e.g. caused by an attempt to open the config of an already known VM again. Much better to let people know what's going on instead of failing with Medium object conflicts (and without a human readable error message).

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