VirtualBox

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

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

Main,NAT: Managing port-forwarding at runtime. (xTracker/#4835).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 383.4 KB
Line 
1/* $Id: MachineImpl.cpp 33825 2010-11-08 10:16:25Z 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 && !defined(RT_OS_LINUX)
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::onNATRedirectRuleChange(INetworkAdapter *networkAdapter, BOOL aNatRuleRemove, IN_BSTR aRuleName,
10929 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
10930{
10931 LogFlowThisFunc(("\n"));
10932
10933 AutoCaller autoCaller(this);
10934 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10935
10936 ComPtr<IInternalSessionControl> directControl;
10937 {
10938 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10939 directControl = mData->mSession.mDirectControl;
10940 }
10941
10942 /* ignore notifications sent after #OnSessionEnd() is called */
10943 if (!directControl)
10944 return S_OK;
10945
10946 return directControl->OnNATRedirectRuleChange(networkAdapter, aNatRuleRemove, aRuleName, aProto, aHostIp, aHostPort, aGuestIp, aGuestPort);
10947}
10948
10949/**
10950 * @note Locks this object for reading.
10951 */
10952HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
10953{
10954 LogFlowThisFunc(("\n"));
10955
10956 AutoCaller autoCaller(this);
10957 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10958
10959 ComPtr<IInternalSessionControl> directControl;
10960 {
10961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10962 directControl = mData->mSession.mDirectControl;
10963 }
10964
10965 /* ignore notifications sent after #OnSessionEnd() is called */
10966 if (!directControl)
10967 return S_OK;
10968
10969 return directControl->OnSerialPortChange(serialPort);
10970}
10971
10972/**
10973 * @note Locks this object for reading.
10974 */
10975HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
10976{
10977 LogFlowThisFunc(("\n"));
10978
10979 AutoCaller autoCaller(this);
10980 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10981
10982 ComPtr<IInternalSessionControl> directControl;
10983 {
10984 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10985 directControl = mData->mSession.mDirectControl;
10986 }
10987
10988 /* ignore notifications sent after #OnSessionEnd() is called */
10989 if (!directControl)
10990 return S_OK;
10991
10992 return directControl->OnParallelPortChange(parallelPort);
10993}
10994
10995/**
10996 * @note Locks this object for reading.
10997 */
10998HRESULT SessionMachine::onStorageControllerChange()
10999{
11000 LogFlowThisFunc(("\n"));
11001
11002 AutoCaller autoCaller(this);
11003 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11004
11005 ComPtr<IInternalSessionControl> directControl;
11006 {
11007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11008 directControl = mData->mSession.mDirectControl;
11009 }
11010
11011 /* ignore notifications sent after #OnSessionEnd() is called */
11012 if (!directControl)
11013 return S_OK;
11014
11015 return directControl->OnStorageControllerChange();
11016}
11017
11018/**
11019 * @note Locks this object for reading.
11020 */
11021HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
11022{
11023 LogFlowThisFunc(("\n"));
11024
11025 AutoCaller autoCaller(this);
11026 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11027
11028 ComPtr<IInternalSessionControl> directControl;
11029 {
11030 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11031 directControl = mData->mSession.mDirectControl;
11032 }
11033
11034 /* ignore notifications sent after #OnSessionEnd() is called */
11035 if (!directControl)
11036 return S_OK;
11037
11038 return directControl->OnMediumChange(aAttachment, aForce);
11039}
11040
11041/**
11042 * @note Locks this object for reading.
11043 */
11044HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
11045{
11046 LogFlowThisFunc(("\n"));
11047
11048 AutoCaller autoCaller(this);
11049 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
11050
11051 ComPtr<IInternalSessionControl> directControl;
11052 {
11053 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11054 directControl = mData->mSession.mDirectControl;
11055 }
11056
11057 /* ignore notifications sent after #OnSessionEnd() is called */
11058 if (!directControl)
11059 return S_OK;
11060
11061 return directControl->OnCPUChange(aCPU, aRemove);
11062}
11063
11064HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
11065{
11066 LogFlowThisFunc(("\n"));
11067
11068 AutoCaller autoCaller(this);
11069 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
11070
11071 ComPtr<IInternalSessionControl> directControl;
11072 {
11073 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11074 directControl = mData->mSession.mDirectControl;
11075 }
11076
11077 /* ignore notifications sent after #OnSessionEnd() is called */
11078 if (!directControl)
11079 return S_OK;
11080
11081 return directControl->OnCPUExecutionCapChange(aExecutionCap);
11082}
11083
11084/**
11085 * @note Locks this object for reading.
11086 */
11087HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
11088{
11089 LogFlowThisFunc(("\n"));
11090
11091 AutoCaller autoCaller(this);
11092 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11093
11094 ComPtr<IInternalSessionControl> directControl;
11095 {
11096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11097 directControl = mData->mSession.mDirectControl;
11098 }
11099
11100 /* ignore notifications sent after #OnSessionEnd() is called */
11101 if (!directControl)
11102 return S_OK;
11103
11104 return directControl->OnVRDEServerChange(aRestart);
11105}
11106
11107/**
11108 * @note Locks this object for reading.
11109 */
11110HRESULT SessionMachine::onUSBControllerChange()
11111{
11112 LogFlowThisFunc(("\n"));
11113
11114 AutoCaller autoCaller(this);
11115 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11116
11117 ComPtr<IInternalSessionControl> directControl;
11118 {
11119 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11120 directControl = mData->mSession.mDirectControl;
11121 }
11122
11123 /* ignore notifications sent after #OnSessionEnd() is called */
11124 if (!directControl)
11125 return S_OK;
11126
11127 return directControl->OnUSBControllerChange();
11128}
11129
11130/**
11131 * @note Locks this object for reading.
11132 */
11133HRESULT SessionMachine::onSharedFolderChange()
11134{
11135 LogFlowThisFunc(("\n"));
11136
11137 AutoCaller autoCaller(this);
11138 AssertComRCReturnRC(autoCaller.rc());
11139
11140 ComPtr<IInternalSessionControl> directControl;
11141 {
11142 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11143 directControl = mData->mSession.mDirectControl;
11144 }
11145
11146 /* ignore notifications sent after #OnSessionEnd() is called */
11147 if (!directControl)
11148 return S_OK;
11149
11150 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
11151}
11152
11153/**
11154 * Returns @c true if this machine's USB controller reports it has a matching
11155 * filter for the given USB device and @c false otherwise.
11156 *
11157 * @note Caller must have requested machine read lock.
11158 */
11159bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
11160{
11161 AutoCaller autoCaller(this);
11162 /* silently return if not ready -- this method may be called after the
11163 * direct machine session has been called */
11164 if (!autoCaller.isOk())
11165 return false;
11166
11167
11168#ifdef VBOX_WITH_USB
11169 switch (mData->mMachineState)
11170 {
11171 case MachineState_Starting:
11172 case MachineState_Restoring:
11173 case MachineState_TeleportingIn:
11174 case MachineState_Paused:
11175 case MachineState_Running:
11176 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
11177 * elsewhere... */
11178 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
11179 default: break;
11180 }
11181#else
11182 NOREF(aDevice);
11183 NOREF(aMaskedIfs);
11184#endif
11185 return false;
11186}
11187
11188/**
11189 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
11190 */
11191HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
11192 IVirtualBoxErrorInfo *aError,
11193 ULONG aMaskedIfs)
11194{
11195 LogFlowThisFunc(("\n"));
11196
11197 AutoCaller autoCaller(this);
11198
11199 /* This notification may happen after the machine object has been
11200 * uninitialized (the session was closed), so don't assert. */
11201 if (FAILED(autoCaller.rc())) return autoCaller.rc();
11202
11203 ComPtr<IInternalSessionControl> directControl;
11204 {
11205 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11206 directControl = mData->mSession.mDirectControl;
11207 }
11208
11209 /* fail on notifications sent after #OnSessionEnd() is called, it is
11210 * expected by the caller */
11211 if (!directControl)
11212 return E_FAIL;
11213
11214 /* No locks should be held at this point. */
11215 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
11216 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
11217
11218 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
11219}
11220
11221/**
11222 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
11223 */
11224HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
11225 IVirtualBoxErrorInfo *aError)
11226{
11227 LogFlowThisFunc(("\n"));
11228
11229 AutoCaller autoCaller(this);
11230
11231 /* This notification may happen after the machine object has been
11232 * uninitialized (the session was closed), so don't assert. */
11233 if (FAILED(autoCaller.rc())) return autoCaller.rc();
11234
11235 ComPtr<IInternalSessionControl> directControl;
11236 {
11237 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11238 directControl = mData->mSession.mDirectControl;
11239 }
11240
11241 /* fail on notifications sent after #OnSessionEnd() is called, it is
11242 * expected by the caller */
11243 if (!directControl)
11244 return E_FAIL;
11245
11246 /* No locks should be held at this point. */
11247 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
11248 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
11249
11250 return directControl->OnUSBDeviceDetach(aId, aError);
11251}
11252
11253// protected methods
11254/////////////////////////////////////////////////////////////////////////////
11255
11256/**
11257 * Helper method to finalize saving the state.
11258 *
11259 * @note Must be called from under this object's lock.
11260 *
11261 * @param aSuccess TRUE if the snapshot has been taken successfully
11262 *
11263 * @note Locks mParent + this objects for writing.
11264 */
11265HRESULT SessionMachine::endSavingState(BOOL aSuccess)
11266{
11267 LogFlowThisFuncEnter();
11268
11269 AutoCaller autoCaller(this);
11270 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11271
11272 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11273
11274 HRESULT rc = S_OK;
11275
11276 if (aSuccess)
11277 {
11278 mSSData->mStateFilePath = mSnapshotData.mStateFilePath;
11279
11280 /* save all VM settings */
11281 rc = saveSettings(NULL);
11282 // no need to check whether VirtualBox.xml needs saving also since
11283 // we can't have a name change pending at this point
11284 }
11285 else
11286 {
11287 /* delete the saved state file (it might have been already created) */
11288 RTFileDelete(mSnapshotData.mStateFilePath.c_str());
11289 }
11290
11291 /* remove the completed progress object */
11292 mParent->removeProgress(mSnapshotData.mProgressId.ref());
11293
11294 /* clear out the temporary saved state data */
11295 mSnapshotData.mLastState = MachineState_Null;
11296 mSnapshotData.mProgressId.clear();
11297 mSnapshotData.mStateFilePath.setNull();
11298
11299 LogFlowThisFuncLeave();
11300 return rc;
11301}
11302
11303/**
11304 * Locks the attached media.
11305 *
11306 * All attached hard disks are locked for writing and DVD/floppy are locked for
11307 * reading. Parents of attached hard disks (if any) are locked for reading.
11308 *
11309 * This method also performs accessibility check of all media it locks: if some
11310 * media is inaccessible, the method will return a failure and a bunch of
11311 * extended error info objects per each inaccessible medium.
11312 *
11313 * Note that this method is atomic: if it returns a success, all media are
11314 * locked as described above; on failure no media is locked at all (all
11315 * succeeded individual locks will be undone).
11316 *
11317 * This method is intended to be called when the machine is in Starting or
11318 * Restoring state and asserts otherwise.
11319 *
11320 * The locks made by this method must be undone by calling #unlockMedia() when
11321 * no more needed.
11322 */
11323HRESULT SessionMachine::lockMedia()
11324{
11325 AutoCaller autoCaller(this);
11326 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11327
11328 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11329
11330 AssertReturn( mData->mMachineState == MachineState_Starting
11331 || mData->mMachineState == MachineState_Restoring
11332 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
11333 /* bail out if trying to lock things with already set up locking */
11334 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
11335
11336 MultiResult mrc(S_OK);
11337
11338 /* Collect locking information for all medium objects attached to the VM. */
11339 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11340 it != mMediaData->mAttachments.end();
11341 ++it)
11342 {
11343 MediumAttachment* pAtt = *it;
11344 DeviceType_T devType = pAtt->getType();
11345 Medium *pMedium = pAtt->getMedium();
11346
11347 MediumLockList *pMediumLockList(new MediumLockList());
11348 // There can be attachments without a medium (floppy/dvd), and thus
11349 // it's impossible to create a medium lock list. It still makes sense
11350 // to have the empty medium lock list in the map in case a medium is
11351 // attached later.
11352 if (pMedium != NULL)
11353 {
11354 MediumType_T mediumType = pMedium->getType();
11355 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
11356 || mediumType == MediumType_Shareable;
11357 bool fIsVitalImage = (devType == DeviceType_HardDisk);
11358
11359 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
11360 !fIsReadOnlyLock /* fMediumLockWrite */,
11361 NULL,
11362 *pMediumLockList);
11363 if (FAILED(mrc))
11364 {
11365 delete pMediumLockList;
11366 mData->mSession.mLockedMedia.Clear();
11367 break;
11368 }
11369 }
11370
11371 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
11372 if (FAILED(rc))
11373 {
11374 mData->mSession.mLockedMedia.Clear();
11375 mrc = setError(rc,
11376 tr("Collecting locking information for all attached media failed"));
11377 break;
11378 }
11379 }
11380
11381 if (SUCCEEDED(mrc))
11382 {
11383 /* Now lock all media. If this fails, nothing is locked. */
11384 HRESULT rc = mData->mSession.mLockedMedia.Lock();
11385 if (FAILED(rc))
11386 {
11387 mrc = setError(rc,
11388 tr("Locking of attached media failed"));
11389 }
11390 }
11391
11392 return mrc;
11393}
11394
11395/**
11396 * Undoes the locks made by by #lockMedia().
11397 */
11398void SessionMachine::unlockMedia()
11399{
11400 AutoCaller autoCaller(this);
11401 AssertComRCReturnVoid(autoCaller.rc());
11402
11403 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11404
11405 /* we may be holding important error info on the current thread;
11406 * preserve it */
11407 ErrorInfoKeeper eik;
11408
11409 HRESULT rc = mData->mSession.mLockedMedia.Clear();
11410 AssertComRC(rc);
11411}
11412
11413/**
11414 * Helper to change the machine state (reimplementation).
11415 *
11416 * @note Locks this object for writing.
11417 */
11418HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
11419{
11420 LogFlowThisFuncEnter();
11421 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
11422
11423 AutoCaller autoCaller(this);
11424 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11425
11426 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11427
11428 MachineState_T oldMachineState = mData->mMachineState;
11429
11430 AssertMsgReturn(oldMachineState != aMachineState,
11431 ("oldMachineState=%s, aMachineState=%s\n",
11432 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
11433 E_FAIL);
11434
11435 HRESULT rc = S_OK;
11436
11437 int stsFlags = 0;
11438 bool deleteSavedState = false;
11439
11440 /* detect some state transitions */
11441
11442 if ( ( oldMachineState == MachineState_Saved
11443 && aMachineState == MachineState_Restoring)
11444 || ( ( oldMachineState == MachineState_PoweredOff
11445 || oldMachineState == MachineState_Teleported
11446 || oldMachineState == MachineState_Aborted
11447 )
11448 && ( aMachineState == MachineState_TeleportingIn
11449 || aMachineState == MachineState_Starting
11450 )
11451 )
11452 )
11453 {
11454 /* The EMT thread is about to start */
11455
11456 /* Nothing to do here for now... */
11457
11458 /// @todo NEWMEDIA don't let mDVDDrive and other children
11459 /// change anything when in the Starting/Restoring state
11460 }
11461 else if ( ( oldMachineState == MachineState_Running
11462 || oldMachineState == MachineState_Paused
11463 || oldMachineState == MachineState_Teleporting
11464 || oldMachineState == MachineState_LiveSnapshotting
11465 || oldMachineState == MachineState_Stuck
11466 || oldMachineState == MachineState_Starting
11467 || oldMachineState == MachineState_Stopping
11468 || oldMachineState == MachineState_Saving
11469 || oldMachineState == MachineState_Restoring
11470 || oldMachineState == MachineState_TeleportingPausedVM
11471 || oldMachineState == MachineState_TeleportingIn
11472 )
11473 && ( aMachineState == MachineState_PoweredOff
11474 || aMachineState == MachineState_Saved
11475 || aMachineState == MachineState_Teleported
11476 || aMachineState == MachineState_Aborted
11477 )
11478 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
11479 * snapshot */
11480 && ( mSnapshotData.mSnapshot.isNull()
11481 || mSnapshotData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
11482 )
11483 )
11484 {
11485 /* The EMT thread has just stopped, unlock attached media. Note that as
11486 * opposed to locking that is done from Console, we do unlocking here
11487 * because the VM process may have aborted before having a chance to
11488 * properly unlock all media it locked. */
11489
11490 unlockMedia();
11491 }
11492
11493 if (oldMachineState == MachineState_Restoring)
11494 {
11495 if (aMachineState != MachineState_Saved)
11496 {
11497 /*
11498 * delete the saved state file once the machine has finished
11499 * restoring from it (note that Console sets the state from
11500 * Restoring to Saved if the VM couldn't restore successfully,
11501 * to give the user an ability to fix an error and retry --
11502 * we keep the saved state file in this case)
11503 */
11504 deleteSavedState = true;
11505 }
11506 }
11507 else if ( oldMachineState == MachineState_Saved
11508 && ( aMachineState == MachineState_PoweredOff
11509 || aMachineState == MachineState_Aborted
11510 || aMachineState == MachineState_Teleported
11511 )
11512 )
11513 {
11514 /*
11515 * delete the saved state after Console::ForgetSavedState() is called
11516 * or if the VM process (owning a direct VM session) crashed while the
11517 * VM was Saved
11518 */
11519
11520 /// @todo (dmik)
11521 // Not sure that deleting the saved state file just because of the
11522 // client death before it attempted to restore the VM is a good
11523 // thing. But when it crashes we need to go to the Aborted state
11524 // which cannot have the saved state file associated... The only
11525 // way to fix this is to make the Aborted condition not a VM state
11526 // but a bool flag: i.e., when a crash occurs, set it to true and
11527 // change the state to PoweredOff or Saved depending on the
11528 // saved state presence.
11529
11530 deleteSavedState = true;
11531 mData->mCurrentStateModified = TRUE;
11532 stsFlags |= SaveSTS_CurStateModified;
11533 }
11534
11535 if ( aMachineState == MachineState_Starting
11536 || aMachineState == MachineState_Restoring
11537 || aMachineState == MachineState_TeleportingIn
11538 )
11539 {
11540 /* set the current state modified flag to indicate that the current
11541 * state is no more identical to the state in the
11542 * current snapshot */
11543 if (!mData->mCurrentSnapshot.isNull())
11544 {
11545 mData->mCurrentStateModified = TRUE;
11546 stsFlags |= SaveSTS_CurStateModified;
11547 }
11548 }
11549
11550 if (deleteSavedState)
11551 {
11552 if (mRemoveSavedState)
11553 {
11554 Assert(!mSSData->mStateFilePath.isEmpty());
11555 RTFileDelete(mSSData->mStateFilePath.c_str());
11556 }
11557 mSSData->mStateFilePath.setNull();
11558 stsFlags |= SaveSTS_StateFilePath;
11559 }
11560
11561 /* redirect to the underlying peer machine */
11562 mPeer->setMachineState(aMachineState);
11563
11564 if ( aMachineState == MachineState_PoweredOff
11565 || aMachineState == MachineState_Teleported
11566 || aMachineState == MachineState_Aborted
11567 || aMachineState == MachineState_Saved)
11568 {
11569 /* the machine has stopped execution
11570 * (or the saved state file was adopted) */
11571 stsFlags |= SaveSTS_StateTimeStamp;
11572 }
11573
11574 if ( ( oldMachineState == MachineState_PoweredOff
11575 || oldMachineState == MachineState_Aborted
11576 || oldMachineState == MachineState_Teleported
11577 )
11578 && aMachineState == MachineState_Saved)
11579 {
11580 /* the saved state file was adopted */
11581 Assert(!mSSData->mStateFilePath.isEmpty());
11582 stsFlags |= SaveSTS_StateFilePath;
11583 }
11584
11585#ifdef VBOX_WITH_GUEST_PROPS
11586 if ( aMachineState == MachineState_PoweredOff
11587 || aMachineState == MachineState_Aborted
11588 || aMachineState == MachineState_Teleported)
11589 {
11590 /* Make sure any transient guest properties get removed from the
11591 * property store on shutdown. */
11592
11593 HWData::GuestPropertyList::iterator it;
11594 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
11595 if (!fNeedsSaving)
11596 for (it = mHWData->mGuestProperties.begin();
11597 it != mHWData->mGuestProperties.end(); ++it)
11598 if (it->mFlags & guestProp::TRANSIENT)
11599 {
11600 fNeedsSaving = true;
11601 break;
11602 }
11603 if (fNeedsSaving)
11604 {
11605 mData->mCurrentStateModified = TRUE;
11606 stsFlags |= SaveSTS_CurStateModified;
11607 SaveSettings(); // @todo r=dj why the public method? why first SaveSettings and then saveStateSettings?
11608 }
11609 }
11610#endif
11611
11612 rc = saveStateSettings(stsFlags);
11613
11614 if ( ( oldMachineState != MachineState_PoweredOff
11615 && oldMachineState != MachineState_Aborted
11616 && oldMachineState != MachineState_Teleported
11617 )
11618 && ( aMachineState == MachineState_PoweredOff
11619 || aMachineState == MachineState_Aborted
11620 || aMachineState == MachineState_Teleported
11621 )
11622 )
11623 {
11624 /* we've been shut down for any reason */
11625 /* no special action so far */
11626 }
11627
11628 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
11629 LogFlowThisFuncLeave();
11630 return rc;
11631}
11632
11633/**
11634 * Sends the current machine state value to the VM process.
11635 *
11636 * @note Locks this object for reading, then calls a client process.
11637 */
11638HRESULT SessionMachine::updateMachineStateOnClient()
11639{
11640 AutoCaller autoCaller(this);
11641 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11642
11643 ComPtr<IInternalSessionControl> directControl;
11644 {
11645 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11646 AssertReturn(!!mData, E_FAIL);
11647 directControl = mData->mSession.mDirectControl;
11648
11649 /* directControl may be already set to NULL here in #OnSessionEnd()
11650 * called too early by the direct session process while there is still
11651 * some operation (like deleting the snapshot) in progress. The client
11652 * process in this case is waiting inside Session::close() for the
11653 * "end session" process object to complete, while #uninit() called by
11654 * #checkForDeath() on the Watcher thread is waiting for the pending
11655 * operation to complete. For now, we accept this inconsistent behavior
11656 * and simply do nothing here. */
11657
11658 if (mData->mSession.mState == SessionState_Unlocking)
11659 return S_OK;
11660
11661 AssertReturn(!directControl.isNull(), E_FAIL);
11662 }
11663
11664 return directControl->UpdateMachineState(mData->mMachineState);
11665}
Note: See TracBrowser for help on using the repository browser.

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