VirtualBox

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

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

Main: prohibit setting a UUID as the machine or snapshot name

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

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