VirtualBox

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

Last change on this file since 42021 was 42021, checked in by vboxsync, 13 years ago

Main/Machine: added a caller and a read lock before calling of MediumAttachment instance in the function RemoveStorageController.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 448.5 KB
Line 
1/* $Id: MachineImpl.cpp 42021 2012-07-04 14:18:14Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2012 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. 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#include "MachineImplCloneVM.h"
53#include "AutostartDb.h"
54
55// generated header
56#include "VBoxEvents.h"
57
58#ifdef VBOX_WITH_USB
59# include "USBProxyService.h"
60#endif
61
62#include "AutoCaller.h"
63#include "HashedPw.h"
64#include "Performance.h"
65
66#include <iprt/asm.h>
67#include <iprt/path.h>
68#include <iprt/dir.h>
69#include <iprt/env.h>
70#include <iprt/lockvalidator.h>
71#include <iprt/process.h>
72#include <iprt/cpp/utils.h>
73#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
74#include <iprt/sha.h>
75#include <iprt/string.h>
76
77#include <VBox/com/array.h>
78#include <VBox/com/list.h>
79
80#include <VBox/err.h>
81#include <VBox/param.h>
82#include <VBox/settings.h>
83#include <VBox/vmm/ssm.h>
84
85#ifdef VBOX_WITH_GUEST_PROPS
86# include <VBox/HostServices/GuestPropertySvc.h>
87# include <VBox/com/array.h>
88#endif
89
90#include "VBox/com/MultiResult.h"
91
92#include <algorithm>
93
94#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
95# define HOSTSUFF_EXE ".exe"
96#else /* !RT_OS_WINDOWS */
97# define HOSTSUFF_EXE ""
98#endif /* !RT_OS_WINDOWS */
99
100// defines / prototypes
101/////////////////////////////////////////////////////////////////////////////
102
103/////////////////////////////////////////////////////////////////////////////
104// Machine::Data structure
105/////////////////////////////////////////////////////////////////////////////
106
107Machine::Data::Data()
108{
109 mRegistered = FALSE;
110 pMachineConfigFile = NULL;
111 /* Contains hints on what has changed when the user is using the VM (config
112 * changes, running the VM, ...). This is used to decide if a config needs
113 * to be written to disk. */
114 flModifications = 0;
115 /* VM modification usually also trigger setting the current state to
116 * "Modified". Although this is not always the case. An e.g. is the VM
117 * initialization phase or when snapshot related data is changed. The
118 * actually behavior is controlled by the following flag. */
119 m_fAllowStateModification = false;
120 mAccessible = FALSE;
121 /* mUuid is initialized in Machine::init() */
122
123 mMachineState = MachineState_PoweredOff;
124 RTTimeNow(&mLastStateChange);
125
126 mMachineStateDeps = 0;
127 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
128 mMachineStateChangePending = 0;
129
130 mCurrentStateModified = TRUE;
131 mGuestPropertiesModified = FALSE;
132
133 mSession.mPid = NIL_RTPROCESS;
134 mSession.mState = SessionState_Unlocked;
135}
136
137Machine::Data::~Data()
138{
139 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
140 {
141 RTSemEventMultiDestroy(mMachineStateDepsSem);
142 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
143 }
144 if (pMachineConfigFile)
145 {
146 delete pMachineConfigFile;
147 pMachineConfigFile = NULL;
148 }
149}
150
151/////////////////////////////////////////////////////////////////////////////
152// Machine::HWData structure
153/////////////////////////////////////////////////////////////////////////////
154
155Machine::HWData::HWData()
156{
157 /* default values for a newly created machine */
158 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
159 mMemorySize = 128;
160 mCPUCount = 1;
161 mCPUHotPlugEnabled = false;
162 mMemoryBalloonSize = 0;
163 mPageFusionEnabled = false;
164 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 mHWVirtExEnabled = true;
169 mHWVirtExNestedPagingEnabled = true;
170#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
171 mHWVirtExLargePagesEnabled = true;
172#else
173 /* Not supported on 32 bits hosts. */
174 mHWVirtExLargePagesEnabled = false;
175#endif
176 mHWVirtExVPIDEnabled = true;
177 mHWVirtExForceEnabled = false;
178#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
179 mHWVirtExExclusive = false;
180#else
181 mHWVirtExExclusive = true;
182#endif
183#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
184 mPAEEnabled = true;
185#else
186 mPAEEnabled = false;
187#endif
188 mSyntheticCpu = false;
189 mHpetEnabled = false;
190
191 /* default boot order: floppy - DVD - HDD */
192 mBootOrder[0] = DeviceType_Floppy;
193 mBootOrder[1] = DeviceType_DVD;
194 mBootOrder[2] = DeviceType_HardDisk;
195 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
196 mBootOrder[i] = DeviceType_Null;
197
198 mClipboardMode = ClipboardMode_Disabled;
199 mGuestPropertyNotificationPatterns = "";
200
201 mFirmwareType = FirmwareType_BIOS;
202 mKeyboardHidType = KeyboardHidType_PS2Keyboard;
203 mPointingHidType = PointingHidType_PS2Mouse;
204 mChipsetType = ChipsetType_PIIX3;
205 mEmulatedUSBCardReaderEnabled = FALSE;
206
207 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
208 mCPUAttached[i] = false;
209
210 mIoCacheEnabled = true;
211 mIoCacheSize = 5; /* 5MB */
212
213 /* Maximum CPU execution cap by default. */
214 mCpuExecutionCap = 100;
215}
216
217Machine::HWData::~HWData()
218{
219}
220
221/////////////////////////////////////////////////////////////////////////////
222// Machine::HDData structure
223/////////////////////////////////////////////////////////////////////////////
224
225Machine::MediaData::MediaData()
226{
227}
228
229Machine::MediaData::~MediaData()
230{
231}
232
233/////////////////////////////////////////////////////////////////////////////
234// Machine class
235/////////////////////////////////////////////////////////////////////////////
236
237// constructor / destructor
238/////////////////////////////////////////////////////////////////////////////
239
240Machine::Machine()
241 : mCollectorGuest(NULL),
242 mPeer(NULL),
243 mParent(NULL),
244 mSerialPorts(),
245 mParallelPorts(),
246 uRegistryNeedsSaving(0)
247{}
248
249Machine::~Machine()
250{}
251
252HRESULT Machine::FinalConstruct()
253{
254 LogFlowThisFunc(("\n"));
255 return BaseFinalConstruct();
256}
257
258void Machine::FinalRelease()
259{
260 LogFlowThisFunc(("\n"));
261 uninit();
262 BaseFinalRelease();
263}
264
265/**
266 * Initializes a new machine instance; this init() variant creates a new, empty machine.
267 * This gets called from VirtualBox::CreateMachine().
268 *
269 * @param aParent Associated parent object
270 * @param strConfigFile Local file system path to the VM settings file (can
271 * be relative to the VirtualBox config directory).
272 * @param strName name for the machine
273 * @param aId UUID for the new machine.
274 * @param aOsType OS Type of this machine or NULL.
275 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
276 *
277 * @return Success indicator. if not S_OK, the machine object is invalid
278 */
279HRESULT Machine::init(VirtualBox *aParent,
280 const Utf8Str &strConfigFile,
281 const Utf8Str &strName,
282 GuestOSType *aOsType,
283 const Guid &aId,
284 bool fForceOverwrite)
285{
286 LogFlowThisFuncEnter();
287 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
288
289 /* Enclose the state transition NotReady->InInit->Ready */
290 AutoInitSpan autoInitSpan(this);
291 AssertReturn(autoInitSpan.isOk(), E_FAIL);
292
293 HRESULT rc = initImpl(aParent, strConfigFile);
294 if (FAILED(rc)) return rc;
295
296 rc = tryCreateMachineConfigFile(fForceOverwrite);
297 if (FAILED(rc)) return rc;
298
299 if (SUCCEEDED(rc))
300 {
301 // create an empty machine config
302 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
303
304 rc = initDataAndChildObjects();
305 }
306
307 if (SUCCEEDED(rc))
308 {
309 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
310 mData->mAccessible = TRUE;
311
312 unconst(mData->mUuid) = aId;
313
314 mUserData->s.strName = strName;
315
316 // the "name sync" flag determines whether the machine directory gets renamed along
317 // with the machine file; say so if the settings file name is the same as the
318 // settings file parent directory (machine directory)
319 mUserData->s.fNameSync = isInOwnDir();
320
321 // initialize the default snapshots folder
322 rc = COMSETTER(SnapshotFolder)(NULL);
323 AssertComRC(rc);
324
325 if (aOsType)
326 {
327 /* Store OS type */
328 mUserData->s.strOsType = aOsType->id();
329
330 /* Apply BIOS defaults */
331 mBIOSSettings->applyDefaults(aOsType);
332
333 /* Apply network adapters defaults */
334 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
335 mNetworkAdapters[slot]->applyDefaults(aOsType);
336
337 /* Apply serial port defaults */
338 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
339 mSerialPorts[slot]->applyDefaults(aOsType);
340 }
341
342 /* At this point the changing of the current state modification
343 * flag is allowed. */
344 allowStateModification();
345
346 /* commit all changes made during the initialization */
347 commit();
348 }
349
350 /* Confirm a successful initialization when it's the case */
351 if (SUCCEEDED(rc))
352 {
353 if (mData->mAccessible)
354 autoInitSpan.setSucceeded();
355 else
356 autoInitSpan.setLimited();
357 }
358
359 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
360 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
361 mData->mRegistered,
362 mData->mAccessible,
363 rc));
364
365 LogFlowThisFuncLeave();
366
367 return rc;
368}
369
370/**
371 * Initializes a new instance with data from machine XML (formerly Init_Registered).
372 * Gets called in two modes:
373 *
374 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
375 * UUID is specified and we mark the machine as "registered";
376 *
377 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
378 * and the machine remains unregistered until RegisterMachine() is called.
379 *
380 * @param aParent Associated parent object
381 * @param aConfigFile Local file system path to the VM settings file (can
382 * be relative to the VirtualBox config directory).
383 * @param aId UUID of the machine or NULL (see above).
384 *
385 * @return Success indicator. if not S_OK, the machine object is invalid
386 */
387HRESULT Machine::init(VirtualBox *aParent,
388 const Utf8Str &strConfigFile,
389 const Guid *aId)
390{
391 LogFlowThisFuncEnter();
392 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
393
394 /* Enclose the state transition NotReady->InInit->Ready */
395 AutoInitSpan autoInitSpan(this);
396 AssertReturn(autoInitSpan.isOk(), E_FAIL);
397
398 HRESULT rc = initImpl(aParent, strConfigFile);
399 if (FAILED(rc)) return rc;
400
401 if (aId)
402 {
403 // loading a registered VM:
404 unconst(mData->mUuid) = *aId;
405 mData->mRegistered = TRUE;
406 // now load the settings from XML:
407 rc = registeredInit();
408 // this calls initDataAndChildObjects() and loadSettings()
409 }
410 else
411 {
412 // opening an unregistered VM (VirtualBox::OpenMachine()):
413 rc = initDataAndChildObjects();
414
415 if (SUCCEEDED(rc))
416 {
417 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
418 mData->mAccessible = TRUE;
419
420 try
421 {
422 // load and parse machine XML; this will throw on XML or logic errors
423 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
424
425 // reject VM UUID duplicates, they can happen if someone
426 // tries to register an already known VM config again
427 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
428 true /* fPermitInaccessible */,
429 false /* aDoSetError */,
430 NULL) != VBOX_E_OBJECT_NOT_FOUND)
431 {
432 throw setError(E_FAIL,
433 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
434 mData->m_strConfigFile.c_str());
435 }
436
437 // use UUID from machine config
438 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
439
440 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
441 NULL /* puuidRegistry */);
442 if (FAILED(rc)) throw rc;
443
444 /* At this point the changing of the current state modification
445 * flag is allowed. */
446 allowStateModification();
447
448 commit();
449 }
450 catch (HRESULT err)
451 {
452 /* we assume that error info is set by the thrower */
453 rc = err;
454 }
455 catch (...)
456 {
457 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
458 }
459 }
460 }
461
462 /* Confirm a successful initialization when it's the case */
463 if (SUCCEEDED(rc))
464 {
465 if (mData->mAccessible)
466 autoInitSpan.setSucceeded();
467 else
468 {
469 autoInitSpan.setLimited();
470
471 // uninit media from this machine's media registry, or else
472 // reloading the settings will fail
473 mParent->unregisterMachineMedia(getId());
474 }
475 }
476
477 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
478 "rc=%08X\n",
479 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
480 mData->mRegistered, mData->mAccessible, rc));
481
482 LogFlowThisFuncLeave();
483
484 return rc;
485}
486
487/**
488 * Initializes a new instance from a machine config that is already in memory
489 * (import OVF case). Since we are importing, the UUID in the machine
490 * config is ignored and we always generate a fresh one.
491 *
492 * @param strName Name for the new machine; this overrides what is specified in config and is used
493 * for the settings file as well.
494 * @param config Machine configuration loaded and parsed from XML.
495 *
496 * @return Success indicator. if not S_OK, the machine object is invalid
497 */
498HRESULT Machine::init(VirtualBox *aParent,
499 const Utf8Str &strName,
500 const settings::MachineConfigFile &config)
501{
502 LogFlowThisFuncEnter();
503
504 /* Enclose the state transition NotReady->InInit->Ready */
505 AutoInitSpan autoInitSpan(this);
506 AssertReturn(autoInitSpan.isOk(), E_FAIL);
507
508 Utf8Str strConfigFile;
509 aParent->getDefaultMachineFolder(strConfigFile);
510 strConfigFile.append(RTPATH_DELIMITER);
511 strConfigFile.append(strName);
512 strConfigFile.append(RTPATH_DELIMITER);
513 strConfigFile.append(strName);
514 strConfigFile.append(".vbox");
515
516 HRESULT rc = initImpl(aParent, strConfigFile);
517 if (FAILED(rc)) return rc;
518
519 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
520 if (FAILED(rc)) return rc;
521
522 rc = initDataAndChildObjects();
523
524 if (SUCCEEDED(rc))
525 {
526 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
527 mData->mAccessible = TRUE;
528
529 // create empty machine config for instance data
530 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
531
532 // generate fresh UUID, ignore machine config
533 unconst(mData->mUuid).create();
534
535 rc = loadMachineDataFromSettings(config,
536 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
537
538 // override VM name as well, it may be different
539 mUserData->s.strName = strName;
540
541 if (SUCCEEDED(rc))
542 {
543 /* At this point the changing of the current state modification
544 * flag is allowed. */
545 allowStateModification();
546
547 /* commit all changes made during the initialization */
548 commit();
549 }
550 }
551
552 /* Confirm a successful initialization when it's the case */
553 if (SUCCEEDED(rc))
554 {
555 if (mData->mAccessible)
556 autoInitSpan.setSucceeded();
557 else
558 {
559 autoInitSpan.setLimited();
560
561 // uninit media from this machine's media registry, or else
562 // reloading the settings will fail
563 mParent->unregisterMachineMedia(getId());
564 }
565 }
566
567 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
568 "rc=%08X\n",
569 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
570 mData->mRegistered, mData->mAccessible, rc));
571
572 LogFlowThisFuncLeave();
573
574 return rc;
575}
576
577/**
578 * Shared code between the various init() implementations.
579 * @param aParent
580 * @return
581 */
582HRESULT Machine::initImpl(VirtualBox *aParent,
583 const Utf8Str &strConfigFile)
584{
585 LogFlowThisFuncEnter();
586
587 AssertReturn(aParent, E_INVALIDARG);
588 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
589
590 HRESULT rc = S_OK;
591
592 /* share the parent weakly */
593 unconst(mParent) = aParent;
594
595 /* allocate the essential machine data structure (the rest will be
596 * allocated later by initDataAndChildObjects() */
597 mData.allocate();
598
599 /* memorize the config file name (as provided) */
600 mData->m_strConfigFile = strConfigFile;
601
602 /* get the full file name */
603 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
604 if (RT_FAILURE(vrc1))
605 return setError(VBOX_E_FILE_ERROR,
606 tr("Invalid machine settings file name '%s' (%Rrc)"),
607 strConfigFile.c_str(),
608 vrc1);
609
610 LogFlowThisFuncLeave();
611
612 return rc;
613}
614
615/**
616 * Tries to create a machine settings file in the path stored in the machine
617 * instance data. Used when a new machine is created to fail gracefully if
618 * the settings file could not be written (e.g. because machine dir is read-only).
619 * @return
620 */
621HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
622{
623 HRESULT rc = S_OK;
624
625 // when we create a new machine, we must be able to create the settings file
626 RTFILE f = NIL_RTFILE;
627 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
628 if ( RT_SUCCESS(vrc)
629 || vrc == VERR_SHARING_VIOLATION
630 )
631 {
632 if (RT_SUCCESS(vrc))
633 RTFileClose(f);
634 if (!fForceOverwrite)
635 rc = setError(VBOX_E_FILE_ERROR,
636 tr("Machine settings file '%s' already exists"),
637 mData->m_strConfigFileFull.c_str());
638 else
639 {
640 /* try to delete the config file, as otherwise the creation
641 * of a new settings file will fail. */
642 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
643 if (RT_FAILURE(vrc2))
644 rc = setError(VBOX_E_FILE_ERROR,
645 tr("Could not delete the existing settings file '%s' (%Rrc)"),
646 mData->m_strConfigFileFull.c_str(), vrc2);
647 }
648 }
649 else if ( vrc != VERR_FILE_NOT_FOUND
650 && vrc != VERR_PATH_NOT_FOUND
651 )
652 rc = setError(VBOX_E_FILE_ERROR,
653 tr("Invalid machine settings file name '%s' (%Rrc)"),
654 mData->m_strConfigFileFull.c_str(),
655 vrc);
656 return rc;
657}
658
659/**
660 * Initializes the registered machine by loading the settings file.
661 * This method is separated from #init() in order to make it possible to
662 * retry the operation after VirtualBox startup instead of refusing to
663 * startup the whole VirtualBox server in case if the settings file of some
664 * registered VM is invalid or inaccessible.
665 *
666 * @note Must be always called from this object's write lock
667 * (unless called from #init() that doesn't need any locking).
668 * @note Locks the mUSBController method for writing.
669 * @note Subclasses must not call this method.
670 */
671HRESULT Machine::registeredInit()
672{
673 AssertReturn(!isSessionMachine(), E_FAIL);
674 AssertReturn(!isSnapshotMachine(), E_FAIL);
675 AssertReturn(!mData->mUuid.isEmpty(), E_FAIL);
676 AssertReturn(!mData->mAccessible, E_FAIL);
677
678 HRESULT rc = initDataAndChildObjects();
679
680 if (SUCCEEDED(rc))
681 {
682 /* Temporarily reset the registered flag in order to let setters
683 * potentially called from loadSettings() succeed (isMutable() used in
684 * all setters will return FALSE for a Machine instance if mRegistered
685 * is TRUE). */
686 mData->mRegistered = FALSE;
687
688 try
689 {
690 // load and parse machine XML; this will throw on XML or logic errors
691 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
692
693 if (mData->mUuid != mData->pMachineConfigFile->uuid)
694 throw setError(E_FAIL,
695 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
696 mData->pMachineConfigFile->uuid.raw(),
697 mData->m_strConfigFileFull.c_str(),
698 mData->mUuid.toString().c_str(),
699 mParent->settingsFilePath().c_str());
700
701 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
702 NULL /* const Guid *puuidRegistry */);
703 if (FAILED(rc)) throw rc;
704 }
705 catch (HRESULT err)
706 {
707 /* we assume that error info is set by the thrower */
708 rc = err;
709 }
710 catch (...)
711 {
712 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
713 }
714
715 /* Restore the registered flag (even on failure) */
716 mData->mRegistered = TRUE;
717 }
718
719 if (SUCCEEDED(rc))
720 {
721 /* Set mAccessible to TRUE only if we successfully locked and loaded
722 * the settings file */
723 mData->mAccessible = TRUE;
724
725 /* commit all changes made during loading the settings file */
726 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
727 /// @todo r=klaus for some reason the settings loading logic backs up
728 // the settings, and therefore a commit is needed. Should probably be changed.
729 }
730 else
731 {
732 /* If the machine is registered, then, instead of returning a
733 * failure, we mark it as inaccessible and set the result to
734 * success to give it a try later */
735
736 /* fetch the current error info */
737 mData->mAccessError = com::ErrorInfo();
738 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
739 mData->mUuid.raw(),
740 mData->mAccessError.getText().raw()));
741
742 /* rollback all changes */
743 rollback(false /* aNotify */);
744
745 // uninit media from this machine's media registry, or else
746 // reloading the settings will fail
747 mParent->unregisterMachineMedia(getId());
748
749 /* uninitialize the common part to make sure all data is reset to
750 * default (null) values */
751 uninitDataAndChildObjects();
752
753 rc = S_OK;
754 }
755
756 return rc;
757}
758
759/**
760 * Uninitializes the instance.
761 * Called either from FinalRelease() or by the parent when it gets destroyed.
762 *
763 * @note The caller of this method must make sure that this object
764 * a) doesn't have active callers on the current thread and b) is not locked
765 * by the current thread; otherwise uninit() will hang either a) due to
766 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
767 * a dead-lock caused by this thread waiting for all callers on the other
768 * threads are done but preventing them from doing so by holding a lock.
769 */
770void Machine::uninit()
771{
772 LogFlowThisFuncEnter();
773
774 Assert(!isWriteLockOnCurrentThread());
775
776 Assert(!uRegistryNeedsSaving);
777 if (uRegistryNeedsSaving)
778 {
779 AutoCaller autoCaller(this);
780 if (SUCCEEDED(autoCaller.rc()))
781 {
782 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
783 saveSettings(NULL, Machine::SaveS_Force);
784 }
785 }
786
787 /* Enclose the state transition Ready->InUninit->NotReady */
788 AutoUninitSpan autoUninitSpan(this);
789 if (autoUninitSpan.uninitDone())
790 return;
791
792 Assert(!isSnapshotMachine());
793 Assert(!isSessionMachine());
794 Assert(!!mData);
795
796 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
797 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
798
799 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
800
801 if (!mData->mSession.mMachine.isNull())
802 {
803 /* Theoretically, this can only happen if the VirtualBox server has been
804 * terminated while there were clients running that owned open direct
805 * sessions. Since in this case we are definitely called by
806 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
807 * won't happen on the client watcher thread (because it does
808 * VirtualBox::addCaller() for the duration of the
809 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
810 * cannot happen until the VirtualBox caller is released). This is
811 * important, because SessionMachine::uninit() cannot correctly operate
812 * after we return from this method (it expects the Machine instance is
813 * still valid). We'll call it ourselves below.
814 */
815 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
816 (SessionMachine*)mData->mSession.mMachine));
817
818 if (Global::IsOnlineOrTransient(mData->mMachineState))
819 {
820 LogWarningThisFunc(("Setting state to Aborted!\n"));
821 /* set machine state using SessionMachine reimplementation */
822 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
823 }
824
825 /*
826 * Uninitialize SessionMachine using public uninit() to indicate
827 * an unexpected uninitialization.
828 */
829 mData->mSession.mMachine->uninit();
830 /* SessionMachine::uninit() must set mSession.mMachine to null */
831 Assert(mData->mSession.mMachine.isNull());
832 }
833
834 // uninit media from this machine's media registry, if they're still there
835 Guid uuidMachine(getId());
836
837 /* XXX This will fail with
838 * "cannot be closed because it is still attached to 1 virtual machines"
839 * because at this point we did not call uninitDataAndChildObjects() yet
840 * and therefore also removeBackReference() for all these mediums was not called! */
841 if (!uuidMachine.isEmpty()) // can be empty if we're called from a failure of Machine::init
842 mParent->unregisterMachineMedia(uuidMachine);
843
844 /* the lock is no more necessary (SessionMachine is uninitialized) */
845 alock.release();
846
847 // has machine been modified?
848 if (mData->flModifications)
849 {
850 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
851 rollback(false /* aNotify */);
852 }
853
854 if (mData->mAccessible)
855 uninitDataAndChildObjects();
856
857 /* free the essential data structure last */
858 mData.free();
859
860 LogFlowThisFuncLeave();
861}
862
863// IMachine properties
864/////////////////////////////////////////////////////////////////////////////
865
866STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
867{
868 CheckComArgOutPointerValid(aParent);
869
870 AutoLimitedCaller autoCaller(this);
871 if (FAILED(autoCaller.rc())) return autoCaller.rc();
872
873 /* mParent is constant during life time, no need to lock */
874 ComObjPtr<VirtualBox> pVirtualBox(mParent);
875 pVirtualBox.queryInterfaceTo(aParent);
876
877 return S_OK;
878}
879
880STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
881{
882 CheckComArgOutPointerValid(aAccessible);
883
884 AutoLimitedCaller autoCaller(this);
885 if (FAILED(autoCaller.rc())) return autoCaller.rc();
886
887 LogFlowThisFunc(("ENTER\n"));
888
889 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
890
891 HRESULT rc = S_OK;
892
893 if (!mData->mAccessible)
894 {
895 /* try to initialize the VM once more if not accessible */
896
897 AutoReinitSpan autoReinitSpan(this);
898 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
899
900#ifdef DEBUG
901 LogFlowThisFunc(("Dumping media backreferences\n"));
902 mParent->dumpAllBackRefs();
903#endif
904
905 if (mData->pMachineConfigFile)
906 {
907 // reset the XML file to force loadSettings() (called from registeredInit())
908 // to parse it again; the file might have changed
909 delete mData->pMachineConfigFile;
910 mData->pMachineConfigFile = NULL;
911 }
912
913 rc = registeredInit();
914
915 if (SUCCEEDED(rc) && mData->mAccessible)
916 {
917 autoReinitSpan.setSucceeded();
918
919 /* make sure interesting parties will notice the accessibility
920 * state change */
921 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
922 mParent->onMachineDataChange(mData->mUuid);
923 }
924 }
925
926 if (SUCCEEDED(rc))
927 *aAccessible = mData->mAccessible;
928
929 LogFlowThisFuncLeave();
930
931 return rc;
932}
933
934STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
935{
936 CheckComArgOutPointerValid(aAccessError);
937
938 AutoLimitedCaller autoCaller(this);
939 if (FAILED(autoCaller.rc())) return autoCaller.rc();
940
941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
942
943 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
944 {
945 /* return shortly */
946 aAccessError = NULL;
947 return S_OK;
948 }
949
950 HRESULT rc = S_OK;
951
952 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
953 rc = errorInfo.createObject();
954 if (SUCCEEDED(rc))
955 {
956 errorInfo->init(mData->mAccessError.getResultCode(),
957 mData->mAccessError.getInterfaceID().ref(),
958 Utf8Str(mData->mAccessError.getComponent()).c_str(),
959 Utf8Str(mData->mAccessError.getText()));
960 rc = errorInfo.queryInterfaceTo(aAccessError);
961 }
962
963 return rc;
964}
965
966STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
967{
968 CheckComArgOutPointerValid(aName);
969
970 AutoCaller autoCaller(this);
971 if (FAILED(autoCaller.rc())) return autoCaller.rc();
972
973 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
974
975 mUserData->s.strName.cloneTo(aName);
976
977 return S_OK;
978}
979
980STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
981{
982 CheckComArgStrNotEmptyOrNull(aName);
983
984 AutoCaller autoCaller(this);
985 if (FAILED(autoCaller.rc())) return autoCaller.rc();
986
987 // prohibit setting a UUID only as the machine name, or else it can
988 // never be found by findMachine()
989 Guid test(aName);
990 if (test.isNotEmpty())
991 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
992
993 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
994
995 HRESULT rc = checkStateDependency(MutableStateDep);
996 if (FAILED(rc)) return rc;
997
998 setModified(IsModified_MachineData);
999 mUserData.backup();
1000 mUserData->s.strName = aName;
1001
1002 return S_OK;
1003}
1004
1005STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1006{
1007 CheckComArgOutPointerValid(aDescription);
1008
1009 AutoCaller autoCaller(this);
1010 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1011
1012 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1013
1014 mUserData->s.strDescription.cloneTo(aDescription);
1015
1016 return S_OK;
1017}
1018
1019STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1020{
1021 AutoCaller autoCaller(this);
1022 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1023
1024 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1025
1026 HRESULT rc = checkStateDependency(MutableStateDep);
1027 if (FAILED(rc)) return rc;
1028
1029 setModified(IsModified_MachineData);
1030 mUserData.backup();
1031 mUserData->s.strDescription = aDescription;
1032
1033 return S_OK;
1034}
1035
1036STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1037{
1038 CheckComArgOutPointerValid(aId);
1039
1040 AutoLimitedCaller autoCaller(this);
1041 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1042
1043 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1044
1045 mData->mUuid.toUtf16().cloneTo(aId);
1046
1047 return S_OK;
1048}
1049
1050STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1051{
1052 CheckComArgOutPointerValid(aOSTypeId);
1053
1054 AutoCaller autoCaller(this);
1055 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1056
1057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1058
1059 mUserData->s.strOsType.cloneTo(aOSTypeId);
1060
1061 return S_OK;
1062}
1063
1064STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1065{
1066 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1067
1068 AutoCaller autoCaller(this);
1069 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1070
1071 /* look up the object by Id to check it is valid */
1072 ComPtr<IGuestOSType> guestOSType;
1073 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1074 if (FAILED(rc)) return rc;
1075
1076 /* when setting, always use the "etalon" value for consistency -- lookup
1077 * by ID is case-insensitive and the input value may have different case */
1078 Bstr osTypeId;
1079 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1080 if (FAILED(rc)) return rc;
1081
1082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1083
1084 rc = checkStateDependency(MutableStateDep);
1085 if (FAILED(rc)) return rc;
1086
1087 setModified(IsModified_MachineData);
1088 mUserData.backup();
1089 mUserData->s.strOsType = osTypeId;
1090
1091 return S_OK;
1092}
1093
1094
1095STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1096{
1097 CheckComArgOutPointerValid(aFirmwareType);
1098
1099 AutoCaller autoCaller(this);
1100 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1101
1102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1103
1104 *aFirmwareType = mHWData->mFirmwareType;
1105
1106 return S_OK;
1107}
1108
1109STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1110{
1111 AutoCaller autoCaller(this);
1112 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1113 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1114
1115 int rc = checkStateDependency(MutableStateDep);
1116 if (FAILED(rc)) return rc;
1117
1118 setModified(IsModified_MachineData);
1119 mHWData.backup();
1120 mHWData->mFirmwareType = aFirmwareType;
1121
1122 return S_OK;
1123}
1124
1125STDMETHODIMP Machine::COMGETTER(KeyboardHidType)(KeyboardHidType_T *aKeyboardHidType)
1126{
1127 CheckComArgOutPointerValid(aKeyboardHidType);
1128
1129 AutoCaller autoCaller(this);
1130 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1131
1132 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1133
1134 *aKeyboardHidType = mHWData->mKeyboardHidType;
1135
1136 return S_OK;
1137}
1138
1139STDMETHODIMP Machine::COMSETTER(KeyboardHidType)(KeyboardHidType_T aKeyboardHidType)
1140{
1141 AutoCaller autoCaller(this);
1142 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1143 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1144
1145 int rc = checkStateDependency(MutableStateDep);
1146 if (FAILED(rc)) return rc;
1147
1148 setModified(IsModified_MachineData);
1149 mHWData.backup();
1150 mHWData->mKeyboardHidType = aKeyboardHidType;
1151
1152 return S_OK;
1153}
1154
1155STDMETHODIMP Machine::COMGETTER(PointingHidType)(PointingHidType_T *aPointingHidType)
1156{
1157 CheckComArgOutPointerValid(aPointingHidType);
1158
1159 AutoCaller autoCaller(this);
1160 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1161
1162 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1163
1164 *aPointingHidType = mHWData->mPointingHidType;
1165
1166 return S_OK;
1167}
1168
1169STDMETHODIMP Machine::COMSETTER(PointingHidType)(PointingHidType_T aPointingHidType)
1170{
1171 AutoCaller autoCaller(this);
1172 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1173 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1174
1175 int rc = checkStateDependency(MutableStateDep);
1176 if (FAILED(rc)) return rc;
1177
1178 setModified(IsModified_MachineData);
1179 mHWData.backup();
1180 mHWData->mPointingHidType = aPointingHidType;
1181
1182 return S_OK;
1183}
1184
1185STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1186{
1187 CheckComArgOutPointerValid(aChipsetType);
1188
1189 AutoCaller autoCaller(this);
1190 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1191
1192 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1193
1194 *aChipsetType = mHWData->mChipsetType;
1195
1196 return S_OK;
1197}
1198
1199STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1200{
1201 AutoCaller autoCaller(this);
1202 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1203 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1204
1205 int rc = checkStateDependency(MutableStateDep);
1206 if (FAILED(rc)) return rc;
1207
1208 if (aChipsetType != mHWData->mChipsetType)
1209 {
1210 setModified(IsModified_MachineData);
1211 mHWData.backup();
1212 mHWData->mChipsetType = aChipsetType;
1213
1214 // Resize network adapter array, to be finalized on commit/rollback.
1215 // We must not throw away entries yet, otherwise settings are lost
1216 // without a way to roll back.
1217 uint32_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1218 uint32_t oldCount = mNetworkAdapters.size();
1219 if (newCount > oldCount)
1220 {
1221 mNetworkAdapters.resize(newCount);
1222 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1223 {
1224 unconst(mNetworkAdapters[slot]).createObject();
1225 mNetworkAdapters[slot]->init(this, slot);
1226 }
1227 }
1228 }
1229
1230 return S_OK;
1231}
1232
1233STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1234{
1235 if (!aHWVersion)
1236 return E_POINTER;
1237
1238 AutoCaller autoCaller(this);
1239 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1240
1241 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1242
1243 mHWData->mHWVersion.cloneTo(aHWVersion);
1244
1245 return S_OK;
1246}
1247
1248STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1249{
1250 /* check known version */
1251 Utf8Str hwVersion = aHWVersion;
1252 if ( hwVersion.compare("1") != 0
1253 && hwVersion.compare("2") != 0)
1254 return setError(E_INVALIDARG,
1255 tr("Invalid hardware version: %ls\n"), aHWVersion);
1256
1257 AutoCaller autoCaller(this);
1258 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1259
1260 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1261
1262 HRESULT rc = checkStateDependency(MutableStateDep);
1263 if (FAILED(rc)) return rc;
1264
1265 setModified(IsModified_MachineData);
1266 mHWData.backup();
1267 mHWData->mHWVersion = hwVersion;
1268
1269 return S_OK;
1270}
1271
1272STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1273{
1274 CheckComArgOutPointerValid(aUUID);
1275
1276 AutoCaller autoCaller(this);
1277 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1278
1279 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1280
1281 if (!mHWData->mHardwareUUID.isEmpty())
1282 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1283 else
1284 mData->mUuid.toUtf16().cloneTo(aUUID);
1285
1286 return S_OK;
1287}
1288
1289STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1290{
1291 Guid hardwareUUID(aUUID);
1292 if (hardwareUUID.isEmpty())
1293 return E_INVALIDARG;
1294
1295 AutoCaller autoCaller(this);
1296 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1297
1298 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1299
1300 HRESULT rc = checkStateDependency(MutableStateDep);
1301 if (FAILED(rc)) return rc;
1302
1303 setModified(IsModified_MachineData);
1304 mHWData.backup();
1305 if (hardwareUUID == mData->mUuid)
1306 mHWData->mHardwareUUID.clear();
1307 else
1308 mHWData->mHardwareUUID = hardwareUUID;
1309
1310 return S_OK;
1311}
1312
1313STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1314{
1315 if (!memorySize)
1316 return E_POINTER;
1317
1318 AutoCaller autoCaller(this);
1319 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1320
1321 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1322
1323 *memorySize = mHWData->mMemorySize;
1324
1325 return S_OK;
1326}
1327
1328STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1329{
1330 /* check RAM limits */
1331 if ( memorySize < MM_RAM_MIN_IN_MB
1332 || memorySize > MM_RAM_MAX_IN_MB
1333 )
1334 return setError(E_INVALIDARG,
1335 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1336 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1337
1338 AutoCaller autoCaller(this);
1339 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1340
1341 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1342
1343 HRESULT rc = checkStateDependency(MutableStateDep);
1344 if (FAILED(rc)) return rc;
1345
1346 setModified(IsModified_MachineData);
1347 mHWData.backup();
1348 mHWData->mMemorySize = memorySize;
1349
1350 return S_OK;
1351}
1352
1353STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1354{
1355 if (!CPUCount)
1356 return E_POINTER;
1357
1358 AutoCaller autoCaller(this);
1359 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1360
1361 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1362
1363 *CPUCount = mHWData->mCPUCount;
1364
1365 return S_OK;
1366}
1367
1368STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1369{
1370 /* check CPU limits */
1371 if ( CPUCount < SchemaDefs::MinCPUCount
1372 || CPUCount > SchemaDefs::MaxCPUCount
1373 )
1374 return setError(E_INVALIDARG,
1375 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1376 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1377
1378 AutoCaller autoCaller(this);
1379 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1380
1381 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1382
1383 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1384 if (mHWData->mCPUHotPlugEnabled)
1385 {
1386 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1387 {
1388 if (mHWData->mCPUAttached[idx])
1389 return setError(E_INVALIDARG,
1390 tr("There is still a CPU attached to socket %lu."
1391 "Detach the CPU before removing the socket"),
1392 CPUCount, idx+1);
1393 }
1394 }
1395
1396 HRESULT rc = checkStateDependency(MutableStateDep);
1397 if (FAILED(rc)) return rc;
1398
1399 setModified(IsModified_MachineData);
1400 mHWData.backup();
1401 mHWData->mCPUCount = CPUCount;
1402
1403 return S_OK;
1404}
1405
1406STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1407{
1408 if (!aExecutionCap)
1409 return E_POINTER;
1410
1411 AutoCaller autoCaller(this);
1412 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1413
1414 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1415
1416 *aExecutionCap = mHWData->mCpuExecutionCap;
1417
1418 return S_OK;
1419}
1420
1421STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1422{
1423 HRESULT rc = S_OK;
1424
1425 /* check throttle limits */
1426 if ( aExecutionCap < 1
1427 || aExecutionCap > 100
1428 )
1429 return setError(E_INVALIDARG,
1430 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1431 aExecutionCap, 1, 100);
1432
1433 AutoCaller autoCaller(this);
1434 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1435
1436 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1437
1438 alock.release();
1439 rc = onCPUExecutionCapChange(aExecutionCap);
1440 alock.acquire();
1441 if (FAILED(rc)) return rc;
1442
1443 setModified(IsModified_MachineData);
1444 mHWData.backup();
1445 mHWData->mCpuExecutionCap = aExecutionCap;
1446
1447 /* Save settings if online - todo why is this required?? */
1448 if (Global::IsOnline(mData->mMachineState))
1449 saveSettings(NULL);
1450
1451 return S_OK;
1452}
1453
1454
1455STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *enabled)
1456{
1457 if (!enabled)
1458 return E_POINTER;
1459
1460 AutoCaller autoCaller(this);
1461 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1462
1463 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1464
1465 *enabled = mHWData->mCPUHotPlugEnabled;
1466
1467 return S_OK;
1468}
1469
1470STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled)
1471{
1472 HRESULT rc = S_OK;
1473
1474 AutoCaller autoCaller(this);
1475 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1476
1477 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1478
1479 rc = checkStateDependency(MutableStateDep);
1480 if (FAILED(rc)) return rc;
1481
1482 if (mHWData->mCPUHotPlugEnabled != enabled)
1483 {
1484 if (enabled)
1485 {
1486 setModified(IsModified_MachineData);
1487 mHWData.backup();
1488
1489 /* Add the amount of CPUs currently attached */
1490 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1491 {
1492 mHWData->mCPUAttached[i] = true;
1493 }
1494 }
1495 else
1496 {
1497 /*
1498 * We can disable hotplug only if the amount of maximum CPUs is equal
1499 * to the amount of attached CPUs
1500 */
1501 unsigned cCpusAttached = 0;
1502 unsigned iHighestId = 0;
1503
1504 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1505 {
1506 if (mHWData->mCPUAttached[i])
1507 {
1508 cCpusAttached++;
1509 iHighestId = i;
1510 }
1511 }
1512
1513 if ( (cCpusAttached != mHWData->mCPUCount)
1514 || (iHighestId >= mHWData->mCPUCount))
1515 return setError(E_INVALIDARG,
1516 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1517
1518 setModified(IsModified_MachineData);
1519 mHWData.backup();
1520 }
1521 }
1522
1523 mHWData->mCPUHotPlugEnabled = enabled;
1524
1525 return rc;
1526}
1527
1528STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *enabled)
1529{
1530#ifdef VBOX_WITH_USB_CARDREADER
1531 CheckComArgOutPointerValid(enabled);
1532
1533 AutoCaller autoCaller(this);
1534 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1535
1536 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1537
1538 *enabled = mHWData->mEmulatedUSBCardReaderEnabled;
1539
1540 return S_OK;
1541#else
1542 NOREF(enabled);
1543 return E_NOTIMPL;
1544#endif
1545}
1546
1547STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL enabled)
1548{
1549#ifdef VBOX_WITH_USB_CARDREADER
1550 AutoCaller autoCaller(this);
1551 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1552 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1553
1554 int rc = checkStateDependency(MutableStateDep);
1555 if (FAILED(rc)) return rc;
1556
1557 setModified(IsModified_MachineData);
1558 mHWData.backup();
1559 mHWData->mEmulatedUSBCardReaderEnabled = enabled;
1560
1561 return S_OK;
1562#else
1563 NOREF(enabled);
1564 return E_NOTIMPL;
1565#endif
1566}
1567
1568STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *enabled)
1569{
1570 NOREF(enabled);
1571 return E_NOTIMPL;
1572}
1573
1574STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL enabled)
1575{
1576 NOREF(enabled);
1577 return E_NOTIMPL;
1578}
1579
1580STDMETHODIMP Machine::COMGETTER(HpetEnabled)(BOOL *enabled)
1581{
1582 CheckComArgOutPointerValid(enabled);
1583
1584 AutoCaller autoCaller(this);
1585 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1586 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1587
1588 *enabled = mHWData->mHpetEnabled;
1589
1590 return S_OK;
1591}
1592
1593STDMETHODIMP Machine::COMSETTER(HpetEnabled)(BOOL enabled)
1594{
1595 HRESULT rc = S_OK;
1596
1597 AutoCaller autoCaller(this);
1598 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1599 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1600
1601 rc = checkStateDependency(MutableStateDep);
1602 if (FAILED(rc)) return rc;
1603
1604 setModified(IsModified_MachineData);
1605 mHWData.backup();
1606
1607 mHWData->mHpetEnabled = enabled;
1608
1609 return rc;
1610}
1611
1612STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1613{
1614 if (!memorySize)
1615 return E_POINTER;
1616
1617 AutoCaller autoCaller(this);
1618 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1619
1620 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1621
1622 *memorySize = mHWData->mVRAMSize;
1623
1624 return S_OK;
1625}
1626
1627STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1628{
1629 /* check VRAM limits */
1630 if (memorySize < SchemaDefs::MinGuestVRAM ||
1631 memorySize > SchemaDefs::MaxGuestVRAM)
1632 return setError(E_INVALIDARG,
1633 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1634 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1635
1636 AutoCaller autoCaller(this);
1637 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1638
1639 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1640
1641 HRESULT rc = checkStateDependency(MutableStateDep);
1642 if (FAILED(rc)) return rc;
1643
1644 setModified(IsModified_MachineData);
1645 mHWData.backup();
1646 mHWData->mVRAMSize = memorySize;
1647
1648 return S_OK;
1649}
1650
1651/** @todo this method should not be public */
1652STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1653{
1654 if (!memoryBalloonSize)
1655 return E_POINTER;
1656
1657 AutoCaller autoCaller(this);
1658 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1659
1660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1661
1662 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
1663
1664 return S_OK;
1665}
1666
1667/**
1668 * Set the memory balloon size.
1669 *
1670 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1671 * we have to make sure that we never call IGuest from here.
1672 */
1673STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
1674{
1675 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1676#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1677 /* check limits */
1678 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1679 return setError(E_INVALIDARG,
1680 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1681 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1682
1683 AutoCaller autoCaller(this);
1684 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1685
1686 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1687
1688 setModified(IsModified_MachineData);
1689 mHWData.backup();
1690 mHWData->mMemoryBalloonSize = memoryBalloonSize;
1691
1692 return S_OK;
1693#else
1694 NOREF(memoryBalloonSize);
1695 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1696#endif
1697}
1698
1699STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *enabled)
1700{
1701 if (!enabled)
1702 return E_POINTER;
1703
1704 AutoCaller autoCaller(this);
1705 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1706
1707 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1708
1709 *enabled = mHWData->mPageFusionEnabled;
1710 return S_OK;
1711}
1712
1713STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL enabled)
1714{
1715#ifdef VBOX_WITH_PAGE_SHARING
1716 AutoCaller autoCaller(this);
1717 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1718
1719 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1720
1721 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1722 setModified(IsModified_MachineData);
1723 mHWData.backup();
1724 mHWData->mPageFusionEnabled = enabled;
1725 return S_OK;
1726#else
1727 NOREF(enabled);
1728 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1729#endif
1730}
1731
1732STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *enabled)
1733{
1734 if (!enabled)
1735 return E_POINTER;
1736
1737 AutoCaller autoCaller(this);
1738 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1739
1740 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1741
1742 *enabled = mHWData->mAccelerate3DEnabled;
1743
1744 return S_OK;
1745}
1746
1747STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
1748{
1749 AutoCaller autoCaller(this);
1750 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1751
1752 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1753
1754 HRESULT rc = checkStateDependency(MutableStateDep);
1755 if (FAILED(rc)) return rc;
1756
1757 /** @todo check validity! */
1758
1759 setModified(IsModified_MachineData);
1760 mHWData.backup();
1761 mHWData->mAccelerate3DEnabled = enable;
1762
1763 return S_OK;
1764}
1765
1766
1767STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *enabled)
1768{
1769 if (!enabled)
1770 return E_POINTER;
1771
1772 AutoCaller autoCaller(this);
1773 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1774
1775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1776
1777 *enabled = mHWData->mAccelerate2DVideoEnabled;
1778
1779 return S_OK;
1780}
1781
1782STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
1783{
1784 AutoCaller autoCaller(this);
1785 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1786
1787 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1788
1789 HRESULT rc = checkStateDependency(MutableStateDep);
1790 if (FAILED(rc)) return rc;
1791
1792 /** @todo check validity! */
1793
1794 setModified(IsModified_MachineData);
1795 mHWData.backup();
1796 mHWData->mAccelerate2DVideoEnabled = enable;
1797
1798 return S_OK;
1799}
1800
1801STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
1802{
1803 if (!monitorCount)
1804 return E_POINTER;
1805
1806 AutoCaller autoCaller(this);
1807 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1808
1809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1810
1811 *monitorCount = mHWData->mMonitorCount;
1812
1813 return S_OK;
1814}
1815
1816STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
1817{
1818 /* make sure monitor count is a sensible number */
1819 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
1820 return setError(E_INVALIDARG,
1821 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1822 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
1823
1824 AutoCaller autoCaller(this);
1825 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1826
1827 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1828
1829 HRESULT rc = checkStateDependency(MutableStateDep);
1830 if (FAILED(rc)) return rc;
1831
1832 setModified(IsModified_MachineData);
1833 mHWData.backup();
1834 mHWData->mMonitorCount = monitorCount;
1835
1836 return S_OK;
1837}
1838
1839STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
1840{
1841 if (!biosSettings)
1842 return E_POINTER;
1843
1844 AutoCaller autoCaller(this);
1845 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1846
1847 /* mBIOSSettings is constant during life time, no need to lock */
1848 mBIOSSettings.queryInterfaceTo(biosSettings);
1849
1850 return S_OK;
1851}
1852
1853STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
1854{
1855 if (!aVal)
1856 return E_POINTER;
1857
1858 AutoCaller autoCaller(this);
1859 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1860
1861 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1862
1863 switch(property)
1864 {
1865 case CPUPropertyType_PAE:
1866 *aVal = mHWData->mPAEEnabled;
1867 break;
1868
1869 case CPUPropertyType_Synthetic:
1870 *aVal = mHWData->mSyntheticCpu;
1871 break;
1872
1873 default:
1874 return E_INVALIDARG;
1875 }
1876 return S_OK;
1877}
1878
1879STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
1880{
1881 AutoCaller autoCaller(this);
1882 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1883
1884 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1885
1886 HRESULT rc = checkStateDependency(MutableStateDep);
1887 if (FAILED(rc)) return rc;
1888
1889 switch(property)
1890 {
1891 case CPUPropertyType_PAE:
1892 setModified(IsModified_MachineData);
1893 mHWData.backup();
1894 mHWData->mPAEEnabled = !!aVal;
1895 break;
1896
1897 case CPUPropertyType_Synthetic:
1898 setModified(IsModified_MachineData);
1899 mHWData.backup();
1900 mHWData->mSyntheticCpu = !!aVal;
1901 break;
1902
1903 default:
1904 return E_INVALIDARG;
1905 }
1906 return S_OK;
1907}
1908
1909STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
1910{
1911 CheckComArgOutPointerValid(aValEax);
1912 CheckComArgOutPointerValid(aValEbx);
1913 CheckComArgOutPointerValid(aValEcx);
1914 CheckComArgOutPointerValid(aValEdx);
1915
1916 AutoCaller autoCaller(this);
1917 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1918
1919 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1920
1921 switch(aId)
1922 {
1923 case 0x0:
1924 case 0x1:
1925 case 0x2:
1926 case 0x3:
1927 case 0x4:
1928 case 0x5:
1929 case 0x6:
1930 case 0x7:
1931 case 0x8:
1932 case 0x9:
1933 case 0xA:
1934 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
1935 return E_INVALIDARG;
1936
1937 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
1938 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
1939 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
1940 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
1941 break;
1942
1943 case 0x80000000:
1944 case 0x80000001:
1945 case 0x80000002:
1946 case 0x80000003:
1947 case 0x80000004:
1948 case 0x80000005:
1949 case 0x80000006:
1950 case 0x80000007:
1951 case 0x80000008:
1952 case 0x80000009:
1953 case 0x8000000A:
1954 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
1955 return E_INVALIDARG;
1956
1957 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
1958 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
1959 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
1960 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
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::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
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 switch(aId)
1980 {
1981 case 0x0:
1982 case 0x1:
1983 case 0x2:
1984 case 0x3:
1985 case 0x4:
1986 case 0x5:
1987 case 0x6:
1988 case 0x7:
1989 case 0x8:
1990 case 0x9:
1991 case 0xA:
1992 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
1993 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
1994 setModified(IsModified_MachineData);
1995 mHWData.backup();
1996 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
1997 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
1998 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
1999 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2000 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2001 break;
2002
2003 case 0x80000000:
2004 case 0x80000001:
2005 case 0x80000002:
2006 case 0x80000003:
2007 case 0x80000004:
2008 case 0x80000005:
2009 case 0x80000006:
2010 case 0x80000007:
2011 case 0x80000008:
2012 case 0x80000009:
2013 case 0x8000000A:
2014 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2015 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2016 setModified(IsModified_MachineData);
2017 mHWData.backup();
2018 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2019 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2020 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2021 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2022 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2023 break;
2024
2025 default:
2026 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2027 }
2028 return S_OK;
2029}
2030
2031STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2032{
2033 AutoCaller autoCaller(this);
2034 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2035
2036 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2037
2038 HRESULT rc = checkStateDependency(MutableStateDep);
2039 if (FAILED(rc)) return rc;
2040
2041 switch(aId)
2042 {
2043 case 0x0:
2044 case 0x1:
2045 case 0x2:
2046 case 0x3:
2047 case 0x4:
2048 case 0x5:
2049 case 0x6:
2050 case 0x7:
2051 case 0x8:
2052 case 0x9:
2053 case 0xA:
2054 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2055 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2056 setModified(IsModified_MachineData);
2057 mHWData.backup();
2058 /* Invalidate leaf. */
2059 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2060 break;
2061
2062 case 0x80000000:
2063 case 0x80000001:
2064 case 0x80000002:
2065 case 0x80000003:
2066 case 0x80000004:
2067 case 0x80000005:
2068 case 0x80000006:
2069 case 0x80000007:
2070 case 0x80000008:
2071 case 0x80000009:
2072 case 0x8000000A:
2073 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2074 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2075 setModified(IsModified_MachineData);
2076 mHWData.backup();
2077 /* Invalidate leaf. */
2078 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2079 break;
2080
2081 default:
2082 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2083 }
2084 return S_OK;
2085}
2086
2087STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2088{
2089 AutoCaller autoCaller(this);
2090 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2091
2092 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2093
2094 HRESULT rc = checkStateDependency(MutableStateDep);
2095 if (FAILED(rc)) return rc;
2096
2097 setModified(IsModified_MachineData);
2098 mHWData.backup();
2099
2100 /* Invalidate all standard leafs. */
2101 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2102 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2103
2104 /* Invalidate all extended leafs. */
2105 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2106 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2107
2108 return S_OK;
2109}
2110
2111STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2112{
2113 if (!aVal)
2114 return E_POINTER;
2115
2116 AutoCaller autoCaller(this);
2117 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2118
2119 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2120
2121 switch(property)
2122 {
2123 case HWVirtExPropertyType_Enabled:
2124 *aVal = mHWData->mHWVirtExEnabled;
2125 break;
2126
2127 case HWVirtExPropertyType_Exclusive:
2128 *aVal = mHWData->mHWVirtExExclusive;
2129 break;
2130
2131 case HWVirtExPropertyType_VPID:
2132 *aVal = mHWData->mHWVirtExVPIDEnabled;
2133 break;
2134
2135 case HWVirtExPropertyType_NestedPaging:
2136 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2137 break;
2138
2139 case HWVirtExPropertyType_LargePages:
2140 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2141#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2142 *aVal = FALSE;
2143#endif
2144 break;
2145
2146 case HWVirtExPropertyType_Force:
2147 *aVal = mHWData->mHWVirtExForceEnabled;
2148 break;
2149
2150 default:
2151 return E_INVALIDARG;
2152 }
2153 return S_OK;
2154}
2155
2156STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2157{
2158 AutoCaller autoCaller(this);
2159 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2160
2161 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2162
2163 HRESULT rc = checkStateDependency(MutableStateDep);
2164 if (FAILED(rc)) return rc;
2165
2166 switch(property)
2167 {
2168 case HWVirtExPropertyType_Enabled:
2169 setModified(IsModified_MachineData);
2170 mHWData.backup();
2171 mHWData->mHWVirtExEnabled = !!aVal;
2172 break;
2173
2174 case HWVirtExPropertyType_Exclusive:
2175 setModified(IsModified_MachineData);
2176 mHWData.backup();
2177 mHWData->mHWVirtExExclusive = !!aVal;
2178 break;
2179
2180 case HWVirtExPropertyType_VPID:
2181 setModified(IsModified_MachineData);
2182 mHWData.backup();
2183 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2184 break;
2185
2186 case HWVirtExPropertyType_NestedPaging:
2187 setModified(IsModified_MachineData);
2188 mHWData.backup();
2189 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2190 break;
2191
2192 case HWVirtExPropertyType_LargePages:
2193 setModified(IsModified_MachineData);
2194 mHWData.backup();
2195 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2196 break;
2197
2198 case HWVirtExPropertyType_Force:
2199 setModified(IsModified_MachineData);
2200 mHWData.backup();
2201 mHWData->mHWVirtExForceEnabled = !!aVal;
2202 break;
2203
2204 default:
2205 return E_INVALIDARG;
2206 }
2207
2208 return S_OK;
2209}
2210
2211STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2212{
2213 CheckComArgOutPointerValid(aSnapshotFolder);
2214
2215 AutoCaller autoCaller(this);
2216 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2217
2218 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2219
2220 Utf8Str strFullSnapshotFolder;
2221 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2222 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2223
2224 return S_OK;
2225}
2226
2227STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2228{
2229 /* @todo (r=dmik):
2230 * 1. Allow to change the name of the snapshot folder containing snapshots
2231 * 2. Rename the folder on disk instead of just changing the property
2232 * value (to be smart and not to leave garbage). Note that it cannot be
2233 * done here because the change may be rolled back. Thus, the right
2234 * place is #saveSettings().
2235 */
2236
2237 AutoCaller autoCaller(this);
2238 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2239
2240 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2241
2242 HRESULT rc = checkStateDependency(MutableStateDep);
2243 if (FAILED(rc)) return rc;
2244
2245 if (!mData->mCurrentSnapshot.isNull())
2246 return setError(E_FAIL,
2247 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2248
2249 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2250
2251 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2252 if (strSnapshotFolder.isEmpty())
2253 strSnapshotFolder = "Snapshots";
2254 int vrc = calculateFullPath(strSnapshotFolder,
2255 strSnapshotFolder);
2256 if (RT_FAILURE(vrc))
2257 return setError(E_FAIL,
2258 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2259 aSnapshotFolder, vrc);
2260
2261 setModified(IsModified_MachineData);
2262 mUserData.backup();
2263
2264 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2265
2266 return S_OK;
2267}
2268
2269STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2270{
2271 if (ComSafeArrayOutIsNull(aAttachments))
2272 return E_POINTER;
2273
2274 AutoCaller autoCaller(this);
2275 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2276
2277 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2278
2279 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2280 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2281
2282 return S_OK;
2283}
2284
2285STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2286{
2287 if (!vrdeServer)
2288 return E_POINTER;
2289
2290 AutoCaller autoCaller(this);
2291 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2292
2293 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2294
2295 Assert(!!mVRDEServer);
2296 mVRDEServer.queryInterfaceTo(vrdeServer);
2297
2298 return S_OK;
2299}
2300
2301STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2302{
2303 if (!audioAdapter)
2304 return E_POINTER;
2305
2306 AutoCaller autoCaller(this);
2307 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2308
2309 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2310
2311 mAudioAdapter.queryInterfaceTo(audioAdapter);
2312 return S_OK;
2313}
2314
2315STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2316{
2317#ifdef VBOX_WITH_VUSB
2318 CheckComArgOutPointerValid(aUSBController);
2319
2320 AutoCaller autoCaller(this);
2321 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2322
2323 clearError();
2324 MultiResult rc(S_OK);
2325
2326# ifdef VBOX_WITH_USB
2327 rc = mParent->host()->checkUSBProxyService();
2328 if (FAILED(rc)) return rc;
2329# endif
2330
2331 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2332
2333 return rc = mUSBController.queryInterfaceTo(aUSBController);
2334#else
2335 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2336 * extended error info to indicate that USB is simply not available
2337 * (w/o treating it as a failure), for example, as in OSE */
2338 NOREF(aUSBController);
2339 ReturnComNotImplemented();
2340#endif /* VBOX_WITH_VUSB */
2341}
2342
2343STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2344{
2345 CheckComArgOutPointerValid(aFilePath);
2346
2347 AutoLimitedCaller autoCaller(this);
2348 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2349
2350 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2351
2352 mData->m_strConfigFileFull.cloneTo(aFilePath);
2353 return S_OK;
2354}
2355
2356STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2357{
2358 CheckComArgOutPointerValid(aModified);
2359
2360 AutoCaller autoCaller(this);
2361 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2362
2363 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2364
2365 HRESULT rc = checkStateDependency(MutableStateDep);
2366 if (FAILED(rc)) return rc;
2367
2368 if (!mData->pMachineConfigFile->fileExists())
2369 // this is a new machine, and no config file exists yet:
2370 *aModified = TRUE;
2371 else
2372 *aModified = (mData->flModifications != 0);
2373
2374 return S_OK;
2375}
2376
2377STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2378{
2379 CheckComArgOutPointerValid(aSessionState);
2380
2381 AutoCaller autoCaller(this);
2382 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2383
2384 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2385
2386 *aSessionState = mData->mSession.mState;
2387
2388 return S_OK;
2389}
2390
2391STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2392{
2393 CheckComArgOutPointerValid(aSessionType);
2394
2395 AutoCaller autoCaller(this);
2396 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2397
2398 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2399
2400 mData->mSession.mType.cloneTo(aSessionType);
2401
2402 return S_OK;
2403}
2404
2405STDMETHODIMP Machine::COMGETTER(SessionPid)(ULONG *aSessionPid)
2406{
2407 CheckComArgOutPointerValid(aSessionPid);
2408
2409 AutoCaller autoCaller(this);
2410 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2411
2412 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2413
2414 *aSessionPid = mData->mSession.mPid;
2415
2416 return S_OK;
2417}
2418
2419STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2420{
2421 if (!machineState)
2422 return E_POINTER;
2423
2424 AutoCaller autoCaller(this);
2425 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2426
2427 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2428
2429 *machineState = mData->mMachineState;
2430
2431 return S_OK;
2432}
2433
2434STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2435{
2436 CheckComArgOutPointerValid(aLastStateChange);
2437
2438 AutoCaller autoCaller(this);
2439 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2440
2441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2442
2443 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2444
2445 return S_OK;
2446}
2447
2448STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2449{
2450 CheckComArgOutPointerValid(aStateFilePath);
2451
2452 AutoCaller autoCaller(this);
2453 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2454
2455 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2456
2457 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2458
2459 return S_OK;
2460}
2461
2462STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2463{
2464 CheckComArgOutPointerValid(aLogFolder);
2465
2466 AutoCaller autoCaller(this);
2467 AssertComRCReturnRC(autoCaller.rc());
2468
2469 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2470
2471 Utf8Str logFolder;
2472 getLogFolder(logFolder);
2473 logFolder.cloneTo(aLogFolder);
2474
2475 return S_OK;
2476}
2477
2478STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2479{
2480 CheckComArgOutPointerValid(aCurrentSnapshot);
2481
2482 AutoCaller autoCaller(this);
2483 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2484
2485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2486
2487 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2488
2489 return S_OK;
2490}
2491
2492STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2493{
2494 CheckComArgOutPointerValid(aSnapshotCount);
2495
2496 AutoCaller autoCaller(this);
2497 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2498
2499 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2500
2501 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2502 ? 0
2503 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2504
2505 return S_OK;
2506}
2507
2508STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2509{
2510 CheckComArgOutPointerValid(aCurrentStateModified);
2511
2512 AutoCaller autoCaller(this);
2513 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2514
2515 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2516
2517 /* Note: for machines with no snapshots, we always return FALSE
2518 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2519 * reasons :) */
2520
2521 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2522 ? FALSE
2523 : mData->mCurrentStateModified;
2524
2525 return S_OK;
2526}
2527
2528STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2529{
2530 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2531
2532 AutoCaller autoCaller(this);
2533 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2534
2535 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2536
2537 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2538 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2539
2540 return S_OK;
2541}
2542
2543STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2544{
2545 CheckComArgOutPointerValid(aClipboardMode);
2546
2547 AutoCaller autoCaller(this);
2548 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2549
2550 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2551
2552 *aClipboardMode = mHWData->mClipboardMode;
2553
2554 return S_OK;
2555}
2556
2557STDMETHODIMP
2558Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2559{
2560 HRESULT rc = S_OK;
2561
2562 AutoCaller autoCaller(this);
2563 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2564
2565 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2566
2567 alock.release();
2568 rc = onClipboardModeChange(aClipboardMode);
2569 alock.acquire();
2570 if (FAILED(rc)) return rc;
2571
2572 setModified(IsModified_MachineData);
2573 mHWData.backup();
2574 mHWData->mClipboardMode = aClipboardMode;
2575
2576 /* Save settings if online - todo why is this required?? */
2577 if (Global::IsOnline(mData->mMachineState))
2578 saveSettings(NULL);
2579
2580 return S_OK;
2581}
2582
2583STDMETHODIMP
2584Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
2585{
2586 CheckComArgOutPointerValid(aPatterns);
2587
2588 AutoCaller autoCaller(this);
2589 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2590
2591 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2592
2593 try
2594 {
2595 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
2596 }
2597 catch (...)
2598 {
2599 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2600 }
2601
2602 return S_OK;
2603}
2604
2605STDMETHODIMP
2606Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
2607{
2608 AutoCaller autoCaller(this);
2609 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2610
2611 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2612
2613 HRESULT rc = checkStateDependency(MutableStateDep);
2614 if (FAILED(rc)) return rc;
2615
2616 setModified(IsModified_MachineData);
2617 mHWData.backup();
2618 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
2619 return rc;
2620}
2621
2622STDMETHODIMP
2623Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
2624{
2625 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
2626
2627 AutoCaller autoCaller(this);
2628 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2629
2630 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2631
2632 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
2633 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
2634
2635 return S_OK;
2636}
2637
2638STDMETHODIMP
2639Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
2640{
2641 CheckComArgOutPointerValid(aEnabled);
2642
2643 AutoCaller autoCaller(this);
2644 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2645
2646 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2647
2648 *aEnabled = mUserData->s.fTeleporterEnabled;
2649
2650 return S_OK;
2651}
2652
2653STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
2654{
2655 AutoCaller autoCaller(this);
2656 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2657
2658 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2659
2660 /* Only allow it to be set to true when PoweredOff or Aborted.
2661 (Clearing it is always permitted.) */
2662 if ( aEnabled
2663 && mData->mRegistered
2664 && ( !isSessionMachine()
2665 || ( mData->mMachineState != MachineState_PoweredOff
2666 && mData->mMachineState != MachineState_Teleported
2667 && mData->mMachineState != MachineState_Aborted
2668 )
2669 )
2670 )
2671 return setError(VBOX_E_INVALID_VM_STATE,
2672 tr("The machine is not powered off (state is %s)"),
2673 Global::stringifyMachineState(mData->mMachineState));
2674
2675 setModified(IsModified_MachineData);
2676 mUserData.backup();
2677 mUserData->s.fTeleporterEnabled = !!aEnabled;
2678
2679 return S_OK;
2680}
2681
2682STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
2683{
2684 CheckComArgOutPointerValid(aPort);
2685
2686 AutoCaller autoCaller(this);
2687 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2688
2689 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2690
2691 *aPort = (ULONG)mUserData->s.uTeleporterPort;
2692
2693 return S_OK;
2694}
2695
2696STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
2697{
2698 if (aPort >= _64K)
2699 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
2700
2701 AutoCaller autoCaller(this);
2702 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2703
2704 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2705
2706 HRESULT rc = checkStateDependency(MutableStateDep);
2707 if (FAILED(rc)) return rc;
2708
2709 setModified(IsModified_MachineData);
2710 mUserData.backup();
2711 mUserData->s.uTeleporterPort = (uint32_t)aPort;
2712
2713 return S_OK;
2714}
2715
2716STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
2717{
2718 CheckComArgOutPointerValid(aAddress);
2719
2720 AutoCaller autoCaller(this);
2721 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2722
2723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2724
2725 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
2726
2727 return S_OK;
2728}
2729
2730STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
2731{
2732 AutoCaller autoCaller(this);
2733 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2734
2735 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2736
2737 HRESULT rc = checkStateDependency(MutableStateDep);
2738 if (FAILED(rc)) return rc;
2739
2740 setModified(IsModified_MachineData);
2741 mUserData.backup();
2742 mUserData->s.strTeleporterAddress = aAddress;
2743
2744 return S_OK;
2745}
2746
2747STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
2748{
2749 CheckComArgOutPointerValid(aPassword);
2750
2751 AutoCaller autoCaller(this);
2752 HRESULT hrc = autoCaller.rc();
2753 if (SUCCEEDED(hrc))
2754 {
2755 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2756 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
2757 }
2758
2759 return hrc;
2760}
2761
2762STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
2763{
2764 /*
2765 * Hash the password first.
2766 */
2767 Utf8Str strPassword(aPassword);
2768 if (!strPassword.isEmpty())
2769 {
2770 if (VBoxIsPasswordHashed(&strPassword))
2771 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2772 VBoxHashPassword(&strPassword);
2773 }
2774
2775 /*
2776 * Do the update.
2777 */
2778 AutoCaller autoCaller(this);
2779 HRESULT hrc = autoCaller.rc();
2780 if (SUCCEEDED(hrc))
2781 {
2782 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2783 hrc = checkStateDependency(MutableStateDep);
2784 if (SUCCEEDED(hrc))
2785 {
2786 setModified(IsModified_MachineData);
2787 mUserData.backup();
2788 mUserData->s.strTeleporterPassword = strPassword;
2789 }
2790 }
2791
2792 return hrc;
2793}
2794
2795STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
2796{
2797 CheckComArgOutPointerValid(aState);
2798
2799 AutoCaller autoCaller(this);
2800 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2801
2802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2803
2804 *aState = mUserData->s.enmFaultToleranceState;
2805 return S_OK;
2806}
2807
2808STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
2809{
2810 AutoCaller autoCaller(this);
2811 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2812
2813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2814
2815 /* @todo deal with running state change. */
2816 HRESULT rc = checkStateDependency(MutableStateDep);
2817 if (FAILED(rc)) return rc;
2818
2819 setModified(IsModified_MachineData);
2820 mUserData.backup();
2821 mUserData->s.enmFaultToleranceState = aState;
2822 return S_OK;
2823}
2824
2825STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
2826{
2827 CheckComArgOutPointerValid(aAddress);
2828
2829 AutoCaller autoCaller(this);
2830 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2831
2832 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2833
2834 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
2835 return S_OK;
2836}
2837
2838STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
2839{
2840 AutoCaller autoCaller(this);
2841 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2842
2843 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2844
2845 /* @todo deal with running state change. */
2846 HRESULT rc = checkStateDependency(MutableStateDep);
2847 if (FAILED(rc)) return rc;
2848
2849 setModified(IsModified_MachineData);
2850 mUserData.backup();
2851 mUserData->s.strFaultToleranceAddress = aAddress;
2852 return S_OK;
2853}
2854
2855STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
2856{
2857 CheckComArgOutPointerValid(aPort);
2858
2859 AutoCaller autoCaller(this);
2860 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2861
2862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2863
2864 *aPort = mUserData->s.uFaultTolerancePort;
2865 return S_OK;
2866}
2867
2868STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
2869{
2870 AutoCaller autoCaller(this);
2871 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2872
2873 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2874
2875 /* @todo deal with running state change. */
2876 HRESULT rc = checkStateDependency(MutableStateDep);
2877 if (FAILED(rc)) return rc;
2878
2879 setModified(IsModified_MachineData);
2880 mUserData.backup();
2881 mUserData->s.uFaultTolerancePort = aPort;
2882 return S_OK;
2883}
2884
2885STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
2886{
2887 CheckComArgOutPointerValid(aPassword);
2888
2889 AutoCaller autoCaller(this);
2890 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2891
2892 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2893
2894 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
2895
2896 return S_OK;
2897}
2898
2899STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
2900{
2901 AutoCaller autoCaller(this);
2902 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2903
2904 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2905
2906 /* @todo deal with running state change. */
2907 HRESULT rc = checkStateDependency(MutableStateDep);
2908 if (FAILED(rc)) return rc;
2909
2910 setModified(IsModified_MachineData);
2911 mUserData.backup();
2912 mUserData->s.strFaultTolerancePassword = aPassword;
2913
2914 return S_OK;
2915}
2916
2917STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
2918{
2919 CheckComArgOutPointerValid(aInterval);
2920
2921 AutoCaller autoCaller(this);
2922 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2923
2924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2925
2926 *aInterval = mUserData->s.uFaultToleranceInterval;
2927 return S_OK;
2928}
2929
2930STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
2931{
2932 AutoCaller autoCaller(this);
2933 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2934
2935 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2936
2937 /* @todo deal with running state change. */
2938 HRESULT rc = checkStateDependency(MutableStateDep);
2939 if (FAILED(rc)) return rc;
2940
2941 setModified(IsModified_MachineData);
2942 mUserData.backup();
2943 mUserData->s.uFaultToleranceInterval = aInterval;
2944 return S_OK;
2945}
2946
2947STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
2948{
2949 CheckComArgOutPointerValid(aEnabled);
2950
2951 AutoCaller autoCaller(this);
2952 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2953
2954 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2955
2956 *aEnabled = mUserData->s.fRTCUseUTC;
2957
2958 return S_OK;
2959}
2960
2961STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
2962{
2963 AutoCaller autoCaller(this);
2964 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2965
2966 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2967
2968 /* Only allow it to be set to true when PoweredOff or Aborted.
2969 (Clearing it is always permitted.) */
2970 if ( aEnabled
2971 && mData->mRegistered
2972 && ( !isSessionMachine()
2973 || ( mData->mMachineState != MachineState_PoweredOff
2974 && mData->mMachineState != MachineState_Teleported
2975 && mData->mMachineState != MachineState_Aborted
2976 )
2977 )
2978 )
2979 return setError(VBOX_E_INVALID_VM_STATE,
2980 tr("The machine is not powered off (state is %s)"),
2981 Global::stringifyMachineState(mData->mMachineState));
2982
2983 setModified(IsModified_MachineData);
2984 mUserData.backup();
2985 mUserData->s.fRTCUseUTC = !!aEnabled;
2986
2987 return S_OK;
2988}
2989
2990STDMETHODIMP Machine::COMGETTER(IoCacheEnabled)(BOOL *aEnabled)
2991{
2992 CheckComArgOutPointerValid(aEnabled);
2993
2994 AutoCaller autoCaller(this);
2995 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2996
2997 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2998
2999 *aEnabled = mHWData->mIoCacheEnabled;
3000
3001 return S_OK;
3002}
3003
3004STDMETHODIMP Machine::COMSETTER(IoCacheEnabled)(BOOL aEnabled)
3005{
3006 AutoCaller autoCaller(this);
3007 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3008
3009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3010
3011 HRESULT rc = checkStateDependency(MutableStateDep);
3012 if (FAILED(rc)) return rc;
3013
3014 setModified(IsModified_MachineData);
3015 mHWData.backup();
3016 mHWData->mIoCacheEnabled = aEnabled;
3017
3018 return S_OK;
3019}
3020
3021STDMETHODIMP Machine::COMGETTER(IoCacheSize)(ULONG *aIoCacheSize)
3022{
3023 CheckComArgOutPointerValid(aIoCacheSize);
3024
3025 AutoCaller autoCaller(this);
3026 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3027
3028 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3029
3030 *aIoCacheSize = mHWData->mIoCacheSize;
3031
3032 return S_OK;
3033}
3034
3035STDMETHODIMP Machine::COMSETTER(IoCacheSize)(ULONG aIoCacheSize)
3036{
3037 AutoCaller autoCaller(this);
3038 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3039
3040 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3041
3042 HRESULT rc = checkStateDependency(MutableStateDep);
3043 if (FAILED(rc)) return rc;
3044
3045 setModified(IsModified_MachineData);
3046 mHWData.backup();
3047 mHWData->mIoCacheSize = aIoCacheSize;
3048
3049 return S_OK;
3050}
3051
3052
3053/**
3054 * @note Locks objects!
3055 */
3056STDMETHODIMP Machine::LockMachine(ISession *aSession,
3057 LockType_T lockType)
3058{
3059 CheckComArgNotNull(aSession);
3060
3061 AutoCaller autoCaller(this);
3062 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3063
3064 /* check the session state */
3065 SessionState_T state;
3066 HRESULT rc = aSession->COMGETTER(State)(&state);
3067 if (FAILED(rc)) return rc;
3068
3069 if (state != SessionState_Unlocked)
3070 return setError(VBOX_E_INVALID_OBJECT_STATE,
3071 tr("The given session is busy"));
3072
3073 // get the client's IInternalSessionControl interface
3074 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3075 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3076 E_INVALIDARG);
3077
3078 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3079
3080 if (!mData->mRegistered)
3081 return setError(E_UNEXPECTED,
3082 tr("The machine '%s' is not registered"),
3083 mUserData->s.strName.c_str());
3084
3085 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3086
3087 SessionState_T oldState = mData->mSession.mState;
3088 /* Hack: in case the session is closing and there is a progress object
3089 * which allows waiting for the session to be closed, take the opportunity
3090 * and do a limited wait (max. 1 second). This helps a lot when the system
3091 * is busy and thus session closing can take a little while. */
3092 if ( mData->mSession.mState == SessionState_Unlocking
3093 && mData->mSession.mProgress)
3094 {
3095 alock.release();
3096 mData->mSession.mProgress->WaitForCompletion(1000);
3097 alock.acquire();
3098 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3099 }
3100
3101 // try again now
3102 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3103 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3104 )
3105 {
3106 // OK, share the session... we are now dealing with three processes:
3107 // 1) VBoxSVC (where this code runs);
3108 // 2) process C: the caller's client process (who wants a shared session);
3109 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3110
3111 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3112 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3113 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3114 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3115 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3116
3117 /*
3118 * Release the lock before calling the client process. It's safe here
3119 * since the only thing to do after we get the lock again is to add
3120 * the remote control to the list (which doesn't directly influence
3121 * anything).
3122 */
3123 alock.release();
3124
3125 // get the console of the session holding the write lock (this is a remote call)
3126 ComPtr<IConsole> pConsoleW;
3127 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3128 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3129 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3130 if (FAILED(rc))
3131 // the failure may occur w/o any error info (from RPC), so provide one
3132 return setError(VBOX_E_VM_ERROR,
3133 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3134
3135 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3136
3137 // share the session machine and W's console with the caller's session
3138 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3139 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3140 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3141
3142 if (FAILED(rc))
3143 // the failure may occur w/o any error info (from RPC), so provide one
3144 return setError(VBOX_E_VM_ERROR,
3145 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3146 alock.acquire();
3147
3148 // need to revalidate the state after acquiring the lock again
3149 if (mData->mSession.mState != SessionState_Locked)
3150 {
3151 pSessionControl->Uninitialize();
3152 return setError(VBOX_E_INVALID_SESSION_STATE,
3153 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3154 mUserData->s.strName.c_str());
3155 }
3156
3157 // add the caller's session to the list
3158 mData->mSession.mRemoteControls.push_back(pSessionControl);
3159 }
3160 else if ( mData->mSession.mState == SessionState_Locked
3161 || mData->mSession.mState == SessionState_Unlocking
3162 )
3163 {
3164 // sharing not permitted, or machine still unlocking:
3165 return setError(VBOX_E_INVALID_OBJECT_STATE,
3166 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3167 mUserData->s.strName.c_str());
3168 }
3169 else
3170 {
3171 // machine is not locked: then write-lock the machine (create the session machine)
3172
3173 // must not be busy
3174 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3175
3176 // get the caller's session PID
3177 RTPROCESS pid = NIL_RTPROCESS;
3178 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3179 pSessionControl->GetPID((ULONG*)&pid);
3180 Assert(pid != NIL_RTPROCESS);
3181
3182 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3183
3184 if (fLaunchingVMProcess)
3185 {
3186 // this machine is awaiting for a spawning session to be opened:
3187 // then the calling process must be the one that got started by
3188 // LaunchVMProcess()
3189
3190 LogFlowThisFunc(("mSession.mPid=%d(0x%x)\n", mData->mSession.mPid, mData->mSession.mPid));
3191 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3192
3193 if (mData->mSession.mPid != pid)
3194 return setError(E_ACCESSDENIED,
3195 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3196 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3197 pid, mUserData->s.strName.c_str(), mData->mSession.mPid);
3198 }
3199
3200 // create the mutable SessionMachine from the current machine
3201 ComObjPtr<SessionMachine> sessionMachine;
3202 sessionMachine.createObject();
3203 rc = sessionMachine->init(this);
3204 AssertComRC(rc);
3205
3206 /* NOTE: doing return from this function after this point but
3207 * before the end is forbidden since it may call SessionMachine::uninit()
3208 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3209 * lock while still holding the Machine lock in alock so that a deadlock
3210 * is possible due to the wrong lock order. */
3211
3212 if (SUCCEEDED(rc))
3213 {
3214 /*
3215 * Set the session state to Spawning to protect against subsequent
3216 * attempts to open a session and to unregister the machine after
3217 * we release the lock.
3218 */
3219 SessionState_T origState = mData->mSession.mState;
3220 mData->mSession.mState = SessionState_Spawning;
3221
3222 /*
3223 * Release the lock before calling the client process -- it will call
3224 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3225 * because the state is Spawning, so that LaunchVMProcess() and
3226 * LockMachine() calls will fail. This method, called before we
3227 * acquire the lock again, will fail because of the wrong PID.
3228 *
3229 * Note that mData->mSession.mRemoteControls accessed outside
3230 * the lock may not be modified when state is Spawning, so it's safe.
3231 */
3232 alock.release();
3233
3234 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3235 rc = pSessionControl->AssignMachine(sessionMachine);
3236 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3237
3238 /* The failure may occur w/o any error info (from RPC), so provide one */
3239 if (FAILED(rc))
3240 setError(VBOX_E_VM_ERROR,
3241 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3242
3243 if ( SUCCEEDED(rc)
3244 && fLaunchingVMProcess
3245 )
3246 {
3247 /* complete the remote session initialization */
3248
3249 /* get the console from the direct session */
3250 ComPtr<IConsole> console;
3251 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3252 ComAssertComRC(rc);
3253
3254 if (SUCCEEDED(rc) && !console)
3255 {
3256 ComAssert(!!console);
3257 rc = E_FAIL;
3258 }
3259
3260 /* assign machine & console to the remote session */
3261 if (SUCCEEDED(rc))
3262 {
3263 /*
3264 * after LaunchVMProcess(), the first and the only
3265 * entry in remoteControls is that remote session
3266 */
3267 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3268 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3269 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3270
3271 /* The failure may occur w/o any error info (from RPC), so provide one */
3272 if (FAILED(rc))
3273 setError(VBOX_E_VM_ERROR,
3274 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3275 }
3276
3277 if (FAILED(rc))
3278 pSessionControl->Uninitialize();
3279 }
3280
3281 /* acquire the lock again */
3282 alock.acquire();
3283
3284 /* Restore the session state */
3285 mData->mSession.mState = origState;
3286 }
3287
3288 // finalize spawning anyway (this is why we don't return on errors above)
3289 if (fLaunchingVMProcess)
3290 {
3291 /* Note that the progress object is finalized later */
3292 /** @todo Consider checking mData->mSession.mProgress for cancellation
3293 * around here. */
3294
3295 /* We don't reset mSession.mPid here because it is necessary for
3296 * SessionMachine::uninit() to reap the child process later. */
3297
3298 if (FAILED(rc))
3299 {
3300 /* Close the remote session, remove the remote control from the list
3301 * and reset session state to Closed (@note keep the code in sync
3302 * with the relevant part in openSession()). */
3303
3304 Assert(mData->mSession.mRemoteControls.size() == 1);
3305 if (mData->mSession.mRemoteControls.size() == 1)
3306 {
3307 ErrorInfoKeeper eik;
3308 mData->mSession.mRemoteControls.front()->Uninitialize();
3309 }
3310
3311 mData->mSession.mRemoteControls.clear();
3312 mData->mSession.mState = SessionState_Unlocked;
3313 }
3314 }
3315 else
3316 {
3317 /* memorize PID of the directly opened session */
3318 if (SUCCEEDED(rc))
3319 mData->mSession.mPid = pid;
3320 }
3321
3322 if (SUCCEEDED(rc))
3323 {
3324 /* memorize the direct session control and cache IUnknown for it */
3325 mData->mSession.mDirectControl = pSessionControl;
3326 mData->mSession.mState = SessionState_Locked;
3327 /* associate the SessionMachine with this Machine */
3328 mData->mSession.mMachine = sessionMachine;
3329
3330 /* request an IUnknown pointer early from the remote party for later
3331 * identity checks (it will be internally cached within mDirectControl
3332 * at least on XPCOM) */
3333 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3334 NOREF(unk);
3335 }
3336
3337 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3338 * would break the lock order */
3339 alock.release();
3340
3341 /* uninitialize the created session machine on failure */
3342 if (FAILED(rc))
3343 sessionMachine->uninit();
3344
3345 }
3346
3347 if (SUCCEEDED(rc))
3348 {
3349 /*
3350 * tell the client watcher thread to update the set of
3351 * machines that have open sessions
3352 */
3353 mParent->updateClientWatcher();
3354
3355 if (oldState != SessionState_Locked)
3356 /* fire an event */
3357 mParent->onSessionStateChange(getId(), SessionState_Locked);
3358 }
3359
3360 return rc;
3361}
3362
3363/**
3364 * @note Locks objects!
3365 */
3366STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3367 IN_BSTR aType,
3368 IN_BSTR aEnvironment,
3369 IProgress **aProgress)
3370{
3371 CheckComArgStrNotEmptyOrNull(aType);
3372 Utf8Str strType(aType);
3373 Utf8Str strEnvironment(aEnvironment);
3374 /* "emergencystop" doesn't need the session, so skip the checks/interface
3375 * retrieval. This code doesn't quite fit in here, but introducing a
3376 * special API method would be even more effort, and would require explicit
3377 * support by every API client. It's better to hide the feature a bit. */
3378 if (strType != "emergencystop")
3379 CheckComArgNotNull(aSession);
3380 CheckComArgOutPointerValid(aProgress);
3381
3382 AutoCaller autoCaller(this);
3383 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3384
3385 ComPtr<IInternalSessionControl> control;
3386 HRESULT rc = S_OK;
3387
3388 if (strType != "emergencystop")
3389 {
3390 /* check the session state */
3391 SessionState_T state;
3392 rc = aSession->COMGETTER(State)(&state);
3393 if (FAILED(rc))
3394 return rc;
3395
3396 if (state != SessionState_Unlocked)
3397 return setError(VBOX_E_INVALID_OBJECT_STATE,
3398 tr("The given session is busy"));
3399
3400 /* get the IInternalSessionControl interface */
3401 control = aSession;
3402 ComAssertMsgRet(!control.isNull(),
3403 ("No IInternalSessionControl interface"),
3404 E_INVALIDARG);
3405 }
3406
3407 /* get the teleporter enable state for the progress object init. */
3408 BOOL fTeleporterEnabled;
3409 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3410 if (FAILED(rc))
3411 return rc;
3412
3413 /* create a progress object */
3414 if (strType != "emergencystop")
3415 {
3416 ComObjPtr<ProgressProxy> progress;
3417 progress.createObject();
3418 rc = progress->init(mParent,
3419 static_cast<IMachine*>(this),
3420 Bstr(tr("Starting VM")).raw(),
3421 TRUE /* aCancelable */,
3422 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3423 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strType.c_str()).raw(),
3424 2 /* uFirstOperationWeight */,
3425 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3426
3427 if (SUCCEEDED(rc))
3428 {
3429 rc = launchVMProcess(control, strType, strEnvironment, progress);
3430 if (SUCCEEDED(rc))
3431 {
3432 progress.queryInterfaceTo(aProgress);
3433
3434 /* signal the client watcher thread */
3435 mParent->updateClientWatcher();
3436
3437 /* fire an event */
3438 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3439 }
3440 }
3441 }
3442 else
3443 {
3444 /* no progress object - either instant success or failure */
3445 *aProgress = NULL;
3446
3447 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3448
3449 if (mData->mSession.mState != SessionState_Locked)
3450 return setError(VBOX_E_INVALID_OBJECT_STATE,
3451 tr("The machine '%s' is not locked by a session"),
3452 mUserData->s.strName.c_str());
3453
3454 /* must have a VM process associated - do not kill normal API clients
3455 * with an open session */
3456 if (!Global::IsOnline(mData->mMachineState))
3457 return setError(VBOX_E_INVALID_OBJECT_STATE,
3458 tr("The machine '%s' does not have a VM process"),
3459 mUserData->s.strName.c_str());
3460
3461 /* forcibly terminate the VM process */
3462 if (mData->mSession.mPid != NIL_RTPROCESS)
3463 RTProcTerminate(mData->mSession.mPid);
3464
3465 /* signal the client watcher thread, as most likely the client has
3466 * been terminated */
3467 mParent->updateClientWatcher();
3468 }
3469
3470 return rc;
3471}
3472
3473STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3474{
3475 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3476 return setError(E_INVALIDARG,
3477 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3478 aPosition, SchemaDefs::MaxBootPosition);
3479
3480 if (aDevice == DeviceType_USB)
3481 return setError(E_NOTIMPL,
3482 tr("Booting from USB device is currently not supported"));
3483
3484 AutoCaller autoCaller(this);
3485 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3486
3487 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3488
3489 HRESULT rc = checkStateDependency(MutableStateDep);
3490 if (FAILED(rc)) return rc;
3491
3492 setModified(IsModified_MachineData);
3493 mHWData.backup();
3494 mHWData->mBootOrder[aPosition - 1] = aDevice;
3495
3496 return S_OK;
3497}
3498
3499STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3500{
3501 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3502 return setError(E_INVALIDARG,
3503 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3504 aPosition, SchemaDefs::MaxBootPosition);
3505
3506 AutoCaller autoCaller(this);
3507 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3508
3509 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3510
3511 *aDevice = mHWData->mBootOrder[aPosition - 1];
3512
3513 return S_OK;
3514}
3515
3516STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
3517 LONG aControllerPort,
3518 LONG aDevice,
3519 DeviceType_T aType,
3520 IMedium *aMedium)
3521{
3522 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3523 aControllerName, aControllerPort, aDevice, aType, aMedium));
3524
3525 CheckComArgStrNotEmptyOrNull(aControllerName);
3526
3527 AutoCaller autoCaller(this);
3528 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3529
3530 // request the host lock first, since might be calling Host methods for getting host drives;
3531 // next, protect the media tree all the while we're in here, as well as our member variables
3532 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
3533 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3534
3535 HRESULT rc = checkStateDependency(MutableStateDep);
3536 if (FAILED(rc)) return rc;
3537
3538 /// @todo NEWMEDIA implicit machine registration
3539 if (!mData->mRegistered)
3540 return setError(VBOX_E_INVALID_OBJECT_STATE,
3541 tr("Cannot attach storage devices to an unregistered machine"));
3542
3543 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3544
3545 /* Check for an existing controller. */
3546 ComObjPtr<StorageController> ctl;
3547 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
3548 if (FAILED(rc)) return rc;
3549
3550 StorageControllerType_T ctrlType;
3551 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3552 if (FAILED(rc))
3553 return setError(E_FAIL,
3554 tr("Could not get type of controller '%ls'"),
3555 aControllerName);
3556
3557 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3558 bool fHotplug = false;
3559 if (Global::IsOnlineOrTransient(mData->mMachineState))
3560 fHotplug = true;
3561
3562 if (fHotplug && !isControllerHotplugCapable(ctrlType))
3563 return setError(VBOX_E_INVALID_VM_STATE,
3564 tr("Controller '%ls' does not support hotplugging"),
3565 aControllerName);
3566
3567 // check that the port and device are not out of range
3568 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
3569 if (FAILED(rc)) return rc;
3570
3571 /* check if the device slot is already busy */
3572 MediumAttachment *pAttachTemp;
3573 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
3574 aControllerName,
3575 aControllerPort,
3576 aDevice)))
3577 {
3578 Medium *pMedium = pAttachTemp->getMedium();
3579 if (pMedium)
3580 {
3581 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3582 return setError(VBOX_E_OBJECT_IN_USE,
3583 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3584 pMedium->getLocationFull().c_str(),
3585 aControllerPort,
3586 aDevice,
3587 aControllerName);
3588 }
3589 else
3590 return setError(VBOX_E_OBJECT_IN_USE,
3591 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3592 aControllerPort, aDevice, aControllerName);
3593 }
3594
3595 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
3596 if (aMedium && medium.isNull())
3597 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3598
3599 AutoCaller mediumCaller(medium);
3600 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3601
3602 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3603
3604 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
3605 && !medium.isNull()
3606 )
3607 return setError(VBOX_E_OBJECT_IN_USE,
3608 tr("Medium '%s' is already attached to this virtual machine"),
3609 medium->getLocationFull().c_str());
3610
3611 if (!medium.isNull())
3612 {
3613 MediumType_T mtype = medium->getType();
3614 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3615 // For DVDs it's not written to the config file, so needs no global config
3616 // version bump. For floppies it's a new attribute "type", which is ignored
3617 // by older VirtualBox version, so needs no global config version bump either.
3618 // For hard disks this type is not accepted.
3619 if (mtype == MediumType_MultiAttach)
3620 {
3621 // This type is new with VirtualBox 4.0 and therefore requires settings
3622 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3623 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3624 // two reasons: The medium type is a property of the media registry tree, which
3625 // can reside in the global config file (for pre-4.0 media); we would therefore
3626 // possibly need to bump the global config version. We don't want to do that though
3627 // because that might make downgrading to pre-4.0 impossible.
3628 // As a result, we can only use these two new types if the medium is NOT in the
3629 // global registry:
3630 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
3631 if ( medium->isInRegistry(uuidGlobalRegistry)
3632 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3633 )
3634 return setError(VBOX_E_INVALID_OBJECT_STATE,
3635 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3636 "to machines that were created with VirtualBox 4.0 or later"),
3637 medium->getLocationFull().c_str());
3638 }
3639 }
3640
3641 bool fIndirect = false;
3642 if (!medium.isNull())
3643 fIndirect = medium->isReadOnly();
3644 bool associate = true;
3645
3646 do
3647 {
3648 if ( aType == DeviceType_HardDisk
3649 && mMediaData.isBackedUp())
3650 {
3651 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3652
3653 /* check if the medium was attached to the VM before we started
3654 * changing attachments in which case the attachment just needs to
3655 * be restored */
3656 if ((pAttachTemp = findAttachment(oldAtts, medium)))
3657 {
3658 AssertReturn(!fIndirect, E_FAIL);
3659
3660 /* see if it's the same bus/channel/device */
3661 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
3662 {
3663 /* the simplest case: restore the whole attachment
3664 * and return, nothing else to do */
3665 mMediaData->mAttachments.push_back(pAttachTemp);
3666 return S_OK;
3667 }
3668
3669 /* bus/channel/device differ; we need a new attachment object,
3670 * but don't try to associate it again */
3671 associate = false;
3672 break;
3673 }
3674 }
3675
3676 /* go further only if the attachment is to be indirect */
3677 if (!fIndirect)
3678 break;
3679
3680 /* perform the so called smart attachment logic for indirect
3681 * attachments. Note that smart attachment is only applicable to base
3682 * hard disks. */
3683
3684 if (medium->getParent().isNull())
3685 {
3686 /* first, investigate the backup copy of the current hard disk
3687 * attachments to make it possible to re-attach existing diffs to
3688 * another device slot w/o losing their contents */
3689 if (mMediaData.isBackedUp())
3690 {
3691 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3692
3693 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3694 uint32_t foundLevel = 0;
3695
3696 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
3697 it != oldAtts.end();
3698 ++it)
3699 {
3700 uint32_t level = 0;
3701 MediumAttachment *pAttach = *it;
3702 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3703 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3704 if (pMedium.isNull())
3705 continue;
3706
3707 if (pMedium->getBase(&level) == medium)
3708 {
3709 /* skip the hard disk if its currently attached (we
3710 * cannot attach the same hard disk twice) */
3711 if (findAttachment(mMediaData->mAttachments,
3712 pMedium))
3713 continue;
3714
3715 /* matched device, channel and bus (i.e. attached to the
3716 * same place) will win and immediately stop the search;
3717 * otherwise the attachment that has the youngest
3718 * descendant of medium will be used
3719 */
3720 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
3721 {
3722 /* the simplest case: restore the whole attachment
3723 * and return, nothing else to do */
3724 mMediaData->mAttachments.push_back(*it);
3725 return S_OK;
3726 }
3727 else if ( foundIt == oldAtts.end()
3728 || level > foundLevel /* prefer younger */
3729 )
3730 {
3731 foundIt = it;
3732 foundLevel = level;
3733 }
3734 }
3735 }
3736
3737 if (foundIt != oldAtts.end())
3738 {
3739 /* use the previously attached hard disk */
3740 medium = (*foundIt)->getMedium();
3741 mediumCaller.attach(medium);
3742 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3743 mediumLock.attach(medium);
3744 /* not implicit, doesn't require association with this VM */
3745 fIndirect = false;
3746 associate = false;
3747 /* go right to the MediumAttachment creation */
3748 break;
3749 }
3750 }
3751
3752 /* must give up the medium lock and medium tree lock as below we
3753 * go over snapshots, which needs a lock with higher lock order. */
3754 mediumLock.release();
3755 treeLock.release();
3756
3757 /* then, search through snapshots for the best diff in the given
3758 * hard disk's chain to base the new diff on */
3759
3760 ComObjPtr<Medium> base;
3761 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3762 while (snap)
3763 {
3764 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3765
3766 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
3767
3768 MediumAttachment *pAttachFound = NULL;
3769 uint32_t foundLevel = 0;
3770
3771 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
3772 it != snapAtts.end();
3773 ++it)
3774 {
3775 MediumAttachment *pAttach = *it;
3776 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3777 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3778 if (pMedium.isNull())
3779 continue;
3780
3781 uint32_t level = 0;
3782 if (pMedium->getBase(&level) == medium)
3783 {
3784 /* matched device, channel and bus (i.e. attached to the
3785 * same place) will win and immediately stop the search;
3786 * otherwise the attachment that has the youngest
3787 * descendant of medium will be used
3788 */
3789 if ( pAttach->getDevice() == aDevice
3790 && pAttach->getPort() == aControllerPort
3791 && pAttach->getControllerName() == aControllerName
3792 )
3793 {
3794 pAttachFound = pAttach;
3795 break;
3796 }
3797 else if ( !pAttachFound
3798 || level > foundLevel /* prefer younger */
3799 )
3800 {
3801 pAttachFound = pAttach;
3802 foundLevel = level;
3803 }
3804 }
3805 }
3806
3807 if (pAttachFound)
3808 {
3809 base = pAttachFound->getMedium();
3810 break;
3811 }
3812
3813 snap = snap->getParent();
3814 }
3815
3816 /* re-lock medium tree and the medium, as we need it below */
3817 treeLock.acquire();
3818 mediumLock.acquire();
3819
3820 /* found a suitable diff, use it as a base */
3821 if (!base.isNull())
3822 {
3823 medium = base;
3824 mediumCaller.attach(medium);
3825 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3826 mediumLock.attach(medium);
3827 }
3828 }
3829
3830 Utf8Str strFullSnapshotFolder;
3831 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3832
3833 ComObjPtr<Medium> diff;
3834 diff.createObject();
3835 // store this diff in the same registry as the parent
3836 Guid uuidRegistryParent;
3837 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
3838 {
3839 // parent image has no registry: this can happen if we're attaching a new immutable
3840 // image that has not yet been attached (medium then points to the base and we're
3841 // creating the diff image for the immutable, and the parent is not yet registered);
3842 // put the parent in the machine registry then
3843 mediumLock.release();
3844 treeLock.release();
3845 alock.release();
3846 addMediumToRegistry(medium);
3847 alock.acquire();
3848 treeLock.acquire();
3849 mediumLock.acquire();
3850 medium->getFirstRegistryMachineId(uuidRegistryParent);
3851 }
3852 rc = diff->init(mParent,
3853 medium->getPreferredDiffFormat(),
3854 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3855 uuidRegistryParent);
3856 if (FAILED(rc)) return rc;
3857
3858 /* Apply the normal locking logic to the entire chain. */
3859 MediumLockList *pMediumLockList(new MediumLockList());
3860 mediumLock.release();
3861 treeLock.release();
3862 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
3863 true /* fMediumLockWrite */,
3864 medium,
3865 *pMediumLockList);
3866 treeLock.acquire();
3867 mediumLock.acquire();
3868 if (SUCCEEDED(rc))
3869 {
3870 mediumLock.release();
3871 treeLock.release();
3872 rc = pMediumLockList->Lock();
3873 treeLock.acquire();
3874 mediumLock.acquire();
3875 if (FAILED(rc))
3876 setError(rc,
3877 tr("Could not lock medium when creating diff '%s'"),
3878 diff->getLocationFull().c_str());
3879 else
3880 {
3881 /* will release the lock before the potentially lengthy
3882 * operation, so protect with the special state */
3883 MachineState_T oldState = mData->mMachineState;
3884 setMachineState(MachineState_SettingUp);
3885
3886 mediumLock.release();
3887 treeLock.release();
3888 alock.release();
3889
3890 rc = medium->createDiffStorage(diff,
3891 MediumVariant_Standard,
3892 pMediumLockList,
3893 NULL /* aProgress */,
3894 true /* aWait */);
3895
3896 alock.acquire();
3897 treeLock.acquire();
3898 mediumLock.acquire();
3899
3900 setMachineState(oldState);
3901 }
3902 }
3903
3904 /* Unlock the media and free the associated memory. */
3905 delete pMediumLockList;
3906
3907 if (FAILED(rc)) return rc;
3908
3909 /* use the created diff for the actual attachment */
3910 medium = diff;
3911 mediumCaller.attach(medium);
3912 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3913 mediumLock.attach(medium);
3914 }
3915 while (0);
3916
3917 ComObjPtr<MediumAttachment> attachment;
3918 attachment.createObject();
3919 rc = attachment->init(this,
3920 medium,
3921 aControllerName,
3922 aControllerPort,
3923 aDevice,
3924 aType,
3925 fIndirect,
3926 false /* fPassthrough */,
3927 false /* fTempEject */,
3928 false /* fNonRotational */,
3929 false /* fDiscard */,
3930 Utf8Str::Empty);
3931 if (FAILED(rc)) return rc;
3932
3933 if (associate && !medium.isNull())
3934 {
3935 // as the last step, associate the medium to the VM
3936 rc = medium->addBackReference(mData->mUuid);
3937 // here we can fail because of Deleting, or being in process of creating a Diff
3938 if (FAILED(rc)) return rc;
3939
3940 mediumLock.release();
3941 treeLock.release();
3942 alock.release();
3943 addMediumToRegistry(medium);
3944 alock.acquire();
3945 treeLock.acquire();
3946 mediumLock.acquire();
3947 }
3948
3949 /* success: finally remember the attachment */
3950 setModified(IsModified_Storage);
3951 mMediaData.backup();
3952 mMediaData->mAttachments.push_back(attachment);
3953
3954 mediumLock.release();
3955 treeLock.release();
3956 alock.release();
3957
3958 if (fHotplug)
3959 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */);
3960
3961 mParent->saveModifiedRegistries();
3962
3963 return rc;
3964}
3965
3966STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
3967 LONG aDevice)
3968{
3969 CheckComArgStrNotEmptyOrNull(aControllerName);
3970
3971 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
3972 aControllerName, aControllerPort, aDevice));
3973
3974 AutoCaller autoCaller(this);
3975 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3976
3977 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3978
3979 HRESULT rc = checkStateDependency(MutableStateDep);
3980 if (FAILED(rc)) return rc;
3981
3982 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3983
3984 /* Check for an existing controller. */
3985 ComObjPtr<StorageController> ctl;
3986 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
3987 if (FAILED(rc)) return rc;
3988
3989 StorageControllerType_T ctrlType;
3990 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3991 if (FAILED(rc))
3992 return setError(E_FAIL,
3993 tr("Could not get type of controller '%ls'"),
3994 aControllerName);
3995
3996 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3997 bool fHotplug = false;
3998 if (Global::IsOnlineOrTransient(mData->mMachineState))
3999 fHotplug = true;
4000
4001 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4002 return setError(VBOX_E_INVALID_VM_STATE,
4003 tr("Controller '%ls' does not support hotplugging"),
4004 aControllerName);
4005
4006 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4007 aControllerName,
4008 aControllerPort,
4009 aDevice);
4010 if (!pAttach)
4011 return setError(VBOX_E_OBJECT_NOT_FOUND,
4012 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4013 aDevice, aControllerPort, aControllerName);
4014
4015 /*
4016 * The VM has to detach the device before we delete any implicit diffs.
4017 * If this fails we can roll back without loosing data.
4018 */
4019 if (fHotplug)
4020 {
4021 alock.release();
4022 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */);
4023 alock.acquire();
4024 }
4025 if (FAILED(rc)) return rc;
4026
4027 /* If we are here everything went well and we can delete the implicit now. */
4028 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4029
4030 alock.release();
4031
4032 mParent->saveModifiedRegistries();
4033
4034 return rc;
4035}
4036
4037STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4038 LONG aDevice, BOOL aPassthrough)
4039{
4040 CheckComArgStrNotEmptyOrNull(aControllerName);
4041
4042 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4043 aControllerName, aControllerPort, aDevice, aPassthrough));
4044
4045 AutoCaller autoCaller(this);
4046 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4047
4048 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4049
4050 HRESULT rc = checkStateDependency(MutableStateDep);
4051 if (FAILED(rc)) return rc;
4052
4053 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4054
4055 if (Global::IsOnlineOrTransient(mData->mMachineState))
4056 return setError(VBOX_E_INVALID_VM_STATE,
4057 tr("Invalid machine state: %s"),
4058 Global::stringifyMachineState(mData->mMachineState));
4059
4060 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4061 aControllerName,
4062 aControllerPort,
4063 aDevice);
4064 if (!pAttach)
4065 return setError(VBOX_E_OBJECT_NOT_FOUND,
4066 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4067 aDevice, aControllerPort, aControllerName);
4068
4069
4070 setModified(IsModified_Storage);
4071 mMediaData.backup();
4072
4073 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4074
4075 if (pAttach->getType() != DeviceType_DVD)
4076 return setError(E_INVALIDARG,
4077 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4078 aDevice, aControllerPort, aControllerName);
4079 pAttach->updatePassthrough(!!aPassthrough);
4080
4081 return S_OK;
4082}
4083
4084STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4085 LONG aDevice, BOOL aTemporaryEject)
4086{
4087 CheckComArgStrNotEmptyOrNull(aControllerName);
4088
4089 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4090 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4091
4092 AutoCaller autoCaller(this);
4093 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4094
4095 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4096
4097 HRESULT rc = checkStateDependency(MutableStateDep);
4098 if (FAILED(rc)) return rc;
4099
4100 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4101 aControllerName,
4102 aControllerPort,
4103 aDevice);
4104 if (!pAttach)
4105 return setError(VBOX_E_OBJECT_NOT_FOUND,
4106 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4107 aDevice, aControllerPort, aControllerName);
4108
4109
4110 setModified(IsModified_Storage);
4111 mMediaData.backup();
4112
4113 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4114
4115 if (pAttach->getType() != DeviceType_DVD)
4116 return setError(E_INVALIDARG,
4117 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4118 aDevice, aControllerPort, aControllerName);
4119 pAttach->updateTempEject(!!aTemporaryEject);
4120
4121 return S_OK;
4122}
4123
4124STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4125 LONG aDevice, BOOL aNonRotational)
4126{
4127 CheckComArgStrNotEmptyOrNull(aControllerName);
4128
4129 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4130 aControllerName, aControllerPort, aDevice, aNonRotational));
4131
4132 AutoCaller autoCaller(this);
4133 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4134
4135 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4136
4137 HRESULT rc = checkStateDependency(MutableStateDep);
4138 if (FAILED(rc)) return rc;
4139
4140 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4141
4142 if (Global::IsOnlineOrTransient(mData->mMachineState))
4143 return setError(VBOX_E_INVALID_VM_STATE,
4144 tr("Invalid machine state: %s"),
4145 Global::stringifyMachineState(mData->mMachineState));
4146
4147 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4148 aControllerName,
4149 aControllerPort,
4150 aDevice);
4151 if (!pAttach)
4152 return setError(VBOX_E_OBJECT_NOT_FOUND,
4153 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4154 aDevice, aControllerPort, aControllerName);
4155
4156
4157 setModified(IsModified_Storage);
4158 mMediaData.backup();
4159
4160 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4161
4162 if (pAttach->getType() != DeviceType_HardDisk)
4163 return setError(E_INVALIDARG,
4164 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a hard disk"),
4165 aDevice, aControllerPort, aControllerName);
4166 pAttach->updateNonRotational(!!aNonRotational);
4167
4168 return S_OK;
4169}
4170
4171STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4172 LONG aDevice, BOOL aDiscard)
4173{
4174 CheckComArgStrNotEmptyOrNull(aControllerName);
4175
4176 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4177 aControllerName, aControllerPort, aDevice, aDiscard));
4178
4179 AutoCaller autoCaller(this);
4180 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4181
4182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4183
4184 HRESULT rc = checkStateDependency(MutableStateDep);
4185 if (FAILED(rc)) return rc;
4186
4187 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4188
4189 if (Global::IsOnlineOrTransient(mData->mMachineState))
4190 return setError(VBOX_E_INVALID_VM_STATE,
4191 tr("Invalid machine state: %s"),
4192 Global::stringifyMachineState(mData->mMachineState));
4193
4194 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4195 aControllerName,
4196 aControllerPort,
4197 aDevice);
4198 if (!pAttach)
4199 return setError(VBOX_E_OBJECT_NOT_FOUND,
4200 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4201 aDevice, aControllerPort, aControllerName);
4202
4203
4204 setModified(IsModified_Storage);
4205 mMediaData.backup();
4206
4207 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4208
4209 if (pAttach->getType() != DeviceType_HardDisk)
4210 return setError(E_INVALIDARG,
4211 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a hard disk"),
4212 aDevice, aControllerPort, aControllerName);
4213 pAttach->updateDiscard(!!aDiscard);
4214
4215 return S_OK;
4216}
4217
4218STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4219 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4220{
4221 CheckComArgStrNotEmptyOrNull(aControllerName);
4222
4223 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4224 aControllerName, aControllerPort, aDevice));
4225
4226 AutoCaller autoCaller(this);
4227 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4228
4229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4230
4231 HRESULT rc = checkStateDependency(MutableStateDep);
4232 if (FAILED(rc)) return rc;
4233
4234 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4235
4236 if (Global::IsOnlineOrTransient(mData->mMachineState))
4237 return setError(VBOX_E_INVALID_VM_STATE,
4238 tr("Invalid machine state: %s"),
4239 Global::stringifyMachineState(mData->mMachineState));
4240
4241 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4242 aControllerName,
4243 aControllerPort,
4244 aDevice);
4245 if (!pAttach)
4246 return setError(VBOX_E_OBJECT_NOT_FOUND,
4247 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4248 aDevice, aControllerPort, aControllerName);
4249
4250
4251 setModified(IsModified_Storage);
4252 mMediaData.backup();
4253
4254 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4255 if (aBandwidthGroup && group.isNull())
4256 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4257
4258 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4259
4260 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4261 if (strBandwidthGroupOld.isNotEmpty())
4262 {
4263 /* Get the bandwidth group object and release it - this must not fail. */
4264 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4265 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4266 Assert(SUCCEEDED(rc));
4267
4268 pBandwidthGroupOld->release();
4269 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4270 }
4271
4272 if (!group.isNull())
4273 {
4274 group->reference();
4275 pAttach->updateBandwidthGroup(group->getName());
4276 }
4277
4278 return S_OK;
4279}
4280
4281
4282STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4283 LONG aControllerPort,
4284 LONG aDevice,
4285 IMedium *aMedium,
4286 BOOL aForce)
4287{
4288 int rc = S_OK;
4289 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4290 aControllerName, aControllerPort, aDevice, aForce));
4291
4292 CheckComArgStrNotEmptyOrNull(aControllerName);
4293
4294 AutoCaller autoCaller(this);
4295 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4296
4297 // request the host lock first, since might be calling Host methods for getting host drives;
4298 // next, protect the media tree all the while we're in here, as well as our member variables
4299 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4300 this->lockHandle(),
4301 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4302
4303 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4304 aControllerName,
4305 aControllerPort,
4306 aDevice);
4307 if (pAttach.isNull())
4308 return setError(VBOX_E_OBJECT_NOT_FOUND,
4309 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4310 aDevice, aControllerPort, aControllerName);
4311
4312 /* Remember previously mounted medium. The medium before taking the
4313 * backup is not necessarily the same thing. */
4314 ComObjPtr<Medium> oldmedium;
4315 oldmedium = pAttach->getMedium();
4316
4317 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4318 if (aMedium && pMedium.isNull())
4319 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4320
4321 AutoCaller mediumCaller(pMedium);
4322 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4323
4324 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4325 if (pMedium)
4326 {
4327 DeviceType_T mediumType = pAttach->getType();
4328 switch (mediumType)
4329 {
4330 case DeviceType_DVD:
4331 case DeviceType_Floppy:
4332 break;
4333
4334 default:
4335 return setError(VBOX_E_INVALID_OBJECT_STATE,
4336 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
4337 aControllerPort,
4338 aDevice,
4339 aControllerName);
4340 }
4341 }
4342
4343 setModified(IsModified_Storage);
4344 mMediaData.backup();
4345
4346 {
4347 // The backup operation makes the pAttach reference point to the
4348 // old settings. Re-get the correct reference.
4349 pAttach = findAttachment(mMediaData->mAttachments,
4350 aControllerName,
4351 aControllerPort,
4352 aDevice);
4353 if (!oldmedium.isNull())
4354 oldmedium->removeBackReference(mData->mUuid);
4355 if (!pMedium.isNull())
4356 {
4357 pMedium->addBackReference(mData->mUuid);
4358
4359 mediumLock.release();
4360 multiLock.release();
4361 addMediumToRegistry(pMedium);
4362 multiLock.acquire();
4363 mediumLock.acquire();
4364 }
4365
4366 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4367 pAttach->updateMedium(pMedium);
4368 }
4369
4370 setModified(IsModified_Storage);
4371
4372 mediumLock.release();
4373 multiLock.release();
4374 rc = onMediumChange(pAttach, aForce);
4375 multiLock.acquire();
4376 mediumLock.acquire();
4377
4378 /* On error roll back this change only. */
4379 if (FAILED(rc))
4380 {
4381 if (!pMedium.isNull())
4382 pMedium->removeBackReference(mData->mUuid);
4383 pAttach = findAttachment(mMediaData->mAttachments,
4384 aControllerName,
4385 aControllerPort,
4386 aDevice);
4387 /* If the attachment is gone in the meantime, bail out. */
4388 if (pAttach.isNull())
4389 return rc;
4390 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4391 if (!oldmedium.isNull())
4392 oldmedium->addBackReference(mData->mUuid);
4393 pAttach->updateMedium(oldmedium);
4394 }
4395
4396 mediumLock.release();
4397 multiLock.release();
4398
4399 mParent->saveModifiedRegistries();
4400
4401 return rc;
4402}
4403
4404STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
4405 LONG aControllerPort,
4406 LONG aDevice,
4407 IMedium **aMedium)
4408{
4409 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4410 aControllerName, aControllerPort, aDevice));
4411
4412 CheckComArgStrNotEmptyOrNull(aControllerName);
4413 CheckComArgOutPointerValid(aMedium);
4414
4415 AutoCaller autoCaller(this);
4416 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4417
4418 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4419
4420 *aMedium = NULL;
4421
4422 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4423 aControllerName,
4424 aControllerPort,
4425 aDevice);
4426 if (pAttach.isNull())
4427 return setError(VBOX_E_OBJECT_NOT_FOUND,
4428 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4429 aDevice, aControllerPort, aControllerName);
4430
4431 pAttach->getMedium().queryInterfaceTo(aMedium);
4432
4433 return S_OK;
4434}
4435
4436STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
4437{
4438 CheckComArgOutPointerValid(port);
4439 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
4440
4441 AutoCaller autoCaller(this);
4442 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4443
4444 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4445
4446 mSerialPorts[slot].queryInterfaceTo(port);
4447
4448 return S_OK;
4449}
4450
4451STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
4452{
4453 CheckComArgOutPointerValid(port);
4454 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
4455
4456 AutoCaller autoCaller(this);
4457 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4458
4459 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4460
4461 mParallelPorts[slot].queryInterfaceTo(port);
4462
4463 return S_OK;
4464}
4465
4466STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
4467{
4468 CheckComArgOutPointerValid(adapter);
4469 CheckComArgExpr(slot, slot < mNetworkAdapters.size());
4470
4471 AutoCaller autoCaller(this);
4472 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4473
4474 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4475
4476 mNetworkAdapters[slot].queryInterfaceTo(adapter);
4477
4478 return S_OK;
4479}
4480
4481STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
4482{
4483 if (ComSafeArrayOutIsNull(aKeys))
4484 return E_POINTER;
4485
4486 AutoCaller autoCaller(this);
4487 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4488
4489 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4490
4491 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4492 int i = 0;
4493 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4494 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4495 ++it, ++i)
4496 {
4497 const Utf8Str &strKey = it->first;
4498 strKey.cloneTo(&saKeys[i]);
4499 }
4500 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
4501
4502 return S_OK;
4503 }
4504
4505 /**
4506 * @note Locks this object for reading.
4507 */
4508STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
4509 BSTR *aValue)
4510{
4511 CheckComArgStrNotEmptyOrNull(aKey);
4512 CheckComArgOutPointerValid(aValue);
4513
4514 AutoCaller autoCaller(this);
4515 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4516
4517 /* start with nothing found */
4518 Bstr bstrResult("");
4519
4520 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4521
4522 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
4523 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4524 // found:
4525 bstrResult = it->second; // source is a Utf8Str
4526
4527 /* return the result to caller (may be empty) */
4528 bstrResult.cloneTo(aValue);
4529
4530 return S_OK;
4531}
4532
4533 /**
4534 * @note Locks mParent for writing + this object for writing.
4535 */
4536STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
4537{
4538 CheckComArgStrNotEmptyOrNull(aKey);
4539
4540 AutoCaller autoCaller(this);
4541 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4542
4543 Utf8Str strKey(aKey);
4544 Utf8Str strValue(aValue);
4545 Utf8Str strOldValue; // empty
4546
4547 // locking note: we only hold the read lock briefly to look up the old value,
4548 // then release it and call the onExtraCanChange callbacks. There is a small
4549 // chance of a race insofar as the callback might be called twice if two callers
4550 // change the same key at the same time, but that's a much better solution
4551 // than the deadlock we had here before. The actual changing of the extradata
4552 // is then performed under the write lock and race-free.
4553
4554 // look up the old value first; if nothing has changed then we need not do anything
4555 {
4556 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4557 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
4558 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4559 strOldValue = it->second;
4560 }
4561
4562 bool fChanged;
4563 if ((fChanged = (strOldValue != strValue)))
4564 {
4565 // ask for permission from all listeners outside the locks;
4566 // onExtraDataCanChange() only briefly requests the VirtualBox
4567 // lock to copy the list of callbacks to invoke
4568 Bstr error;
4569 Bstr bstrValue(aValue);
4570
4571 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
4572 {
4573 const char *sep = error.isEmpty() ? "" : ": ";
4574 CBSTR err = error.raw();
4575 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4576 sep, err));
4577 return setError(E_ACCESSDENIED,
4578 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
4579 aKey,
4580 bstrValue.raw(),
4581 sep,
4582 err);
4583 }
4584
4585 // data is changing and change not vetoed: then write it out under the lock
4586 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4587
4588 if (isSnapshotMachine())
4589 {
4590 HRESULT rc = checkStateDependency(MutableStateDep);
4591 if (FAILED(rc)) return rc;
4592 }
4593
4594 if (strValue.isEmpty())
4595 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
4596 else
4597 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
4598 // creates a new key if needed
4599
4600 bool fNeedsGlobalSaveSettings = false;
4601 saveSettings(&fNeedsGlobalSaveSettings);
4602
4603 if (fNeedsGlobalSaveSettings)
4604 {
4605 // save the global settings; for that we should hold only the VirtualBox lock
4606 alock.release();
4607 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4608 mParent->saveSettings();
4609 }
4610 }
4611
4612 // fire notification outside the lock
4613 if (fChanged)
4614 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
4615
4616 return S_OK;
4617}
4618
4619STDMETHODIMP Machine::SaveSettings()
4620{
4621 AutoCaller autoCaller(this);
4622 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4623
4624 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4625
4626 /* when there was auto-conversion, we want to save the file even if
4627 * the VM is saved */
4628 HRESULT rc = checkStateDependency(MutableStateDep);
4629 if (FAILED(rc)) return rc;
4630
4631 /* the settings file path may never be null */
4632 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4633
4634 /* save all VM data excluding snapshots */
4635 bool fNeedsGlobalSaveSettings = false;
4636 rc = saveSettings(&fNeedsGlobalSaveSettings);
4637 mlock.release();
4638
4639 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4640 {
4641 // save the global settings; for that we should hold only the VirtualBox lock
4642 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4643 rc = mParent->saveSettings();
4644 }
4645
4646 return rc;
4647}
4648
4649STDMETHODIMP Machine::DiscardSettings()
4650{
4651 AutoCaller autoCaller(this);
4652 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4653
4654 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4655
4656 HRESULT rc = checkStateDependency(MutableStateDep);
4657 if (FAILED(rc)) return rc;
4658
4659 /*
4660 * during this rollback, the session will be notified if data has
4661 * been actually changed
4662 */
4663 rollback(true /* aNotify */);
4664
4665 return S_OK;
4666}
4667
4668/** @note Locks objects! */
4669STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
4670 ComSafeArrayOut(IMedium*, aMedia))
4671{
4672 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
4673 AutoLimitedCaller autoCaller(this);
4674 AssertComRCReturnRC(autoCaller.rc());
4675
4676 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4677
4678 Guid id(getId());
4679
4680 if (mData->mSession.mState != SessionState_Unlocked)
4681 return setError(VBOX_E_INVALID_OBJECT_STATE,
4682 tr("Cannot unregister the machine '%s' while it is locked"),
4683 mUserData->s.strName.c_str());
4684
4685 // wait for state dependents to drop to zero
4686 ensureNoStateDependencies();
4687
4688 if (!mData->mAccessible)
4689 {
4690 // inaccessible maschines can only be unregistered; uninitialize ourselves
4691 // here because currently there may be no unregistered that are inaccessible
4692 // (this state combination is not supported). Note releasing the caller and
4693 // leaving the lock before calling uninit()
4694 alock.release();
4695 autoCaller.release();
4696
4697 uninit();
4698
4699 mParent->unregisterMachine(this, id);
4700 // calls VirtualBox::saveSettings()
4701
4702 return S_OK;
4703 }
4704
4705 HRESULT rc = S_OK;
4706
4707 // discard saved state
4708 if (mData->mMachineState == MachineState_Saved)
4709 {
4710 // add the saved state file to the list of files the caller should delete
4711 Assert(!mSSData->strStateFilePath.isEmpty());
4712 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4713
4714 mSSData->strStateFilePath.setNull();
4715
4716 // unconditionally set the machine state to powered off, we now
4717 // know no session has locked the machine
4718 mData->mMachineState = MachineState_PoweredOff;
4719 }
4720
4721 size_t cSnapshots = 0;
4722 if (mData->mFirstSnapshot)
4723 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
4724 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
4725 // fail now before we start detaching media
4726 return setError(VBOX_E_INVALID_OBJECT_STATE,
4727 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
4728 mUserData->s.strName.c_str(), cSnapshots);
4729
4730 // This list collects the medium objects from all medium attachments
4731 // which we will detach from the machine and its snapshots, in a specific
4732 // order which allows for closing all media without getting "media in use"
4733 // errors, simply by going through the list from the front to the back:
4734 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4735 // and must be closed before the parent media from the snapshots, or closing the parents
4736 // will fail because they still have children);
4737 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4738 // the root ("first") snapshot of the machine.
4739 MediaList llMedia;
4740
4741 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
4742 && mMediaData->mAttachments.size()
4743 )
4744 {
4745 // we have media attachments: detach them all and add the Medium objects to our list
4746 if (cleanupMode != CleanupMode_UnregisterOnly)
4747 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
4748 else
4749 return setError(VBOX_E_INVALID_OBJECT_STATE,
4750 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
4751 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
4752 }
4753
4754 if (cSnapshots)
4755 {
4756 // autoCleanup must be true here, or we would have failed above
4757
4758 // add the media from the medium attachments of the snapshots to llMedia
4759 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4760 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4761 // into the children first
4762
4763 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4764 MachineState_T oldState = mData->mMachineState;
4765 mData->mMachineState = MachineState_DeletingSnapshot;
4766
4767 // make a copy of the first snapshot so the refcount does not drop to 0
4768 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
4769 // because of the AutoCaller voodoo)
4770 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4771
4772 // GO!
4773 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
4774
4775 mData->mMachineState = oldState;
4776 }
4777
4778 if (FAILED(rc))
4779 {
4780 rollbackMedia();
4781 return rc;
4782 }
4783
4784 // commit all the media changes made above
4785 commitMedia();
4786
4787 mData->mRegistered = false;
4788
4789 // machine lock no longer needed
4790 alock.release();
4791
4792 // return media to caller
4793 SafeIfaceArray<IMedium> sfaMedia(llMedia);
4794 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
4795
4796 mParent->unregisterMachine(this, id);
4797 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
4798
4799 return S_OK;
4800}
4801
4802struct Machine::DeleteTask
4803{
4804 ComObjPtr<Machine> pMachine;
4805 RTCList< ComPtr<IMedium> > llMediums;
4806 std::list<Utf8Str> llFilesToDelete;
4807 ComObjPtr<Progress> pProgress;
4808};
4809
4810STDMETHODIMP Machine::Delete(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
4811{
4812 LogFlowFuncEnter();
4813
4814 AutoCaller autoCaller(this);
4815 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4816
4817 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4818
4819 HRESULT rc = checkStateDependency(MutableStateDep);
4820 if (FAILED(rc)) return rc;
4821
4822 if (mData->mRegistered)
4823 return setError(VBOX_E_INVALID_VM_STATE,
4824 tr("Cannot delete settings of a registered machine"));
4825
4826 DeleteTask *pTask = new DeleteTask;
4827 pTask->pMachine = this;
4828 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
4829
4830 // collect files to delete
4831 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
4832
4833 for (size_t i = 0; i < sfaMedia.size(); ++i)
4834 {
4835 IMedium *pIMedium(sfaMedia[i]);
4836 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
4837 if (pMedium.isNull())
4838 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
4839 SafeArray<BSTR> ids;
4840 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
4841 if (FAILED(rc)) return rc;
4842 /* At this point the medium should not have any back references
4843 * anymore. If it has it is attached to another VM and *must* not
4844 * deleted. */
4845 if (ids.size() < 1)
4846 pTask->llMediums.append(pMedium);
4847 }
4848 if (mData->pMachineConfigFile->fileExists())
4849 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
4850
4851 pTask->pProgress.createObject();
4852 pTask->pProgress->init(getVirtualBox(),
4853 static_cast<IMachine*>(this) /* aInitiator */,
4854 Bstr(tr("Deleting files")).raw(),
4855 true /* fCancellable */,
4856 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
4857 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
4858
4859 int vrc = RTThreadCreate(NULL,
4860 Machine::deleteThread,
4861 (void*)pTask,
4862 0,
4863 RTTHREADTYPE_MAIN_WORKER,
4864 0,
4865 "MachineDelete");
4866
4867 pTask->pProgress.queryInterfaceTo(aProgress);
4868
4869 if (RT_FAILURE(vrc))
4870 {
4871 delete pTask;
4872 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
4873 }
4874
4875 LogFlowFuncLeave();
4876
4877 return S_OK;
4878}
4879
4880/**
4881 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
4882 * calls Machine::deleteTaskWorker() on the actual machine object.
4883 * @param Thread
4884 * @param pvUser
4885 * @return
4886 */
4887/*static*/
4888DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
4889{
4890 LogFlowFuncEnter();
4891
4892 DeleteTask *pTask = (DeleteTask*)pvUser;
4893 Assert(pTask);
4894 Assert(pTask->pMachine);
4895 Assert(pTask->pProgress);
4896
4897 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
4898 pTask->pProgress->notifyComplete(rc);
4899
4900 delete pTask;
4901
4902 LogFlowFuncLeave();
4903
4904 NOREF(Thread);
4905
4906 return VINF_SUCCESS;
4907}
4908
4909/**
4910 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
4911 * @param task
4912 * @return
4913 */
4914HRESULT Machine::deleteTaskWorker(DeleteTask &task)
4915{
4916 AutoCaller autoCaller(this);
4917 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4918
4919 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4920
4921 HRESULT rc = S_OK;
4922
4923 try
4924 {
4925 ULONG uLogHistoryCount = 3;
4926 ComPtr<ISystemProperties> systemProperties;
4927 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
4928 if (FAILED(rc)) throw rc;
4929
4930 if (!systemProperties.isNull())
4931 {
4932 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
4933 if (FAILED(rc)) throw rc;
4934 }
4935
4936 MachineState_T oldState = mData->mMachineState;
4937 setMachineState(MachineState_SettingUp);
4938 alock.release();
4939 for (size_t i = 0; i < task.llMediums.size(); ++i)
4940 {
4941 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
4942 {
4943 AutoCaller mac(pMedium);
4944 if (FAILED(mac.rc())) throw mac.rc();
4945 Utf8Str strLocation = pMedium->getLocationFull();
4946 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
4947 if (FAILED(rc)) throw rc;
4948 LogFunc(("Deleting file %s\n", strLocation.c_str()));
4949 }
4950 ComPtr<IProgress> pProgress2;
4951 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
4952 if (FAILED(rc)) throw rc;
4953 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
4954 if (FAILED(rc)) throw rc;
4955 /* Check the result of the asynchrony process. */
4956 LONG iRc;
4957 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
4958 if (FAILED(rc)) throw rc;
4959 /* If the thread of the progress object has an error, then
4960 * retrieve the error info from there, or it'll be lost. */
4961 if (FAILED(iRc))
4962 throw setError(ProgressErrorInfo(pProgress2));
4963 }
4964 setMachineState(oldState);
4965 alock.acquire();
4966
4967 // delete the files pushed on the task list by Machine::Delete()
4968 // (this includes saved states of the machine and snapshots and
4969 // medium storage files from the IMedium list passed in, and the
4970 // machine XML file)
4971 std::list<Utf8Str>::const_iterator it = task.llFilesToDelete.begin();
4972 while (it != task.llFilesToDelete.end())
4973 {
4974 const Utf8Str &strFile = *it;
4975 LogFunc(("Deleting file %s\n", strFile.c_str()));
4976 int vrc = RTFileDelete(strFile.c_str());
4977 if (RT_FAILURE(vrc))
4978 throw setError(VBOX_E_IPRT_ERROR,
4979 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
4980
4981 ++it;
4982 if (it == task.llFilesToDelete.end())
4983 {
4984 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
4985 if (FAILED(rc)) throw rc;
4986 break;
4987 }
4988
4989 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
4990 if (FAILED(rc)) throw rc;
4991 }
4992
4993 /* delete the settings only when the file actually exists */
4994 if (mData->pMachineConfigFile->fileExists())
4995 {
4996 /* Delete any backup or uncommitted XML files. Ignore failures.
4997 See the fSafe parameter of xml::XmlFileWriter::write for details. */
4998 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
4999 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5000 RTFileDelete(otherXml.c_str());
5001 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5002 RTFileDelete(otherXml.c_str());
5003
5004 /* delete the Logs folder, nothing important should be left
5005 * there (we don't check for errors because the user might have
5006 * some private files there that we don't want to delete) */
5007 Utf8Str logFolder;
5008 getLogFolder(logFolder);
5009 Assert(logFolder.length());
5010 if (RTDirExists(logFolder.c_str()))
5011 {
5012 /* Delete all VBox.log[.N] files from the Logs folder
5013 * (this must be in sync with the rotation logic in
5014 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5015 * files that may have been created by the GUI. */
5016 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5017 logFolder.c_str(), RTPATH_DELIMITER);
5018 RTFileDelete(log.c_str());
5019 log = Utf8StrFmt("%s%cVBox.png",
5020 logFolder.c_str(), RTPATH_DELIMITER);
5021 RTFileDelete(log.c_str());
5022 for (int i = uLogHistoryCount; i > 0; i--)
5023 {
5024 log = Utf8StrFmt("%s%cVBox.log.%d",
5025 logFolder.c_str(), RTPATH_DELIMITER, i);
5026 RTFileDelete(log.c_str());
5027 log = Utf8StrFmt("%s%cVBox.png.%d",
5028 logFolder.c_str(), RTPATH_DELIMITER, i);
5029 RTFileDelete(log.c_str());
5030 }
5031
5032 RTDirRemove(logFolder.c_str());
5033 }
5034
5035 /* delete the Snapshots folder, nothing important should be left
5036 * there (we don't check for errors because the user might have
5037 * some private files there that we don't want to delete) */
5038 Utf8Str strFullSnapshotFolder;
5039 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5040 Assert(!strFullSnapshotFolder.isEmpty());
5041 if (RTDirExists(strFullSnapshotFolder.c_str()))
5042 RTDirRemove(strFullSnapshotFolder.c_str());
5043
5044 // delete the directory that contains the settings file, but only
5045 // if it matches the VM name
5046 Utf8Str settingsDir;
5047 if (isInOwnDir(&settingsDir))
5048 RTDirRemove(settingsDir.c_str());
5049 }
5050
5051 alock.release();
5052
5053 mParent->saveModifiedRegistries();
5054 }
5055 catch (HRESULT aRC) { rc = aRC; }
5056
5057 return rc;
5058}
5059
5060STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5061{
5062 CheckComArgOutPointerValid(aSnapshot);
5063
5064 AutoCaller autoCaller(this);
5065 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5066
5067 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5068
5069 ComObjPtr<Snapshot> pSnapshot;
5070 HRESULT rc;
5071
5072 if (!aNameOrId || !*aNameOrId)
5073 // null case (caller wants root snapshot): findSnapshotById() handles this
5074 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5075 else
5076 {
5077 Guid uuid(aNameOrId);
5078 if (!uuid.isEmpty())
5079 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5080 else
5081 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5082 }
5083 pSnapshot.queryInterfaceTo(aSnapshot);
5084
5085 return rc;
5086}
5087
5088STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5089{
5090 CheckComArgStrNotEmptyOrNull(aName);
5091 CheckComArgStrNotEmptyOrNull(aHostPath);
5092
5093 AutoCaller autoCaller(this);
5094 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5095
5096 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5097
5098 HRESULT rc = checkStateDependency(MutableStateDep);
5099 if (FAILED(rc)) return rc;
5100
5101 Utf8Str strName(aName);
5102
5103 ComObjPtr<SharedFolder> sharedFolder;
5104 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5105 if (SUCCEEDED(rc))
5106 return setError(VBOX_E_OBJECT_IN_USE,
5107 tr("Shared folder named '%s' already exists"),
5108 strName.c_str());
5109
5110 sharedFolder.createObject();
5111 rc = sharedFolder->init(getMachine(),
5112 strName,
5113 aHostPath,
5114 !!aWritable,
5115 !!aAutoMount,
5116 true /* fFailOnError */);
5117 if (FAILED(rc)) return rc;
5118
5119 setModified(IsModified_SharedFolders);
5120 mHWData.backup();
5121 mHWData->mSharedFolders.push_back(sharedFolder);
5122
5123 /* inform the direct session if any */
5124 alock.release();
5125 onSharedFolderChange();
5126
5127 return S_OK;
5128}
5129
5130STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5131{
5132 CheckComArgStrNotEmptyOrNull(aName);
5133
5134 AutoCaller autoCaller(this);
5135 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5136
5137 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5138
5139 HRESULT rc = checkStateDependency(MutableStateDep);
5140 if (FAILED(rc)) return rc;
5141
5142 ComObjPtr<SharedFolder> sharedFolder;
5143 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5144 if (FAILED(rc)) return rc;
5145
5146 setModified(IsModified_SharedFolders);
5147 mHWData.backup();
5148 mHWData->mSharedFolders.remove(sharedFolder);
5149
5150 /* inform the direct session if any */
5151 alock.release();
5152 onSharedFolderChange();
5153
5154 return S_OK;
5155}
5156
5157STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5158{
5159 CheckComArgOutPointerValid(aCanShow);
5160
5161 /* start with No */
5162 *aCanShow = FALSE;
5163
5164 AutoCaller autoCaller(this);
5165 AssertComRCReturnRC(autoCaller.rc());
5166
5167 ComPtr<IInternalSessionControl> directControl;
5168 {
5169 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5170
5171 if (mData->mSession.mState != SessionState_Locked)
5172 return setError(VBOX_E_INVALID_VM_STATE,
5173 tr("Machine is not locked for session (session state: %s)"),
5174 Global::stringifySessionState(mData->mSession.mState));
5175
5176 directControl = mData->mSession.mDirectControl;
5177 }
5178
5179 /* ignore calls made after #OnSessionEnd() is called */
5180 if (!directControl)
5181 return S_OK;
5182
5183 LONG64 dummy;
5184 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5185}
5186
5187STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5188{
5189 CheckComArgOutPointerValid(aWinId);
5190
5191 AutoCaller autoCaller(this);
5192 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5193
5194 ComPtr<IInternalSessionControl> directControl;
5195 {
5196 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5197
5198 if (mData->mSession.mState != SessionState_Locked)
5199 return setError(E_FAIL,
5200 tr("Machine is not locked for session (session state: %s)"),
5201 Global::stringifySessionState(mData->mSession.mState));
5202
5203 directControl = mData->mSession.mDirectControl;
5204 }
5205
5206 /* ignore calls made after #OnSessionEnd() is called */
5207 if (!directControl)
5208 return S_OK;
5209
5210 BOOL dummy;
5211 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5212}
5213
5214#ifdef VBOX_WITH_GUEST_PROPS
5215/**
5216 * Look up a guest property in VBoxSVC's internal structures.
5217 */
5218HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5219 BSTR *aValue,
5220 LONG64 *aTimestamp,
5221 BSTR *aFlags) const
5222{
5223 using namespace guestProp;
5224
5225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5226 Utf8Str strName(aName);
5227 HWData::GuestPropertyList::const_iterator it;
5228
5229 for (it = mHWData->mGuestProperties.begin();
5230 it != mHWData->mGuestProperties.end(); ++it)
5231 {
5232 if (it->strName == strName)
5233 {
5234 char szFlags[MAX_FLAGS_LEN + 1];
5235 it->strValue.cloneTo(aValue);
5236 *aTimestamp = it->mTimestamp;
5237 writeFlags(it->mFlags, szFlags);
5238 Bstr(szFlags).cloneTo(aFlags);
5239 break;
5240 }
5241 }
5242 return S_OK;
5243}
5244
5245/**
5246 * Query the VM that a guest property belongs to for the property.
5247 * @returns E_ACCESSDENIED if the VM process is not available or not
5248 * currently handling queries and the lookup should then be done in
5249 * VBoxSVC.
5250 */
5251HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5252 BSTR *aValue,
5253 LONG64 *aTimestamp,
5254 BSTR *aFlags) const
5255{
5256 HRESULT rc;
5257 ComPtr<IInternalSessionControl> directControl;
5258 directControl = mData->mSession.mDirectControl;
5259
5260 /* fail if we were called after #OnSessionEnd() is called. This is a
5261 * silly race condition. */
5262
5263 if (!directControl)
5264 rc = E_ACCESSDENIED;
5265 else
5266 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5267 false /* isSetter */,
5268 aValue, aTimestamp, aFlags);
5269 return rc;
5270}
5271#endif // VBOX_WITH_GUEST_PROPS
5272
5273STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5274 BSTR *aValue,
5275 LONG64 *aTimestamp,
5276 BSTR *aFlags)
5277{
5278#ifndef VBOX_WITH_GUEST_PROPS
5279 ReturnComNotImplemented();
5280#else // VBOX_WITH_GUEST_PROPS
5281 CheckComArgStrNotEmptyOrNull(aName);
5282 CheckComArgOutPointerValid(aValue);
5283 CheckComArgOutPointerValid(aTimestamp);
5284 CheckComArgOutPointerValid(aFlags);
5285
5286 AutoCaller autoCaller(this);
5287 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5288
5289 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5290 if (rc == E_ACCESSDENIED)
5291 /* The VM is not running or the service is not (yet) accessible */
5292 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5293 return rc;
5294#endif // VBOX_WITH_GUEST_PROPS
5295}
5296
5297STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5298{
5299 LONG64 dummyTimestamp;
5300 Bstr dummyFlags;
5301 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5302}
5303
5304STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5305{
5306 Bstr dummyValue;
5307 Bstr dummyFlags;
5308 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5309}
5310
5311#ifdef VBOX_WITH_GUEST_PROPS
5312/**
5313 * Set a guest property in VBoxSVC's internal structures.
5314 */
5315HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5316 IN_BSTR aFlags)
5317{
5318 using namespace guestProp;
5319
5320 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5321 HRESULT rc = S_OK;
5322 HWData::GuestProperty property;
5323 property.mFlags = NILFLAG;
5324 bool found = false;
5325
5326 rc = checkStateDependency(MutableStateDep);
5327 if (FAILED(rc)) return rc;
5328
5329 try
5330 {
5331 Utf8Str utf8Name(aName);
5332 Utf8Str utf8Flags(aFlags);
5333 uint32_t fFlags = NILFLAG;
5334 if ( (aFlags != NULL)
5335 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags))
5336 )
5337 return setError(E_INVALIDARG,
5338 tr("Invalid flag values: '%ls'"),
5339 aFlags);
5340
5341 /** @todo r=bird: see efficiency rant in PushGuestProperty. (Yeah, I
5342 * know, this is simple and do an OK job atm.) */
5343 HWData::GuestPropertyList::iterator it;
5344 for (it = mHWData->mGuestProperties.begin();
5345 it != mHWData->mGuestProperties.end(); ++it)
5346 if (it->strName == utf8Name)
5347 {
5348 property = *it;
5349 if (it->mFlags & (RDONLYHOST))
5350 rc = setError(E_ACCESSDENIED,
5351 tr("The property '%ls' cannot be changed by the host"),
5352 aName);
5353 else
5354 {
5355 setModified(IsModified_MachineData);
5356 mHWData.backup(); // @todo r=dj backup in a loop?!?
5357
5358 /* The backup() operation invalidates our iterator, so
5359 * get a new one. */
5360 for (it = mHWData->mGuestProperties.begin();
5361 it->strName != utf8Name;
5362 ++it)
5363 ;
5364 mHWData->mGuestProperties.erase(it);
5365 }
5366 found = true;
5367 break;
5368 }
5369 if (found && SUCCEEDED(rc))
5370 {
5371 if (aValue)
5372 {
5373 RTTIMESPEC time;
5374 property.strValue = aValue;
5375 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5376 if (aFlags != NULL)
5377 property.mFlags = fFlags;
5378 mHWData->mGuestProperties.push_back(property);
5379 }
5380 }
5381 else if (SUCCEEDED(rc) && aValue)
5382 {
5383 RTTIMESPEC time;
5384 setModified(IsModified_MachineData);
5385 mHWData.backup();
5386 property.strName = aName;
5387 property.strValue = aValue;
5388 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5389 property.mFlags = fFlags;
5390 mHWData->mGuestProperties.push_back(property);
5391 }
5392 if ( SUCCEEDED(rc)
5393 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5394 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5395 RTSTR_MAX,
5396 utf8Name.c_str(),
5397 RTSTR_MAX,
5398 NULL)
5399 )
5400 )
5401 {
5402 /** @todo r=bird: Why aren't we leaving the lock here? The
5403 * same code in PushGuestProperty does... */
5404 mParent->onGuestPropertyChange(mData->mUuid, aName,
5405 aValue ? aValue : Bstr("").raw(),
5406 aFlags ? aFlags : Bstr("").raw());
5407 }
5408 }
5409 catch (std::bad_alloc &)
5410 {
5411 rc = E_OUTOFMEMORY;
5412 }
5413
5414 return rc;
5415}
5416
5417/**
5418 * Set a property on the VM that that property belongs to.
5419 * @returns E_ACCESSDENIED if the VM process is not available or not
5420 * currently handling queries and the setting should then be done in
5421 * VBoxSVC.
5422 */
5423HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
5424 IN_BSTR aFlags)
5425{
5426 HRESULT rc;
5427
5428 try
5429 {
5430 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5431
5432 BSTR dummy = NULL; /* will not be changed (setter) */
5433 LONG64 dummy64;
5434 if (!directControl)
5435 rc = E_ACCESSDENIED;
5436 else
5437 /** @todo Fix when adding DeleteGuestProperty(),
5438 see defect. */
5439 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
5440 true /* isSetter */,
5441 &dummy, &dummy64, &dummy);
5442 }
5443 catch (std::bad_alloc &)
5444 {
5445 rc = E_OUTOFMEMORY;
5446 }
5447
5448 return rc;
5449}
5450#endif // VBOX_WITH_GUEST_PROPS
5451
5452STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
5453 IN_BSTR aFlags)
5454{
5455#ifndef VBOX_WITH_GUEST_PROPS
5456 ReturnComNotImplemented();
5457#else // VBOX_WITH_GUEST_PROPS
5458 CheckComArgStrNotEmptyOrNull(aName);
5459 CheckComArgMaybeNull(aFlags);
5460 CheckComArgMaybeNull(aValue);
5461
5462 AutoCaller autoCaller(this);
5463 if (FAILED(autoCaller.rc()))
5464 return autoCaller.rc();
5465
5466 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
5467 if (rc == E_ACCESSDENIED)
5468 /* The VM is not running or the service is not (yet) accessible */
5469 rc = setGuestPropertyToService(aName, aValue, aFlags);
5470 return rc;
5471#endif // VBOX_WITH_GUEST_PROPS
5472}
5473
5474STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
5475{
5476 return SetGuestProperty(aName, aValue, NULL);
5477}
5478
5479STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
5480{
5481 return SetGuestProperty(aName, NULL, NULL);
5482}
5483
5484#ifdef VBOX_WITH_GUEST_PROPS
5485/**
5486 * Enumerate the guest properties in VBoxSVC's internal structures.
5487 */
5488HRESULT Machine::enumerateGuestPropertiesInService
5489 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5490 ComSafeArrayOut(BSTR, aValues),
5491 ComSafeArrayOut(LONG64, aTimestamps),
5492 ComSafeArrayOut(BSTR, aFlags))
5493{
5494 using namespace guestProp;
5495
5496 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5497 Utf8Str strPatterns(aPatterns);
5498
5499 /*
5500 * Look for matching patterns and build up a list.
5501 */
5502 HWData::GuestPropertyList propList;
5503 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
5504 it != mHWData->mGuestProperties.end();
5505 ++it)
5506 if ( strPatterns.isEmpty()
5507 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5508 RTSTR_MAX,
5509 it->strName.c_str(),
5510 RTSTR_MAX,
5511 NULL)
5512 )
5513 propList.push_back(*it);
5514
5515 /*
5516 * And build up the arrays for returning the property information.
5517 */
5518 size_t cEntries = propList.size();
5519 SafeArray<BSTR> names(cEntries);
5520 SafeArray<BSTR> values(cEntries);
5521 SafeArray<LONG64> timestamps(cEntries);
5522 SafeArray<BSTR> flags(cEntries);
5523 size_t iProp = 0;
5524 for (HWData::GuestPropertyList::iterator it = propList.begin();
5525 it != propList.end();
5526 ++it)
5527 {
5528 char szFlags[MAX_FLAGS_LEN + 1];
5529 it->strName.cloneTo(&names[iProp]);
5530 it->strValue.cloneTo(&values[iProp]);
5531 timestamps[iProp] = it->mTimestamp;
5532 writeFlags(it->mFlags, szFlags);
5533 Bstr(szFlags).cloneTo(&flags[iProp]);
5534 ++iProp;
5535 }
5536 names.detachTo(ComSafeArrayOutArg(aNames));
5537 values.detachTo(ComSafeArrayOutArg(aValues));
5538 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
5539 flags.detachTo(ComSafeArrayOutArg(aFlags));
5540 return S_OK;
5541}
5542
5543/**
5544 * Enumerate the properties managed by a VM.
5545 * @returns E_ACCESSDENIED if the VM process is not available or not
5546 * currently handling queries and the setting should then be done in
5547 * VBoxSVC.
5548 */
5549HRESULT Machine::enumerateGuestPropertiesOnVM
5550 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5551 ComSafeArrayOut(BSTR, aValues),
5552 ComSafeArrayOut(LONG64, aTimestamps),
5553 ComSafeArrayOut(BSTR, aFlags))
5554{
5555 HRESULT rc;
5556 ComPtr<IInternalSessionControl> directControl;
5557 directControl = mData->mSession.mDirectControl;
5558
5559 if (!directControl)
5560 rc = E_ACCESSDENIED;
5561 else
5562 rc = directControl->EnumerateGuestProperties
5563 (aPatterns, ComSafeArrayOutArg(aNames),
5564 ComSafeArrayOutArg(aValues),
5565 ComSafeArrayOutArg(aTimestamps),
5566 ComSafeArrayOutArg(aFlags));
5567 return rc;
5568}
5569#endif // VBOX_WITH_GUEST_PROPS
5570
5571STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
5572 ComSafeArrayOut(BSTR, aNames),
5573 ComSafeArrayOut(BSTR, aValues),
5574 ComSafeArrayOut(LONG64, aTimestamps),
5575 ComSafeArrayOut(BSTR, aFlags))
5576{
5577#ifndef VBOX_WITH_GUEST_PROPS
5578 ReturnComNotImplemented();
5579#else // VBOX_WITH_GUEST_PROPS
5580 CheckComArgMaybeNull(aPatterns);
5581 CheckComArgOutSafeArrayPointerValid(aNames);
5582 CheckComArgOutSafeArrayPointerValid(aValues);
5583 CheckComArgOutSafeArrayPointerValid(aTimestamps);
5584 CheckComArgOutSafeArrayPointerValid(aFlags);
5585
5586 AutoCaller autoCaller(this);
5587 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5588
5589 HRESULT rc = enumerateGuestPropertiesOnVM
5590 (aPatterns, ComSafeArrayOutArg(aNames),
5591 ComSafeArrayOutArg(aValues),
5592 ComSafeArrayOutArg(aTimestamps),
5593 ComSafeArrayOutArg(aFlags));
5594 if (rc == E_ACCESSDENIED)
5595 /* The VM is not running or the service is not (yet) accessible */
5596 rc = enumerateGuestPropertiesInService
5597 (aPatterns, ComSafeArrayOutArg(aNames),
5598 ComSafeArrayOutArg(aValues),
5599 ComSafeArrayOutArg(aTimestamps),
5600 ComSafeArrayOutArg(aFlags));
5601 return rc;
5602#endif // VBOX_WITH_GUEST_PROPS
5603}
5604
5605STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
5606 ComSafeArrayOut(IMediumAttachment*, aAttachments))
5607{
5608 MediaData::AttachmentList atts;
5609
5610 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
5611 if (FAILED(rc)) return rc;
5612
5613 SafeIfaceArray<IMediumAttachment> attachments(atts);
5614 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
5615
5616 return S_OK;
5617}
5618
5619STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
5620 LONG aControllerPort,
5621 LONG aDevice,
5622 IMediumAttachment **aAttachment)
5623{
5624 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5625 aControllerName, aControllerPort, aDevice));
5626
5627 CheckComArgStrNotEmptyOrNull(aControllerName);
5628 CheckComArgOutPointerValid(aAttachment);
5629
5630 AutoCaller autoCaller(this);
5631 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5632
5633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5634
5635 *aAttachment = NULL;
5636
5637 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5638 aControllerName,
5639 aControllerPort,
5640 aDevice);
5641 if (pAttach.isNull())
5642 return setError(VBOX_E_OBJECT_NOT_FOUND,
5643 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5644 aDevice, aControllerPort, aControllerName);
5645
5646 pAttach.queryInterfaceTo(aAttachment);
5647
5648 return S_OK;
5649}
5650
5651STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
5652 StorageBus_T aConnectionType,
5653 IStorageController **controller)
5654{
5655 CheckComArgStrNotEmptyOrNull(aName);
5656
5657 if ( (aConnectionType <= StorageBus_Null)
5658 || (aConnectionType > StorageBus_SAS))
5659 return setError(E_INVALIDARG,
5660 tr("Invalid connection type: %d"),
5661 aConnectionType);
5662
5663 AutoCaller autoCaller(this);
5664 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5665
5666 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5667
5668 HRESULT rc = checkStateDependency(MutableStateDep);
5669 if (FAILED(rc)) return rc;
5670
5671 /* try to find one with the name first. */
5672 ComObjPtr<StorageController> ctrl;
5673
5674 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
5675 if (SUCCEEDED(rc))
5676 return setError(VBOX_E_OBJECT_IN_USE,
5677 tr("Storage controller named '%ls' already exists"),
5678 aName);
5679
5680 ctrl.createObject();
5681
5682 /* get a new instance number for the storage controller */
5683 ULONG ulInstance = 0;
5684 bool fBootable = true;
5685 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5686 it != mStorageControllers->end();
5687 ++it)
5688 {
5689 if ((*it)->getStorageBus() == aConnectionType)
5690 {
5691 ULONG ulCurInst = (*it)->getInstance();
5692
5693 if (ulCurInst >= ulInstance)
5694 ulInstance = ulCurInst + 1;
5695
5696 /* Only one controller of each type can be marked as bootable. */
5697 if ((*it)->getBootable())
5698 fBootable = false;
5699 }
5700 }
5701
5702 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5703 if (FAILED(rc)) return rc;
5704
5705 setModified(IsModified_Storage);
5706 mStorageControllers.backup();
5707 mStorageControllers->push_back(ctrl);
5708
5709 ctrl.queryInterfaceTo(controller);
5710
5711 /* inform the direct session if any */
5712 alock.release();
5713 onStorageControllerChange();
5714
5715 return S_OK;
5716}
5717
5718STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
5719 IStorageController **aStorageController)
5720{
5721 CheckComArgStrNotEmptyOrNull(aName);
5722
5723 AutoCaller autoCaller(this);
5724 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5725
5726 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5727
5728 ComObjPtr<StorageController> ctrl;
5729
5730 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5731 if (SUCCEEDED(rc))
5732 ctrl.queryInterfaceTo(aStorageController);
5733
5734 return rc;
5735}
5736
5737STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
5738 IStorageController **aStorageController)
5739{
5740 AutoCaller autoCaller(this);
5741 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5742
5743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5744
5745 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5746 it != mStorageControllers->end();
5747 ++it)
5748 {
5749 if ((*it)->getInstance() == aInstance)
5750 {
5751 (*it).queryInterfaceTo(aStorageController);
5752 return S_OK;
5753 }
5754 }
5755
5756 return setError(VBOX_E_OBJECT_NOT_FOUND,
5757 tr("Could not find a storage controller with instance number '%lu'"),
5758 aInstance);
5759}
5760
5761STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
5762{
5763 AutoCaller autoCaller(this);
5764 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5765
5766 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5767
5768 HRESULT rc = checkStateDependency(MutableStateDep);
5769 if (FAILED(rc)) return rc;
5770
5771 ComObjPtr<StorageController> ctrl;
5772
5773 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5774 if (SUCCEEDED(rc))
5775 {
5776 /* Ensure that only one controller of each type is marked as bootable. */
5777 if (fBootable == TRUE)
5778 {
5779 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5780 it != mStorageControllers->end();
5781 ++it)
5782 {
5783 ComObjPtr<StorageController> aCtrl = (*it);
5784
5785 if ( (aCtrl->getName() != Utf8Str(aName))
5786 && aCtrl->getBootable() == TRUE
5787 && aCtrl->getStorageBus() == ctrl->getStorageBus()
5788 && aCtrl->getControllerType() == ctrl->getControllerType())
5789 {
5790 aCtrl->setBootable(FALSE);
5791 break;
5792 }
5793 }
5794 }
5795
5796 if (SUCCEEDED(rc))
5797 {
5798 ctrl->setBootable(fBootable);
5799 setModified(IsModified_Storage);
5800 }
5801 }
5802
5803 if (SUCCEEDED(rc))
5804 {
5805 /* inform the direct session if any */
5806 alock.release();
5807 onStorageControllerChange();
5808 }
5809
5810 return rc;
5811}
5812
5813STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
5814{
5815 CheckComArgStrNotEmptyOrNull(aName);
5816
5817 AutoCaller autoCaller(this);
5818 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5819
5820 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5821
5822 HRESULT rc = checkStateDependency(MutableStateDep);
5823 if (FAILED(rc)) return rc;
5824
5825 ComObjPtr<StorageController> ctrl;
5826 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5827 if (FAILED(rc)) return rc;
5828
5829 {
5830 /* find all attached devices to the appropriate storage controller and detach them all*/
5831 MediaData::AttachmentList::const_iterator endList = mMediaData->mAttachments.end();
5832 MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
5833 MediumAttachment *pAttachTemp=NULL;
5834 LONG port = 0;
5835 LONG device = 0;
5836
5837 for (;it != endList; it++)
5838 {
5839 pAttachTemp = *it;
5840 AutoCaller localAutoCaller(pAttachTemp);
5841 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
5842
5843 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
5844
5845 if (pAttachTemp->getControllerName() == aName)
5846 {
5847
5848 port = pAttachTemp->getPort();
5849
5850 device = pAttachTemp->getDevice();
5851
5852 rc = DetachDevice(aName, port, device);
5853 if (FAILED(rc)) return rc;
5854 }
5855 }
5856 }
5857
5858 /* We can remove it now. */
5859 setModified(IsModified_Storage);
5860 mStorageControllers.backup();
5861
5862 ctrl->unshare();
5863
5864 mStorageControllers->remove(ctrl);
5865
5866 /* inform the direct session if any */
5867 alock.release();
5868 onStorageControllerChange();
5869
5870 return S_OK;
5871}
5872
5873STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
5874 ULONG *puOriginX,
5875 ULONG *puOriginY,
5876 ULONG *puWidth,
5877 ULONG *puHeight,
5878 BOOL *pfEnabled)
5879{
5880 LogFlowThisFunc(("\n"));
5881
5882 CheckComArgNotNull(puOriginX);
5883 CheckComArgNotNull(puOriginY);
5884 CheckComArgNotNull(puWidth);
5885 CheckComArgNotNull(puHeight);
5886 CheckComArgNotNull(pfEnabled);
5887
5888 uint32_t u32OriginX= 0;
5889 uint32_t u32OriginY= 0;
5890 uint32_t u32Width = 0;
5891 uint32_t u32Height = 0;
5892 uint16_t u16Flags = 0;
5893
5894 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
5895 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
5896 if (RT_FAILURE(vrc))
5897 {
5898#ifdef RT_OS_WINDOWS
5899 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
5900 * This works with XPCOM. But Windows COM sets all output parameters to zero.
5901 * So just assign fEnable to TRUE again.
5902 * The right fix would be to change GUI API wrappers to make sure that parameters
5903 * are changed only if API succeeds.
5904 */
5905 *pfEnabled = TRUE;
5906#endif
5907 return setError(VBOX_E_IPRT_ERROR,
5908 tr("Saved guest size is not available (%Rrc)"),
5909 vrc);
5910 }
5911
5912 *puOriginX = u32OriginX;
5913 *puOriginY = u32OriginY;
5914 *puWidth = u32Width;
5915 *puHeight = u32Height;
5916 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
5917
5918 return S_OK;
5919}
5920
5921STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
5922{
5923 LogFlowThisFunc(("\n"));
5924
5925 CheckComArgNotNull(aSize);
5926 CheckComArgNotNull(aWidth);
5927 CheckComArgNotNull(aHeight);
5928
5929 if (aScreenId != 0)
5930 return E_NOTIMPL;
5931
5932 AutoCaller autoCaller(this);
5933 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5934
5935 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5936
5937 uint8_t *pu8Data = NULL;
5938 uint32_t cbData = 0;
5939 uint32_t u32Width = 0;
5940 uint32_t u32Height = 0;
5941
5942 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
5943
5944 if (RT_FAILURE(vrc))
5945 return setError(VBOX_E_IPRT_ERROR,
5946 tr("Saved screenshot data is not available (%Rrc)"),
5947 vrc);
5948
5949 *aSize = cbData;
5950 *aWidth = u32Width;
5951 *aHeight = u32Height;
5952
5953 freeSavedDisplayScreenshot(pu8Data);
5954
5955 return S_OK;
5956}
5957
5958STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
5959{
5960 LogFlowThisFunc(("\n"));
5961
5962 CheckComArgNotNull(aWidth);
5963 CheckComArgNotNull(aHeight);
5964 CheckComArgOutSafeArrayPointerValid(aData);
5965
5966 if (aScreenId != 0)
5967 return E_NOTIMPL;
5968
5969 AutoCaller autoCaller(this);
5970 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5971
5972 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5973
5974 uint8_t *pu8Data = NULL;
5975 uint32_t cbData = 0;
5976 uint32_t u32Width = 0;
5977 uint32_t u32Height = 0;
5978
5979 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
5980
5981 if (RT_FAILURE(vrc))
5982 return setError(VBOX_E_IPRT_ERROR,
5983 tr("Saved screenshot data is not available (%Rrc)"),
5984 vrc);
5985
5986 *aWidth = u32Width;
5987 *aHeight = u32Height;
5988
5989 com::SafeArray<BYTE> bitmap(cbData);
5990 /* Convert pixels to format expected by the API caller. */
5991 if (aBGR)
5992 {
5993 /* [0] B, [1] G, [2] R, [3] A. */
5994 for (unsigned i = 0; i < cbData; i += 4)
5995 {
5996 bitmap[i] = pu8Data[i];
5997 bitmap[i + 1] = pu8Data[i + 1];
5998 bitmap[i + 2] = pu8Data[i + 2];
5999 bitmap[i + 3] = 0xff;
6000 }
6001 }
6002 else
6003 {
6004 /* [0] R, [1] G, [2] B, [3] A. */
6005 for (unsigned i = 0; i < cbData; i += 4)
6006 {
6007 bitmap[i] = pu8Data[i + 2];
6008 bitmap[i + 1] = pu8Data[i + 1];
6009 bitmap[i + 2] = pu8Data[i];
6010 bitmap[i + 3] = 0xff;
6011 }
6012 }
6013 bitmap.detachTo(ComSafeArrayOutArg(aData));
6014
6015 freeSavedDisplayScreenshot(pu8Data);
6016
6017 return S_OK;
6018}
6019
6020
6021STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6022{
6023 LogFlowThisFunc(("\n"));
6024
6025 CheckComArgNotNull(aWidth);
6026 CheckComArgNotNull(aHeight);
6027 CheckComArgOutSafeArrayPointerValid(aData);
6028
6029 if (aScreenId != 0)
6030 return E_NOTIMPL;
6031
6032 AutoCaller autoCaller(this);
6033 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6034
6035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6036
6037 uint8_t *pu8Data = NULL;
6038 uint32_t cbData = 0;
6039 uint32_t u32Width = 0;
6040 uint32_t u32Height = 0;
6041
6042 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6043
6044 if (RT_FAILURE(vrc))
6045 return setError(VBOX_E_IPRT_ERROR,
6046 tr("Saved screenshot data is not available (%Rrc)"),
6047 vrc);
6048
6049 *aWidth = u32Width;
6050 *aHeight = u32Height;
6051
6052 HRESULT rc = S_OK;
6053 uint8_t *pu8PNG = NULL;
6054 uint32_t cbPNG = 0;
6055 uint32_t cxPNG = 0;
6056 uint32_t cyPNG = 0;
6057
6058 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6059
6060 if (RT_SUCCESS(vrc))
6061 {
6062 com::SafeArray<BYTE> screenData(cbPNG);
6063 screenData.initFrom(pu8PNG, cbPNG);
6064 if (pu8PNG)
6065 RTMemFree(pu8PNG);
6066 screenData.detachTo(ComSafeArrayOutArg(aData));
6067 }
6068 else
6069 {
6070 if (pu8PNG)
6071 RTMemFree(pu8PNG);
6072 return setError(VBOX_E_IPRT_ERROR,
6073 tr("Could not convert screenshot to PNG (%Rrc)"),
6074 vrc);
6075 }
6076
6077 freeSavedDisplayScreenshot(pu8Data);
6078
6079 return rc;
6080}
6081
6082STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6083{
6084 LogFlowThisFunc(("\n"));
6085
6086 CheckComArgNotNull(aSize);
6087 CheckComArgNotNull(aWidth);
6088 CheckComArgNotNull(aHeight);
6089
6090 if (aScreenId != 0)
6091 return E_NOTIMPL;
6092
6093 AutoCaller autoCaller(this);
6094 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6095
6096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6097
6098 uint8_t *pu8Data = NULL;
6099 uint32_t cbData = 0;
6100 uint32_t u32Width = 0;
6101 uint32_t u32Height = 0;
6102
6103 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6104
6105 if (RT_FAILURE(vrc))
6106 return setError(VBOX_E_IPRT_ERROR,
6107 tr("Saved screenshot data is not available (%Rrc)"),
6108 vrc);
6109
6110 *aSize = cbData;
6111 *aWidth = u32Width;
6112 *aHeight = u32Height;
6113
6114 freeSavedDisplayScreenshot(pu8Data);
6115
6116 return S_OK;
6117}
6118
6119STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6120{
6121 LogFlowThisFunc(("\n"));
6122
6123 CheckComArgNotNull(aWidth);
6124 CheckComArgNotNull(aHeight);
6125 CheckComArgOutSafeArrayPointerValid(aData);
6126
6127 if (aScreenId != 0)
6128 return E_NOTIMPL;
6129
6130 AutoCaller autoCaller(this);
6131 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6132
6133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6134
6135 uint8_t *pu8Data = NULL;
6136 uint32_t cbData = 0;
6137 uint32_t u32Width = 0;
6138 uint32_t u32Height = 0;
6139
6140 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6141
6142 if (RT_FAILURE(vrc))
6143 return setError(VBOX_E_IPRT_ERROR,
6144 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6145 vrc);
6146
6147 *aWidth = u32Width;
6148 *aHeight = u32Height;
6149
6150 com::SafeArray<BYTE> png(cbData);
6151 png.initFrom(pu8Data, cbData);
6152 png.detachTo(ComSafeArrayOutArg(aData));
6153
6154 freeSavedDisplayScreenshot(pu8Data);
6155
6156 return S_OK;
6157}
6158
6159STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6160{
6161 HRESULT rc = S_OK;
6162 LogFlowThisFunc(("\n"));
6163
6164 AutoCaller autoCaller(this);
6165 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6166
6167 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6168
6169 if (!mHWData->mCPUHotPlugEnabled)
6170 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6171
6172 if (aCpu >= mHWData->mCPUCount)
6173 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6174
6175 if (mHWData->mCPUAttached[aCpu])
6176 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6177
6178 alock.release();
6179 rc = onCPUChange(aCpu, false);
6180 alock.acquire();
6181 if (FAILED(rc)) return rc;
6182
6183 setModified(IsModified_MachineData);
6184 mHWData.backup();
6185 mHWData->mCPUAttached[aCpu] = true;
6186
6187 /* Save settings if online */
6188 if (Global::IsOnline(mData->mMachineState))
6189 saveSettings(NULL);
6190
6191 return S_OK;
6192}
6193
6194STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6195{
6196 HRESULT rc = S_OK;
6197 LogFlowThisFunc(("\n"));
6198
6199 AutoCaller autoCaller(this);
6200 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6201
6202 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6203
6204 if (!mHWData->mCPUHotPlugEnabled)
6205 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6206
6207 if (aCpu >= SchemaDefs::MaxCPUCount)
6208 return setError(E_INVALIDARG,
6209 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6210 SchemaDefs::MaxCPUCount);
6211
6212 if (!mHWData->mCPUAttached[aCpu])
6213 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6214
6215 /* CPU 0 can't be detached */
6216 if (aCpu == 0)
6217 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6218
6219 alock.release();
6220 rc = onCPUChange(aCpu, true);
6221 alock.acquire();
6222 if (FAILED(rc)) return rc;
6223
6224 setModified(IsModified_MachineData);
6225 mHWData.backup();
6226 mHWData->mCPUAttached[aCpu] = false;
6227
6228 /* Save settings if online */
6229 if (Global::IsOnline(mData->mMachineState))
6230 saveSettings(NULL);
6231
6232 return S_OK;
6233}
6234
6235STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6236{
6237 LogFlowThisFunc(("\n"));
6238
6239 CheckComArgNotNull(aCpuAttached);
6240
6241 *aCpuAttached = false;
6242
6243 AutoCaller autoCaller(this);
6244 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6245
6246 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6247
6248 /* If hotplug is enabled the CPU is always enabled. */
6249 if (!mHWData->mCPUHotPlugEnabled)
6250 {
6251 if (aCpu < mHWData->mCPUCount)
6252 *aCpuAttached = true;
6253 }
6254 else
6255 {
6256 if (aCpu < SchemaDefs::MaxCPUCount)
6257 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6258 }
6259
6260 return S_OK;
6261}
6262
6263STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6264{
6265 CheckComArgOutPointerValid(aName);
6266
6267 AutoCaller autoCaller(this);
6268 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6269
6270 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6271
6272 Utf8Str log = queryLogFilename(aIdx);
6273 if (!RTFileExists(log.c_str()))
6274 log.setNull();
6275 log.cloneTo(aName);
6276
6277 return S_OK;
6278}
6279
6280STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6281{
6282 LogFlowThisFunc(("\n"));
6283 CheckComArgOutSafeArrayPointerValid(aData);
6284 if (aSize < 0)
6285 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6286
6287 AutoCaller autoCaller(this);
6288 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6289
6290 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6291
6292 HRESULT rc = S_OK;
6293 Utf8Str log = queryLogFilename(aIdx);
6294
6295 /* do not unnecessarily hold the lock while doing something which does
6296 * not need the lock and potentially takes a long time. */
6297 alock.release();
6298
6299 /* Limit the chunk size to 32K for now, as that gives better performance
6300 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6301 * One byte expands to approx. 25 bytes of breathtaking XML. */
6302 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6303 com::SafeArray<BYTE> logData(cbData);
6304
6305 RTFILE LogFile;
6306 int vrc = RTFileOpen(&LogFile, log.c_str(),
6307 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6308 if (RT_SUCCESS(vrc))
6309 {
6310 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6311 if (RT_SUCCESS(vrc))
6312 logData.resize(cbData);
6313 else
6314 rc = setError(VBOX_E_IPRT_ERROR,
6315 tr("Could not read log file '%s' (%Rrc)"),
6316 log.c_str(), vrc);
6317 RTFileClose(LogFile);
6318 }
6319 else
6320 rc = setError(VBOX_E_IPRT_ERROR,
6321 tr("Could not open log file '%s' (%Rrc)"),
6322 log.c_str(), vrc);
6323
6324 if (FAILED(rc))
6325 logData.resize(0);
6326 logData.detachTo(ComSafeArrayOutArg(aData));
6327
6328 return rc;
6329}
6330
6331
6332/**
6333 * Currently this method doesn't attach device to the running VM,
6334 * just makes sure it's plugged on next VM start.
6335 */
6336STDMETHODIMP Machine::AttachHostPciDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6337{
6338 AutoCaller autoCaller(this);
6339 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6340
6341 // lock scope
6342 {
6343 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6344
6345 HRESULT rc = checkStateDependency(MutableStateDep);
6346 if (FAILED(rc)) return rc;
6347
6348 ChipsetType_T aChipset = ChipsetType_PIIX3;
6349 COMGETTER(ChipsetType)(&aChipset);
6350
6351 if (aChipset != ChipsetType_ICH9)
6352 {
6353 return setError(E_INVALIDARG,
6354 tr("Host PCI attachment only supported with ICH9 chipset"));
6355 }
6356
6357 // check if device with this host PCI address already attached
6358 for (HWData::PciDeviceAssignmentList::iterator it = mHWData->mPciDeviceAssignments.begin();
6359 it != mHWData->mPciDeviceAssignments.end();
6360 ++it)
6361 {
6362 LONG iHostAddress = -1;
6363 ComPtr<PciDeviceAttachment> pAttach;
6364 pAttach = *it;
6365 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6366 if (iHostAddress == hostAddress)
6367 return setError(E_INVALIDARG,
6368 tr("Device with host PCI address already attached to this VM"));
6369 }
6370
6371 ComObjPtr<PciDeviceAttachment> pda;
6372 char name[32];
6373
6374 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6375 Bstr bname(name);
6376 pda.createObject();
6377 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
6378 setModified(IsModified_MachineData);
6379 mHWData.backup();
6380 mHWData->mPciDeviceAssignments.push_back(pda);
6381 }
6382
6383 return S_OK;
6384}
6385
6386/**
6387 * Currently this method doesn't detach device from the running VM,
6388 * just makes sure it's not plugged on next VM start.
6389 */
6390STDMETHODIMP Machine::DetachHostPciDevice(LONG hostAddress)
6391{
6392 AutoCaller autoCaller(this);
6393 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6394
6395 ComObjPtr<PciDeviceAttachment> pAttach;
6396 bool fRemoved = false;
6397 HRESULT rc;
6398
6399 // lock scope
6400 {
6401 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6402
6403 rc = checkStateDependency(MutableStateDep);
6404 if (FAILED(rc)) return rc;
6405
6406 for (HWData::PciDeviceAssignmentList::iterator it = mHWData->mPciDeviceAssignments.begin();
6407 it != mHWData->mPciDeviceAssignments.end();
6408 ++it)
6409 {
6410 LONG iHostAddress = -1;
6411 pAttach = *it;
6412 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6413 if (iHostAddress != -1 && iHostAddress == hostAddress)
6414 {
6415 setModified(IsModified_MachineData);
6416 mHWData.backup();
6417 mHWData->mPciDeviceAssignments.remove(pAttach);
6418 fRemoved = true;
6419 break;
6420 }
6421 }
6422 }
6423
6424
6425 /* Fire event outside of the lock */
6426 if (fRemoved)
6427 {
6428 Assert(!pAttach.isNull());
6429 ComPtr<IEventSource> es;
6430 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6431 Assert(SUCCEEDED(rc));
6432 Bstr mid;
6433 rc = this->COMGETTER(Id)(mid.asOutParam());
6434 Assert(SUCCEEDED(rc));
6435 fireHostPciDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6436 }
6437
6438 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6439 tr("No host PCI device %08x attached"),
6440 hostAddress
6441 );
6442}
6443
6444STDMETHODIMP Machine::COMGETTER(PciDeviceAssignments)(ComSafeArrayOut(IPciDeviceAttachment *, aAssignments))
6445{
6446 CheckComArgOutSafeArrayPointerValid(aAssignments);
6447
6448 AutoCaller autoCaller(this);
6449 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6450
6451 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6452
6453 SafeIfaceArray<IPciDeviceAttachment> assignments(mHWData->mPciDeviceAssignments);
6454 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
6455
6456 return S_OK;
6457}
6458
6459STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
6460{
6461 CheckComArgOutPointerValid(aBandwidthControl);
6462
6463 AutoCaller autoCaller(this);
6464 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6465
6466 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
6467
6468 return S_OK;
6469}
6470
6471STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
6472{
6473 CheckComArgOutPointerValid(pfEnabled);
6474 AutoCaller autoCaller(this);
6475 HRESULT hrc = autoCaller.rc();
6476 if (SUCCEEDED(hrc))
6477 {
6478 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6479 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
6480 }
6481 return hrc;
6482}
6483
6484STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
6485{
6486 AutoCaller autoCaller(this);
6487 HRESULT hrc = autoCaller.rc();
6488 if (SUCCEEDED(hrc))
6489 {
6490 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6491 hrc = checkStateDependency(MutableStateDep);
6492 if (SUCCEEDED(hrc))
6493 {
6494 hrc = mHWData.backupEx();
6495 if (SUCCEEDED(hrc))
6496 {
6497 setModified(IsModified_MachineData);
6498 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
6499 }
6500 }
6501 }
6502 return hrc;
6503}
6504
6505STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
6506{
6507 CheckComArgOutPointerValid(pbstrConfig);
6508 AutoCaller autoCaller(this);
6509 HRESULT hrc = autoCaller.rc();
6510 if (SUCCEEDED(hrc))
6511 {
6512 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6513 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
6514 }
6515 return hrc;
6516}
6517
6518STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
6519{
6520 CheckComArgStr(bstrConfig);
6521 AutoCaller autoCaller(this);
6522 HRESULT hrc = autoCaller.rc();
6523 if (SUCCEEDED(hrc))
6524 {
6525 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6526 hrc = checkStateDependency(MutableStateDep);
6527 if (SUCCEEDED(hrc))
6528 {
6529 hrc = mHWData.backupEx();
6530 if (SUCCEEDED(hrc))
6531 {
6532 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
6533 if (SUCCEEDED(hrc))
6534 setModified(IsModified_MachineData);
6535 }
6536 }
6537 }
6538 return hrc;
6539
6540}
6541
6542STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
6543{
6544 CheckComArgOutPointerValid(pfAllow);
6545 AutoCaller autoCaller(this);
6546 HRESULT hrc = autoCaller.rc();
6547 if (SUCCEEDED(hrc))
6548 {
6549 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6550 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
6551 }
6552 return hrc;
6553}
6554
6555STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
6556{
6557 AutoCaller autoCaller(this);
6558 HRESULT hrc = autoCaller.rc();
6559 if (SUCCEEDED(hrc))
6560 {
6561 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6562 hrc = checkStateDependency(MutableStateDep);
6563 if (SUCCEEDED(hrc))
6564 {
6565 hrc = mHWData.backupEx();
6566 if (SUCCEEDED(hrc))
6567 {
6568 setModified(IsModified_MachineData);
6569 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
6570 }
6571 }
6572 }
6573 return hrc;
6574}
6575
6576STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
6577{
6578 CheckComArgOutPointerValid(pfEnabled);
6579 AutoCaller autoCaller(this);
6580 HRESULT hrc = autoCaller.rc();
6581 if (SUCCEEDED(hrc))
6582 {
6583 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6584 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
6585 }
6586 return hrc;
6587}
6588
6589STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
6590{
6591 AutoCaller autoCaller(this);
6592 HRESULT hrc = autoCaller.rc();
6593 if (SUCCEEDED(hrc))
6594 {
6595 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6596 hrc = checkStateDependency(MutableStateDep);
6597 if ( SUCCEEDED(hrc)
6598 && mHWData->mAutostart.fAutostartEnabled != fEnabled)
6599 {
6600 AutostartDb *autostartDb = mParent->getAutostartDb();
6601 int vrc;
6602
6603 if (fEnabled)
6604 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6605 else
6606 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6607
6608 if (RT_SUCCESS(vrc))
6609 {
6610 hrc = mHWData.backupEx();
6611 if (SUCCEEDED(hrc))
6612 {
6613 setModified(IsModified_MachineData);
6614 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
6615 }
6616 }
6617 else if (vrc == VERR_NOT_SUPPORTED)
6618 hrc = setError(VBOX_E_NOT_SUPPORTED,
6619 tr("The VM autostart feature is not supported on this platform"));
6620 else
6621 hrc = setError(E_UNEXPECTED,
6622 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6623 fEnabled ? "Adding" : "Removing",
6624 mUserData->s.strName.c_str(), vrc);
6625 }
6626 }
6627 return hrc;
6628}
6629
6630STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
6631{
6632 CheckComArgOutPointerValid(puDelay);
6633 AutoCaller autoCaller(this);
6634 HRESULT hrc = autoCaller.rc();
6635 if (SUCCEEDED(hrc))
6636 {
6637 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6638 *puDelay = mHWData->mAutostart.uAutostartDelay;
6639 }
6640 return hrc;
6641}
6642
6643STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
6644{
6645 AutoCaller autoCaller(this);
6646 HRESULT hrc = autoCaller.rc();
6647 if (SUCCEEDED(hrc))
6648 {
6649 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6650 hrc = checkStateDependency(MutableStateDep);
6651 if (SUCCEEDED(hrc))
6652 {
6653 hrc = mHWData.backupEx();
6654 if (SUCCEEDED(hrc))
6655 {
6656 setModified(IsModified_MachineData);
6657 mHWData->mAutostart.uAutostartDelay = uDelay;
6658 }
6659 }
6660 }
6661 return hrc;
6662}
6663
6664STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
6665{
6666 CheckComArgOutPointerValid(penmAutostopType);
6667 AutoCaller autoCaller(this);
6668 HRESULT hrc = autoCaller.rc();
6669 if (SUCCEEDED(hrc))
6670 {
6671 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6672 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
6673 }
6674 return hrc;
6675}
6676
6677STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
6678{
6679 AutoCaller autoCaller(this);
6680 HRESULT hrc = autoCaller.rc();
6681 if (SUCCEEDED(hrc))
6682 {
6683 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6684 hrc = checkStateDependency(MutableStateDep);
6685 if ( SUCCEEDED(hrc)
6686 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
6687 {
6688 AutostartDb *autostartDb = mParent->getAutostartDb();
6689 int vrc;
6690
6691 if (enmAutostopType != AutostopType_Disabled)
6692 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6693 else
6694 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6695
6696 if (RT_SUCCESS(vrc))
6697 {
6698 hrc = mHWData.backupEx();
6699 if (SUCCEEDED(hrc))
6700 {
6701 setModified(IsModified_MachineData);
6702 mHWData->mAutostart.enmAutostopType = enmAutostopType;
6703 }
6704 }
6705 else if (vrc == VERR_NOT_SUPPORTED)
6706 hrc = setError(VBOX_E_NOT_SUPPORTED,
6707 tr("The VM autostop feature is not supported on this platform"));
6708 else
6709 hrc = setError(E_UNEXPECTED,
6710 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6711 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6712 mUserData->s.strName.c_str(), vrc);
6713 }
6714 }
6715 return hrc;
6716}
6717
6718
6719STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
6720{
6721 LogFlowFuncEnter();
6722
6723 CheckComArgNotNull(pTarget);
6724 CheckComArgOutPointerValid(pProgress);
6725
6726 /* Convert the options. */
6727 RTCList<CloneOptions_T> optList;
6728 if (options != NULL)
6729 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
6730
6731 if (optList.contains(CloneOptions_Link))
6732 {
6733 if (!isSnapshotMachine())
6734 return setError(E_INVALIDARG,
6735 tr("Linked clone can only be created from a snapshot"));
6736 if (mode != CloneMode_MachineState)
6737 return setError(E_INVALIDARG,
6738 tr("Linked clone can only be created for a single machine state"));
6739 }
6740 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6741
6742 AutoCaller autoCaller(this);
6743 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6744
6745
6746 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
6747
6748 HRESULT rc = pWorker->start(pProgress);
6749
6750 LogFlowFuncLeave();
6751
6752 return rc;
6753}
6754
6755// public methods for internal purposes
6756/////////////////////////////////////////////////////////////////////////////
6757
6758/**
6759 * Adds the given IsModified_* flag to the dirty flags of the machine.
6760 * This must be called either during loadSettings or under the machine write lock.
6761 * @param fl
6762 */
6763void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
6764{
6765 mData->flModifications |= fl;
6766 if (fAllowStateModification && isStateModificationAllowed())
6767 mData->mCurrentStateModified = true;
6768}
6769
6770/**
6771 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
6772 * care of the write locking.
6773 *
6774 * @param fModifications The flag to add.
6775 */
6776void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
6777{
6778 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6779 setModified(fModification, fAllowStateModification);
6780}
6781
6782/**
6783 * Saves the registry entry of this machine to the given configuration node.
6784 *
6785 * @param aEntryNode Node to save the registry entry to.
6786 *
6787 * @note locks this object for reading.
6788 */
6789HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
6790{
6791 AutoLimitedCaller autoCaller(this);
6792 AssertComRCReturnRC(autoCaller.rc());
6793
6794 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6795
6796 data.uuid = mData->mUuid;
6797 data.strSettingsFile = mData->m_strConfigFile;
6798
6799 return S_OK;
6800}
6801
6802/**
6803 * Calculates the absolute path of the given path taking the directory of the
6804 * machine settings file as the current directory.
6805 *
6806 * @param aPath Path to calculate the absolute path for.
6807 * @param aResult Where to put the result (used only on success, can be the
6808 * same Utf8Str instance as passed in @a aPath).
6809 * @return IPRT result.
6810 *
6811 * @note Locks this object for reading.
6812 */
6813int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
6814{
6815 AutoCaller autoCaller(this);
6816 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
6817
6818 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6819
6820 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
6821
6822 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
6823
6824 strSettingsDir.stripFilename();
6825 char folder[RTPATH_MAX];
6826 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
6827 if (RT_SUCCESS(vrc))
6828 aResult = folder;
6829
6830 return vrc;
6831}
6832
6833/**
6834 * Copies strSource to strTarget, making it relative to the machine folder
6835 * if it is a subdirectory thereof, or simply copying it otherwise.
6836 *
6837 * @param strSource Path to evaluate and copy.
6838 * @param strTarget Buffer to receive target path.
6839 *
6840 * @note Locks this object for reading.
6841 */
6842void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
6843 Utf8Str &strTarget)
6844{
6845 AutoCaller autoCaller(this);
6846 AssertComRCReturn(autoCaller.rc(), (void)0);
6847
6848 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6849
6850 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
6851 // use strTarget as a temporary buffer to hold the machine settings dir
6852 strTarget = mData->m_strConfigFileFull;
6853 strTarget.stripFilename();
6854 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
6855 {
6856 // is relative: then append what's left
6857 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
6858 // for empty paths (only possible for subdirs) use "." to avoid
6859 // triggering default settings for not present config attributes.
6860 if (strTarget.isEmpty())
6861 strTarget = ".";
6862 }
6863 else
6864 // is not relative: then overwrite
6865 strTarget = strSource;
6866}
6867
6868/**
6869 * Returns the full path to the machine's log folder in the
6870 * \a aLogFolder argument.
6871 */
6872void Machine::getLogFolder(Utf8Str &aLogFolder)
6873{
6874 AutoCaller autoCaller(this);
6875 AssertComRCReturnVoid(autoCaller.rc());
6876
6877 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6878
6879 char szTmp[RTPATH_MAX];
6880 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
6881 if (RT_SUCCESS(vrc))
6882 {
6883 if (szTmp[0] && !mUserData.isNull())
6884 {
6885 char szTmp2[RTPATH_MAX];
6886 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
6887 if (RT_SUCCESS(vrc))
6888 aLogFolder = BstrFmt("%s%c%s",
6889 szTmp2,
6890 RTPATH_DELIMITER,
6891 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
6892 }
6893 else
6894 vrc = VERR_PATH_IS_RELATIVE;
6895 }
6896
6897 if (RT_FAILURE(vrc))
6898 {
6899 // fallback if VBOX_USER_LOGHOME is not set or invalid
6900 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
6901 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
6902 aLogFolder.append(RTPATH_DELIMITER);
6903 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
6904 }
6905}
6906
6907/**
6908 * Returns the full path to the machine's log file for an given index.
6909 */
6910Utf8Str Machine::queryLogFilename(ULONG idx)
6911{
6912 Utf8Str logFolder;
6913 getLogFolder(logFolder);
6914 Assert(logFolder.length());
6915 Utf8Str log;
6916 if (idx == 0)
6917 log = Utf8StrFmt("%s%cVBox.log",
6918 logFolder.c_str(), RTPATH_DELIMITER);
6919 else
6920 log = Utf8StrFmt("%s%cVBox.log.%d",
6921 logFolder.c_str(), RTPATH_DELIMITER, idx);
6922 return log;
6923}
6924
6925/**
6926 * Composes a unique saved state filename based on the current system time. The filename is
6927 * granular to the second so this will work so long as no more than one snapshot is taken on
6928 * a machine per second.
6929 *
6930 * Before version 4.1, we used this formula for saved state files:
6931 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
6932 * which no longer works because saved state files can now be shared between the saved state of the
6933 * "saved" machine and an online snapshot, and the following would cause problems:
6934 * 1) save machine
6935 * 2) create online snapshot from that machine state --> reusing saved state file
6936 * 3) save machine again --> filename would be reused, breaking the online snapshot
6937 *
6938 * So instead we now use a timestamp.
6939 *
6940 * @param str
6941 */
6942void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
6943{
6944 AutoCaller autoCaller(this);
6945 AssertComRCReturnVoid(autoCaller.rc());
6946
6947 {
6948 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6949 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
6950 }
6951
6952 RTTIMESPEC ts;
6953 RTTimeNow(&ts);
6954 RTTIME time;
6955 RTTimeExplode(&time, &ts);
6956
6957 strStateFilePath += RTPATH_DELIMITER;
6958 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
6959 time.i32Year, time.u8Month, time.u8MonthDay,
6960 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
6961}
6962
6963/**
6964 * @note Locks this object for writing, calls the client process
6965 * (inside the lock).
6966 */
6967HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
6968 const Utf8Str &strType,
6969 const Utf8Str &strEnvironment,
6970 ProgressProxy *aProgress)
6971{
6972 LogFlowThisFuncEnter();
6973
6974 AssertReturn(aControl, E_FAIL);
6975 AssertReturn(aProgress, E_FAIL);
6976
6977 AutoCaller autoCaller(this);
6978 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6979
6980 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6981
6982 if (!mData->mRegistered)
6983 return setError(E_UNEXPECTED,
6984 tr("The machine '%s' is not registered"),
6985 mUserData->s.strName.c_str());
6986
6987 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
6988
6989 if ( mData->mSession.mState == SessionState_Locked
6990 || mData->mSession.mState == SessionState_Spawning
6991 || mData->mSession.mState == SessionState_Unlocking)
6992 return setError(VBOX_E_INVALID_OBJECT_STATE,
6993 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
6994 mUserData->s.strName.c_str());
6995
6996 /* may not be busy */
6997 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
6998
6999 /* get the path to the executable */
7000 char szPath[RTPATH_MAX];
7001 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7002 size_t sz = strlen(szPath);
7003 szPath[sz++] = RTPATH_DELIMITER;
7004 szPath[sz] = 0;
7005 char *cmd = szPath + sz;
7006 sz = RTPATH_MAX - sz;
7007
7008 int vrc = VINF_SUCCESS;
7009 RTPROCESS pid = NIL_RTPROCESS;
7010
7011 RTENV env = RTENV_DEFAULT;
7012
7013 if (!strEnvironment.isEmpty())
7014 {
7015 char *newEnvStr = NULL;
7016
7017 do
7018 {
7019 /* clone the current environment */
7020 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7021 AssertRCBreakStmt(vrc2, vrc = vrc2);
7022
7023 newEnvStr = RTStrDup(strEnvironment.c_str());
7024 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7025
7026 /* put new variables to the environment
7027 * (ignore empty variable names here since RTEnv API
7028 * intentionally doesn't do that) */
7029 char *var = newEnvStr;
7030 for (char *p = newEnvStr; *p; ++p)
7031 {
7032 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7033 {
7034 *p = '\0';
7035 if (*var)
7036 {
7037 char *val = strchr(var, '=');
7038 if (val)
7039 {
7040 *val++ = '\0';
7041 vrc2 = RTEnvSetEx(env, var, val);
7042 }
7043 else
7044 vrc2 = RTEnvUnsetEx(env, var);
7045 if (RT_FAILURE(vrc2))
7046 break;
7047 }
7048 var = p + 1;
7049 }
7050 }
7051 if (RT_SUCCESS(vrc2) && *var)
7052 vrc2 = RTEnvPutEx(env, var);
7053
7054 AssertRCBreakStmt(vrc2, vrc = vrc2);
7055 }
7056 while (0);
7057
7058 if (newEnvStr != NULL)
7059 RTStrFree(newEnvStr);
7060 }
7061
7062 /* Qt is default */
7063#ifdef VBOX_WITH_QTGUI
7064 if (strType == "gui" || strType == "GUI/Qt")
7065 {
7066# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7067 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7068# else
7069 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7070# endif
7071 Assert(sz >= sizeof(VirtualBox_exe));
7072 strcpy(cmd, VirtualBox_exe);
7073
7074 Utf8Str idStr = mData->mUuid.toString();
7075 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7076 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7077 }
7078#else /* !VBOX_WITH_QTGUI */
7079 if (0)
7080 ;
7081#endif /* VBOX_WITH_QTGUI */
7082
7083 else
7084
7085#ifdef VBOX_WITH_VBOXSDL
7086 if (strType == "sdl" || strType == "GUI/SDL")
7087 {
7088 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7089 Assert(sz >= sizeof(VBoxSDL_exe));
7090 strcpy(cmd, VBoxSDL_exe);
7091
7092 Utf8Str idStr = mData->mUuid.toString();
7093 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7094 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7095 }
7096#else /* !VBOX_WITH_VBOXSDL */
7097 if (0)
7098 ;
7099#endif /* !VBOX_WITH_VBOXSDL */
7100
7101 else
7102
7103#ifdef VBOX_WITH_HEADLESS
7104 if ( strType == "headless"
7105 || strType == "capture"
7106 || strType == "vrdp" /* Deprecated. Same as headless. */
7107 )
7108 {
7109 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7110 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7111 * and a VM works even if the server has not been installed.
7112 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7113 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7114 * differently in 4.0 and 3.x.
7115 */
7116 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7117 Assert(sz >= sizeof(VBoxHeadless_exe));
7118 strcpy(cmd, VBoxHeadless_exe);
7119
7120 Utf8Str idStr = mData->mUuid.toString();
7121 /* Leave space for "--capture" arg. */
7122 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7123 "--startvm", idStr.c_str(),
7124 "--vrde", "config",
7125 0, /* For "--capture". */
7126 0 };
7127 if (strType == "capture")
7128 {
7129 unsigned pos = RT_ELEMENTS(args) - 2;
7130 args[pos] = "--capture";
7131 }
7132 vrc = RTProcCreate(szPath, args, env,
7133#ifdef RT_OS_WINDOWS
7134 RTPROC_FLAGS_NO_WINDOW
7135#else
7136 0
7137#endif
7138 , &pid);
7139 }
7140#else /* !VBOX_WITH_HEADLESS */
7141 if (0)
7142 ;
7143#endif /* !VBOX_WITH_HEADLESS */
7144 else
7145 {
7146 RTEnvDestroy(env);
7147 return setError(E_INVALIDARG,
7148 tr("Invalid session type: '%s'"),
7149 strType.c_str());
7150 }
7151
7152 RTEnvDestroy(env);
7153
7154 if (RT_FAILURE(vrc))
7155 return setError(VBOX_E_IPRT_ERROR,
7156 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7157 mUserData->s.strName.c_str(), vrc);
7158
7159 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7160
7161 /*
7162 * Note that we don't release the lock here before calling the client,
7163 * because it doesn't need to call us back if called with a NULL argument.
7164 * Releasing the lock here is dangerous because we didn't prepare the
7165 * launch data yet, but the client we've just started may happen to be
7166 * too fast and call openSession() that will fail (because of PID, etc.),
7167 * so that the Machine will never get out of the Spawning session state.
7168 */
7169
7170 /* inform the session that it will be a remote one */
7171 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7172 HRESULT rc = aControl->AssignMachine(NULL);
7173 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7174
7175 if (FAILED(rc))
7176 {
7177 /* restore the session state */
7178 mData->mSession.mState = SessionState_Unlocked;
7179 /* The failure may occur w/o any error info (from RPC), so provide one */
7180 return setError(VBOX_E_VM_ERROR,
7181 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7182 }
7183
7184 /* attach launch data to the machine */
7185 Assert(mData->mSession.mPid == NIL_RTPROCESS);
7186 mData->mSession.mRemoteControls.push_back(aControl);
7187 mData->mSession.mProgress = aProgress;
7188 mData->mSession.mPid = pid;
7189 mData->mSession.mState = SessionState_Spawning;
7190 mData->mSession.mType = strType;
7191
7192 LogFlowThisFuncLeave();
7193 return S_OK;
7194}
7195
7196/**
7197 * Returns @c true if the given machine has an open direct session and returns
7198 * the session machine instance and additional session data (on some platforms)
7199 * if so.
7200 *
7201 * Note that when the method returns @c false, the arguments remain unchanged.
7202 *
7203 * @param aMachine Session machine object.
7204 * @param aControl Direct session control object (optional).
7205 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7206 *
7207 * @note locks this object for reading.
7208 */
7209#if defined(RT_OS_WINDOWS)
7210bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7211 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7212 HANDLE *aIPCSem /*= NULL*/,
7213 bool aAllowClosing /*= false*/)
7214#elif defined(RT_OS_OS2)
7215bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7216 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7217 HMTX *aIPCSem /*= NULL*/,
7218 bool aAllowClosing /*= false*/)
7219#else
7220bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7221 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7222 bool aAllowClosing /*= false*/)
7223#endif
7224{
7225 AutoLimitedCaller autoCaller(this);
7226 AssertComRCReturn(autoCaller.rc(), false);
7227
7228 /* just return false for inaccessible machines */
7229 if (autoCaller.state() != Ready)
7230 return false;
7231
7232 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7233
7234 if ( mData->mSession.mState == SessionState_Locked
7235 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7236 )
7237 {
7238 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7239
7240 aMachine = mData->mSession.mMachine;
7241
7242 if (aControl != NULL)
7243 *aControl = mData->mSession.mDirectControl;
7244
7245#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7246 /* Additional session data */
7247 if (aIPCSem != NULL)
7248 *aIPCSem = aMachine->mIPCSem;
7249#endif
7250 return true;
7251 }
7252
7253 return false;
7254}
7255
7256/**
7257 * Returns @c true if the given machine has an spawning direct session and
7258 * returns and additional session data (on some platforms) if so.
7259 *
7260 * Note that when the method returns @c false, the arguments remain unchanged.
7261 *
7262 * @param aPID PID of the spawned direct session process.
7263 *
7264 * @note locks this object for reading.
7265 */
7266#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7267bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7268#else
7269bool Machine::isSessionSpawning()
7270#endif
7271{
7272 AutoLimitedCaller autoCaller(this);
7273 AssertComRCReturn(autoCaller.rc(), false);
7274
7275 /* just return false for inaccessible machines */
7276 if (autoCaller.state() != Ready)
7277 return false;
7278
7279 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7280
7281 if (mData->mSession.mState == SessionState_Spawning)
7282 {
7283#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7284 /* Additional session data */
7285 if (aPID != NULL)
7286 {
7287 AssertReturn(mData->mSession.mPid != NIL_RTPROCESS, false);
7288 *aPID = mData->mSession.mPid;
7289 }
7290#endif
7291 return true;
7292 }
7293
7294 return false;
7295}
7296
7297/**
7298 * Called from the client watcher thread to check for unexpected client process
7299 * death during Session_Spawning state (e.g. before it successfully opened a
7300 * direct session).
7301 *
7302 * On Win32 and on OS/2, this method is called only when we've got the
7303 * direct client's process termination notification, so it always returns @c
7304 * true.
7305 *
7306 * On other platforms, this method returns @c true if the client process is
7307 * terminated and @c false if it's still alive.
7308 *
7309 * @note Locks this object for writing.
7310 */
7311bool Machine::checkForSpawnFailure()
7312{
7313 AutoCaller autoCaller(this);
7314 if (!autoCaller.isOk())
7315 {
7316 /* nothing to do */
7317 LogFlowThisFunc(("Already uninitialized!\n"));
7318 return true;
7319 }
7320
7321 /* VirtualBox::addProcessToReap() needs a write lock */
7322 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
7323
7324 if (mData->mSession.mState != SessionState_Spawning)
7325 {
7326 /* nothing to do */
7327 LogFlowThisFunc(("Not spawning any more!\n"));
7328 return true;
7329 }
7330
7331 HRESULT rc = S_OK;
7332
7333#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7334
7335 /* the process was already unexpectedly terminated, we just need to set an
7336 * error and finalize session spawning */
7337 rc = setError(E_FAIL,
7338 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
7339 getName().c_str());
7340#else
7341
7342 /* PID not yet initialized, skip check. */
7343 if (mData->mSession.mPid == NIL_RTPROCESS)
7344 return false;
7345
7346 RTPROCSTATUS status;
7347 int vrc = ::RTProcWait(mData->mSession.mPid, RTPROCWAIT_FLAGS_NOBLOCK,
7348 &status);
7349
7350 if (vrc != VERR_PROCESS_RUNNING)
7351 {
7352 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7353 rc = setError(E_FAIL,
7354 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7355 getName().c_str(), status.iStatus);
7356 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7357 rc = setError(E_FAIL,
7358 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7359 getName().c_str(), status.iStatus);
7360 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7361 rc = setError(E_FAIL,
7362 tr("The virtual machine '%s' has terminated abnormally"),
7363 getName().c_str(), status.iStatus);
7364 else
7365 rc = setError(E_FAIL,
7366 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7367 getName().c_str(), rc);
7368 }
7369
7370#endif
7371
7372 if (FAILED(rc))
7373 {
7374 /* Close the remote session, remove the remote control from the list
7375 * and reset session state to Closed (@note keep the code in sync with
7376 * the relevant part in checkForSpawnFailure()). */
7377
7378 Assert(mData->mSession.mRemoteControls.size() == 1);
7379 if (mData->mSession.mRemoteControls.size() == 1)
7380 {
7381 ErrorInfoKeeper eik;
7382 mData->mSession.mRemoteControls.front()->Uninitialize();
7383 }
7384
7385 mData->mSession.mRemoteControls.clear();
7386 mData->mSession.mState = SessionState_Unlocked;
7387
7388 /* finalize the progress after setting the state */
7389 if (!mData->mSession.mProgress.isNull())
7390 {
7391 mData->mSession.mProgress->notifyComplete(rc);
7392 mData->mSession.mProgress.setNull();
7393 }
7394
7395 mParent->addProcessToReap(mData->mSession.mPid);
7396 mData->mSession.mPid = NIL_RTPROCESS;
7397
7398 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7399 return true;
7400 }
7401
7402 return false;
7403}
7404
7405/**
7406 * Checks whether the machine can be registered. If so, commits and saves
7407 * all settings.
7408 *
7409 * @note Must be called from mParent's write lock. Locks this object and
7410 * children for writing.
7411 */
7412HRESULT Machine::prepareRegister()
7413{
7414 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7415
7416 AutoLimitedCaller autoCaller(this);
7417 AssertComRCReturnRC(autoCaller.rc());
7418
7419 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7420
7421 /* wait for state dependents to drop to zero */
7422 ensureNoStateDependencies();
7423
7424 if (!mData->mAccessible)
7425 return setError(VBOX_E_INVALID_OBJECT_STATE,
7426 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7427 mUserData->s.strName.c_str(),
7428 mData->mUuid.toString().c_str());
7429
7430 AssertReturn(autoCaller.state() == Ready, E_FAIL);
7431
7432 if (mData->mRegistered)
7433 return setError(VBOX_E_INVALID_OBJECT_STATE,
7434 tr("The machine '%s' with UUID {%s} is already registered"),
7435 mUserData->s.strName.c_str(),
7436 mData->mUuid.toString().c_str());
7437
7438 HRESULT rc = S_OK;
7439
7440 // Ensure the settings are saved. If we are going to be registered and
7441 // no config file exists yet, create it by calling saveSettings() too.
7442 if ( (mData->flModifications)
7443 || (!mData->pMachineConfigFile->fileExists())
7444 )
7445 {
7446 rc = saveSettings(NULL);
7447 // no need to check whether VirtualBox.xml needs saving too since
7448 // we can't have a machine XML file rename pending
7449 if (FAILED(rc)) return rc;
7450 }
7451
7452 /* more config checking goes here */
7453
7454 if (SUCCEEDED(rc))
7455 {
7456 /* we may have had implicit modifications we want to fix on success */
7457 commit();
7458
7459 mData->mRegistered = true;
7460 }
7461 else
7462 {
7463 /* we may have had implicit modifications we want to cancel on failure*/
7464 rollback(false /* aNotify */);
7465 }
7466
7467 return rc;
7468}
7469
7470/**
7471 * Increases the number of objects dependent on the machine state or on the
7472 * registered state. Guarantees that these two states will not change at least
7473 * until #releaseStateDependency() is called.
7474 *
7475 * Depending on the @a aDepType value, additional state checks may be made.
7476 * These checks will set extended error info on failure. See
7477 * #checkStateDependency() for more info.
7478 *
7479 * If this method returns a failure, the dependency is not added and the caller
7480 * is not allowed to rely on any particular machine state or registration state
7481 * value and may return the failed result code to the upper level.
7482 *
7483 * @param aDepType Dependency type to add.
7484 * @param aState Current machine state (NULL if not interested).
7485 * @param aRegistered Current registered state (NULL if not interested).
7486 *
7487 * @note Locks this object for writing.
7488 */
7489HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7490 MachineState_T *aState /* = NULL */,
7491 BOOL *aRegistered /* = NULL */)
7492{
7493 AutoCaller autoCaller(this);
7494 AssertComRCReturnRC(autoCaller.rc());
7495
7496 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7497
7498 HRESULT rc = checkStateDependency(aDepType);
7499 if (FAILED(rc)) return rc;
7500
7501 {
7502 if (mData->mMachineStateChangePending != 0)
7503 {
7504 /* ensureNoStateDependencies() is waiting for state dependencies to
7505 * drop to zero so don't add more. It may make sense to wait a bit
7506 * and retry before reporting an error (since the pending state
7507 * transition should be really quick) but let's just assert for
7508 * now to see if it ever happens on practice. */
7509
7510 AssertFailed();
7511
7512 return setError(E_ACCESSDENIED,
7513 tr("Machine state change is in progress. Please retry the operation later."));
7514 }
7515
7516 ++mData->mMachineStateDeps;
7517 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7518 }
7519
7520 if (aState)
7521 *aState = mData->mMachineState;
7522 if (aRegistered)
7523 *aRegistered = mData->mRegistered;
7524
7525 return S_OK;
7526}
7527
7528/**
7529 * Decreases the number of objects dependent on the machine state.
7530 * Must always complete the #addStateDependency() call after the state
7531 * dependency is no more necessary.
7532 */
7533void Machine::releaseStateDependency()
7534{
7535 AutoCaller autoCaller(this);
7536 AssertComRCReturnVoid(autoCaller.rc());
7537
7538 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7539
7540 /* releaseStateDependency() w/o addStateDependency()? */
7541 AssertReturnVoid(mData->mMachineStateDeps != 0);
7542 -- mData->mMachineStateDeps;
7543
7544 if (mData->mMachineStateDeps == 0)
7545 {
7546 /* inform ensureNoStateDependencies() that there are no more deps */
7547 if (mData->mMachineStateChangePending != 0)
7548 {
7549 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7550 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7551 }
7552 }
7553}
7554
7555// protected methods
7556/////////////////////////////////////////////////////////////////////////////
7557
7558/**
7559 * Performs machine state checks based on the @a aDepType value. If a check
7560 * fails, this method will set extended error info, otherwise it will return
7561 * S_OK. It is supposed, that on failure, the caller will immediately return
7562 * the return value of this method to the upper level.
7563 *
7564 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7565 *
7566 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7567 * current state of this machine object allows to change settings of the
7568 * machine (i.e. the machine is not registered, or registered but not running
7569 * and not saved). It is useful to call this method from Machine setters
7570 * before performing any change.
7571 *
7572 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7573 * as for MutableStateDep except that if the machine is saved, S_OK is also
7574 * returned. This is useful in setters which allow changing machine
7575 * properties when it is in the saved state.
7576 *
7577 * @param aDepType Dependency type to check.
7578 *
7579 * @note Non Machine based classes should use #addStateDependency() and
7580 * #releaseStateDependency() methods or the smart AutoStateDependency
7581 * template.
7582 *
7583 * @note This method must be called from under this object's read or write
7584 * lock.
7585 */
7586HRESULT Machine::checkStateDependency(StateDependency aDepType)
7587{
7588 switch (aDepType)
7589 {
7590 case AnyStateDep:
7591 {
7592 break;
7593 }
7594 case MutableStateDep:
7595 {
7596 if ( mData->mRegistered
7597 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7598 || ( mData->mMachineState != MachineState_Paused
7599 && mData->mMachineState != MachineState_Running
7600 && mData->mMachineState != MachineState_Aborted
7601 && mData->mMachineState != MachineState_Teleported
7602 && mData->mMachineState != MachineState_PoweredOff
7603 )
7604 )
7605 )
7606 return setError(VBOX_E_INVALID_VM_STATE,
7607 tr("The machine is not mutable (state is %s)"),
7608 Global::stringifyMachineState(mData->mMachineState));
7609 break;
7610 }
7611 case MutableOrSavedStateDep:
7612 {
7613 if ( mData->mRegistered
7614 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7615 || ( mData->mMachineState != MachineState_Paused
7616 && mData->mMachineState != MachineState_Running
7617 && mData->mMachineState != MachineState_Aborted
7618 && mData->mMachineState != MachineState_Teleported
7619 && mData->mMachineState != MachineState_Saved
7620 && mData->mMachineState != MachineState_PoweredOff
7621 )
7622 )
7623 )
7624 return setError(VBOX_E_INVALID_VM_STATE,
7625 tr("The machine is not mutable (state is %s)"),
7626 Global::stringifyMachineState(mData->mMachineState));
7627 break;
7628 }
7629 }
7630
7631 return S_OK;
7632}
7633
7634/**
7635 * Helper to initialize all associated child objects and allocate data
7636 * structures.
7637 *
7638 * This method must be called as a part of the object's initialization procedure
7639 * (usually done in the #init() method).
7640 *
7641 * @note Must be called only from #init() or from #registeredInit().
7642 */
7643HRESULT Machine::initDataAndChildObjects()
7644{
7645 AutoCaller autoCaller(this);
7646 AssertComRCReturnRC(autoCaller.rc());
7647 AssertComRCReturn(autoCaller.state() == InInit ||
7648 autoCaller.state() == Limited, E_FAIL);
7649
7650 AssertReturn(!mData->mAccessible, E_FAIL);
7651
7652 /* allocate data structures */
7653 mSSData.allocate();
7654 mUserData.allocate();
7655 mHWData.allocate();
7656 mMediaData.allocate();
7657 mStorageControllers.allocate();
7658
7659 /* initialize mOSTypeId */
7660 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
7661
7662 /* create associated BIOS settings object */
7663 unconst(mBIOSSettings).createObject();
7664 mBIOSSettings->init(this);
7665
7666 /* create an associated VRDE object (default is disabled) */
7667 unconst(mVRDEServer).createObject();
7668 mVRDEServer->init(this);
7669
7670 /* create associated serial port objects */
7671 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
7672 {
7673 unconst(mSerialPorts[slot]).createObject();
7674 mSerialPorts[slot]->init(this, slot);
7675 }
7676
7677 /* create associated parallel port objects */
7678 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7679 {
7680 unconst(mParallelPorts[slot]).createObject();
7681 mParallelPorts[slot]->init(this, slot);
7682 }
7683
7684 /* create the audio adapter object (always present, default is disabled) */
7685 unconst(mAudioAdapter).createObject();
7686 mAudioAdapter->init(this);
7687
7688 /* create the USB controller object (always present, default is disabled) */
7689 unconst(mUSBController).createObject();
7690 mUSBController->init(this);
7691
7692 /* create associated network adapter objects */
7693 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
7694 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7695 {
7696 unconst(mNetworkAdapters[slot]).createObject();
7697 mNetworkAdapters[slot]->init(this, slot);
7698 }
7699
7700 /* create the bandwidth control */
7701 unconst(mBandwidthControl).createObject();
7702 mBandwidthControl->init(this);
7703
7704 return S_OK;
7705}
7706
7707/**
7708 * Helper to uninitialize all associated child objects and to free all data
7709 * structures.
7710 *
7711 * This method must be called as a part of the object's uninitialization
7712 * procedure (usually done in the #uninit() method).
7713 *
7714 * @note Must be called only from #uninit() or from #registeredInit().
7715 */
7716void Machine::uninitDataAndChildObjects()
7717{
7718 AutoCaller autoCaller(this);
7719 AssertComRCReturnVoid(autoCaller.rc());
7720 AssertComRCReturnVoid( autoCaller.state() == InUninit
7721 || autoCaller.state() == Limited);
7722
7723 /* tell all our other child objects we've been uninitialized */
7724 if (mBandwidthControl)
7725 {
7726 mBandwidthControl->uninit();
7727 unconst(mBandwidthControl).setNull();
7728 }
7729
7730 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7731 {
7732 if (mNetworkAdapters[slot])
7733 {
7734 mNetworkAdapters[slot]->uninit();
7735 unconst(mNetworkAdapters[slot]).setNull();
7736 }
7737 }
7738
7739 if (mUSBController)
7740 {
7741 mUSBController->uninit();
7742 unconst(mUSBController).setNull();
7743 }
7744
7745 if (mAudioAdapter)
7746 {
7747 mAudioAdapter->uninit();
7748 unconst(mAudioAdapter).setNull();
7749 }
7750
7751 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7752 {
7753 if (mParallelPorts[slot])
7754 {
7755 mParallelPorts[slot]->uninit();
7756 unconst(mParallelPorts[slot]).setNull();
7757 }
7758 }
7759
7760 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
7761 {
7762 if (mSerialPorts[slot])
7763 {
7764 mSerialPorts[slot]->uninit();
7765 unconst(mSerialPorts[slot]).setNull();
7766 }
7767 }
7768
7769 if (mVRDEServer)
7770 {
7771 mVRDEServer->uninit();
7772 unconst(mVRDEServer).setNull();
7773 }
7774
7775 if (mBIOSSettings)
7776 {
7777 mBIOSSettings->uninit();
7778 unconst(mBIOSSettings).setNull();
7779 }
7780
7781 /* Deassociate hard disks (only when a real Machine or a SnapshotMachine
7782 * instance is uninitialized; SessionMachine instances refer to real
7783 * Machine hard disks). This is necessary for a clean re-initialization of
7784 * the VM after successfully re-checking the accessibility state. Note
7785 * that in case of normal Machine or SnapshotMachine uninitialization (as
7786 * a result of unregistering or deleting the snapshot), outdated hard
7787 * disk attachments will already be uninitialized and deleted, so this
7788 * code will not affect them. */
7789 if ( !!mMediaData
7790 && (!isSessionMachine())
7791 )
7792 {
7793 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
7794 it != mMediaData->mAttachments.end();
7795 ++it)
7796 {
7797 ComObjPtr<Medium> hd = (*it)->getMedium();
7798 if (hd.isNull())
7799 continue;
7800 HRESULT rc = hd->removeBackReference(mData->mUuid, getSnapshotId());
7801 AssertComRC(rc);
7802 }
7803 }
7804
7805 if (!isSessionMachine() && !isSnapshotMachine())
7806 {
7807 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
7808 if (mData->mFirstSnapshot)
7809 {
7810 // snapshots tree is protected by media write lock; strictly
7811 // this isn't necessary here since we're deleting the entire
7812 // machine, but otherwise we assert in Snapshot::uninit()
7813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7814 mData->mFirstSnapshot->uninit();
7815 mData->mFirstSnapshot.setNull();
7816 }
7817
7818 mData->mCurrentSnapshot.setNull();
7819 }
7820
7821 /* free data structures (the essential mData structure is not freed here
7822 * since it may be still in use) */
7823 mMediaData.free();
7824 mStorageControllers.free();
7825 mHWData.free();
7826 mUserData.free();
7827 mSSData.free();
7828}
7829
7830/**
7831 * Returns a pointer to the Machine object for this machine that acts like a
7832 * parent for complex machine data objects such as shared folders, etc.
7833 *
7834 * For primary Machine objects and for SnapshotMachine objects, returns this
7835 * object's pointer itself. For SessionMachine objects, returns the peer
7836 * (primary) machine pointer.
7837 */
7838Machine* Machine::getMachine()
7839{
7840 if (isSessionMachine())
7841 return (Machine*)mPeer;
7842 return this;
7843}
7844
7845/**
7846 * Makes sure that there are no machine state dependents. If necessary, waits
7847 * for the number of dependents to drop to zero.
7848 *
7849 * Make sure this method is called from under this object's write lock to
7850 * guarantee that no new dependents may be added when this method returns
7851 * control to the caller.
7852 *
7853 * @note Locks this object for writing. The lock will be released while waiting
7854 * (if necessary).
7855 *
7856 * @warning To be used only in methods that change the machine state!
7857 */
7858void Machine::ensureNoStateDependencies()
7859{
7860 AssertReturnVoid(isWriteLockOnCurrentThread());
7861
7862 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7863
7864 /* Wait for all state dependents if necessary */
7865 if (mData->mMachineStateDeps != 0)
7866 {
7867 /* lazy semaphore creation */
7868 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
7869 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
7870
7871 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
7872 mData->mMachineStateDeps));
7873
7874 ++mData->mMachineStateChangePending;
7875
7876 /* reset the semaphore before waiting, the last dependent will signal
7877 * it */
7878 RTSemEventMultiReset(mData->mMachineStateDepsSem);
7879
7880 alock.release();
7881
7882 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
7883
7884 alock.acquire();
7885
7886 -- mData->mMachineStateChangePending;
7887 }
7888}
7889
7890/**
7891 * Changes the machine state and informs callbacks.
7892 *
7893 * This method is not intended to fail so it either returns S_OK or asserts (and
7894 * returns a failure).
7895 *
7896 * @note Locks this object for writing.
7897 */
7898HRESULT Machine::setMachineState(MachineState_T aMachineState)
7899{
7900 LogFlowThisFuncEnter();
7901 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
7902
7903 AutoCaller autoCaller(this);
7904 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7905
7906 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7907
7908 /* wait for state dependents to drop to zero */
7909 ensureNoStateDependencies();
7910
7911 if (mData->mMachineState != aMachineState)
7912 {
7913 mData->mMachineState = aMachineState;
7914
7915 RTTimeNow(&mData->mLastStateChange);
7916
7917 mParent->onMachineStateChange(mData->mUuid, aMachineState);
7918 }
7919
7920 LogFlowThisFuncLeave();
7921 return S_OK;
7922}
7923
7924/**
7925 * Searches for a shared folder with the given logical name
7926 * in the collection of shared folders.
7927 *
7928 * @param aName logical name of the shared folder
7929 * @param aSharedFolder where to return the found object
7930 * @param aSetError whether to set the error info if the folder is
7931 * not found
7932 * @return
7933 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
7934 *
7935 * @note
7936 * must be called from under the object's lock!
7937 */
7938HRESULT Machine::findSharedFolder(const Utf8Str &aName,
7939 ComObjPtr<SharedFolder> &aSharedFolder,
7940 bool aSetError /* = false */)
7941{
7942 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
7943 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
7944 it != mHWData->mSharedFolders.end();
7945 ++it)
7946 {
7947 SharedFolder *pSF = *it;
7948 AutoCaller autoCaller(pSF);
7949 if (pSF->getName() == aName)
7950 {
7951 aSharedFolder = pSF;
7952 rc = S_OK;
7953 break;
7954 }
7955 }
7956
7957 if (aSetError && FAILED(rc))
7958 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
7959
7960 return rc;
7961}
7962
7963/**
7964 * Initializes all machine instance data from the given settings structures
7965 * from XML. The exception is the machine UUID which needs special handling
7966 * depending on the caller's use case, so the caller needs to set that herself.
7967 *
7968 * This gets called in several contexts during machine initialization:
7969 *
7970 * -- When machine XML exists on disk already and needs to be loaded into memory,
7971 * for example, from registeredInit() to load all registered machines on
7972 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
7973 * attached to the machine should be part of some media registry already.
7974 *
7975 * -- During OVF import, when a machine config has been constructed from an
7976 * OVF file. In this case, puuidRegistry is set to the machine UUID to
7977 * ensure that the media listed as attachments in the config (which have
7978 * been imported from the OVF) receive the correct registry ID.
7979 *
7980 * -- During VM cloning.
7981 *
7982 * @param config Machine settings from XML.
7983 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
7984 * @return
7985 */
7986HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
7987 const Guid *puuidRegistry)
7988{
7989 // copy name, description, OS type, teleporter, UTC etc.
7990 mUserData->s = config.machineUserData;
7991
7992 // look up the object by Id to check it is valid
7993 ComPtr<IGuestOSType> guestOSType;
7994 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
7995 guestOSType.asOutParam());
7996 if (FAILED(rc)) return rc;
7997
7998 // stateFile (optional)
7999 if (config.strStateFile.isEmpty())
8000 mSSData->strStateFilePath.setNull();
8001 else
8002 {
8003 Utf8Str stateFilePathFull(config.strStateFile);
8004 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8005 if (RT_FAILURE(vrc))
8006 return setError(E_FAIL,
8007 tr("Invalid saved state file path '%s' (%Rrc)"),
8008 config.strStateFile.c_str(),
8009 vrc);
8010 mSSData->strStateFilePath = stateFilePathFull;
8011 }
8012
8013 // snapshot folder needs special processing so set it again
8014 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8015 if (FAILED(rc)) return rc;
8016
8017 /* Copy the extra data items (Not in any case config is already the same as
8018 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8019 * make sure the extra data map is copied). */
8020 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8021
8022 /* currentStateModified (optional, default is true) */
8023 mData->mCurrentStateModified = config.fCurrentStateModified;
8024
8025 mData->mLastStateChange = config.timeLastStateChange;
8026
8027 /*
8028 * note: all mUserData members must be assigned prior this point because
8029 * we need to commit changes in order to let mUserData be shared by all
8030 * snapshot machine instances.
8031 */
8032 mUserData.commitCopy();
8033
8034 // machine registry, if present (must be loaded before snapshots)
8035 if (config.canHaveOwnMediaRegistry())
8036 {
8037 // determine machine folder
8038 Utf8Str strMachineFolder = getSettingsFileFull();
8039 strMachineFolder.stripFilename();
8040 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8041 config.mediaRegistry,
8042 strMachineFolder);
8043 if (FAILED(rc)) return rc;
8044 }
8045
8046 /* Snapshot node (optional) */
8047 size_t cRootSnapshots;
8048 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8049 {
8050 // there must be only one root snapshot
8051 Assert(cRootSnapshots == 1);
8052
8053 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8054
8055 rc = loadSnapshot(snap,
8056 config.uuidCurrentSnapshot,
8057 NULL); // no parent == first snapshot
8058 if (FAILED(rc)) return rc;
8059 }
8060
8061 // hardware data
8062 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8063 if (FAILED(rc)) return rc;
8064
8065 // load storage controllers
8066 rc = loadStorageControllers(config.storageMachine,
8067 puuidRegistry,
8068 NULL /* puuidSnapshot */);
8069 if (FAILED(rc)) return rc;
8070
8071 /*
8072 * NOTE: the assignment below must be the last thing to do,
8073 * otherwise it will be not possible to change the settings
8074 * somewhere in the code above because all setters will be
8075 * blocked by checkStateDependency(MutableStateDep).
8076 */
8077
8078 /* set the machine state to Aborted or Saved when appropriate */
8079 if (config.fAborted)
8080 {
8081 mSSData->strStateFilePath.setNull();
8082
8083 /* no need to use setMachineState() during init() */
8084 mData->mMachineState = MachineState_Aborted;
8085 }
8086 else if (!mSSData->strStateFilePath.isEmpty())
8087 {
8088 /* no need to use setMachineState() during init() */
8089 mData->mMachineState = MachineState_Saved;
8090 }
8091
8092 // after loading settings, we are no longer different from the XML on disk
8093 mData->flModifications = 0;
8094
8095 return S_OK;
8096}
8097
8098/**
8099 * Recursively loads all snapshots starting from the given.
8100 *
8101 * @param aNode <Snapshot> node.
8102 * @param aCurSnapshotId Current snapshot ID from the settings file.
8103 * @param aParentSnapshot Parent snapshot.
8104 */
8105HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8106 const Guid &aCurSnapshotId,
8107 Snapshot *aParentSnapshot)
8108{
8109 AssertReturn(!isSnapshotMachine(), E_FAIL);
8110 AssertReturn(!isSessionMachine(), E_FAIL);
8111
8112 HRESULT rc = S_OK;
8113
8114 Utf8Str strStateFile;
8115 if (!data.strStateFile.isEmpty())
8116 {
8117 /* optional */
8118 strStateFile = data.strStateFile;
8119 int vrc = calculateFullPath(strStateFile, strStateFile);
8120 if (RT_FAILURE(vrc))
8121 return setError(E_FAIL,
8122 tr("Invalid saved state file path '%s' (%Rrc)"),
8123 strStateFile.c_str(),
8124 vrc);
8125 }
8126
8127 /* create a snapshot machine object */
8128 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8129 pSnapshotMachine.createObject();
8130 rc = pSnapshotMachine->initFromSettings(this,
8131 data.hardware,
8132 &data.debugging,
8133 &data.autostart,
8134 data.storage,
8135 data.uuid.ref(),
8136 strStateFile);
8137 if (FAILED(rc)) return rc;
8138
8139 /* create a snapshot object */
8140 ComObjPtr<Snapshot> pSnapshot;
8141 pSnapshot.createObject();
8142 /* initialize the snapshot */
8143 rc = pSnapshot->init(mParent, // VirtualBox object
8144 data.uuid,
8145 data.strName,
8146 data.strDescription,
8147 data.timestamp,
8148 pSnapshotMachine,
8149 aParentSnapshot);
8150 if (FAILED(rc)) return rc;
8151
8152 /* memorize the first snapshot if necessary */
8153 if (!mData->mFirstSnapshot)
8154 mData->mFirstSnapshot = pSnapshot;
8155
8156 /* memorize the current snapshot when appropriate */
8157 if ( !mData->mCurrentSnapshot
8158 && pSnapshot->getId() == aCurSnapshotId
8159 )
8160 mData->mCurrentSnapshot = pSnapshot;
8161
8162 // now create the children
8163 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8164 it != data.llChildSnapshots.end();
8165 ++it)
8166 {
8167 const settings::Snapshot &childData = *it;
8168 // recurse
8169 rc = loadSnapshot(childData,
8170 aCurSnapshotId,
8171 pSnapshot); // parent = the one we created above
8172 if (FAILED(rc)) return rc;
8173 }
8174
8175 return rc;
8176}
8177
8178/**
8179 * Loads settings into mHWData.
8180 *
8181 * @param data Reference to the hardware settings.
8182 * @param pDbg Pointer to the debugging settings.
8183 * @param pAutostart Pointer to the autostart settings.
8184 */
8185HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8186 const settings::Autostart *pAutostart)
8187{
8188 AssertReturn(!isSessionMachine(), E_FAIL);
8189
8190 HRESULT rc = S_OK;
8191
8192 try
8193 {
8194 /* The hardware version attribute (optional). */
8195 mHWData->mHWVersion = data.strVersion;
8196 mHWData->mHardwareUUID = data.uuid;
8197
8198 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8199 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8200 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8201 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8202 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8203 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8204 mHWData->mPAEEnabled = data.fPAE;
8205 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8206
8207 mHWData->mCPUCount = data.cCPUs;
8208 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8209 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8210
8211 // cpu
8212 if (mHWData->mCPUHotPlugEnabled)
8213 {
8214 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8215 it != data.llCpus.end();
8216 ++it)
8217 {
8218 const settings::Cpu &cpu = *it;
8219
8220 mHWData->mCPUAttached[cpu.ulId] = true;
8221 }
8222 }
8223
8224 // cpuid leafs
8225 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8226 it != data.llCpuIdLeafs.end();
8227 ++it)
8228 {
8229 const settings::CpuIdLeaf &leaf = *it;
8230
8231 switch (leaf.ulId)
8232 {
8233 case 0x0:
8234 case 0x1:
8235 case 0x2:
8236 case 0x3:
8237 case 0x4:
8238 case 0x5:
8239 case 0x6:
8240 case 0x7:
8241 case 0x8:
8242 case 0x9:
8243 case 0xA:
8244 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8245 break;
8246
8247 case 0x80000000:
8248 case 0x80000001:
8249 case 0x80000002:
8250 case 0x80000003:
8251 case 0x80000004:
8252 case 0x80000005:
8253 case 0x80000006:
8254 case 0x80000007:
8255 case 0x80000008:
8256 case 0x80000009:
8257 case 0x8000000A:
8258 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8259 break;
8260
8261 default:
8262 /* just ignore */
8263 break;
8264 }
8265 }
8266
8267 mHWData->mMemorySize = data.ulMemorySizeMB;
8268 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8269
8270 // boot order
8271 for (size_t i = 0;
8272 i < RT_ELEMENTS(mHWData->mBootOrder);
8273 i++)
8274 {
8275 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8276 if (it == data.mapBootOrder.end())
8277 mHWData->mBootOrder[i] = DeviceType_Null;
8278 else
8279 mHWData->mBootOrder[i] = it->second;
8280 }
8281
8282 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8283 mHWData->mMonitorCount = data.cMonitors;
8284 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8285 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8286 mHWData->mFirmwareType = data.firmwareType;
8287 mHWData->mPointingHidType = data.pointingHidType;
8288 mHWData->mKeyboardHidType = data.keyboardHidType;
8289 mHWData->mChipsetType = data.chipsetType;
8290 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8291 mHWData->mHpetEnabled = data.fHpetEnabled;
8292
8293 /* VRDEServer */
8294 rc = mVRDEServer->loadSettings(data.vrdeSettings);
8295 if (FAILED(rc)) return rc;
8296
8297 /* BIOS */
8298 rc = mBIOSSettings->loadSettings(data.biosSettings);
8299 if (FAILED(rc)) return rc;
8300
8301 // Bandwidth control (must come before network adapters)
8302 rc = mBandwidthControl->loadSettings(data.ioSettings);
8303 if (FAILED(rc)) return rc;
8304
8305 /* USB Controller */
8306 rc = mUSBController->loadSettings(data.usbController);
8307 if (FAILED(rc)) return rc;
8308
8309 // network adapters
8310 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8311 uint32_t oldCount = mNetworkAdapters.size();
8312 if (newCount > oldCount)
8313 {
8314 mNetworkAdapters.resize(newCount);
8315 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
8316 {
8317 unconst(mNetworkAdapters[slot]).createObject();
8318 mNetworkAdapters[slot]->init(this, slot);
8319 }
8320 }
8321 else if (newCount < oldCount)
8322 mNetworkAdapters.resize(newCount);
8323 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8324 it != data.llNetworkAdapters.end();
8325 ++it)
8326 {
8327 const settings::NetworkAdapter &nic = *it;
8328
8329 /* slot unicity is guaranteed by XML Schema */
8330 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8331 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
8332 if (FAILED(rc)) return rc;
8333 }
8334
8335 // serial ports
8336 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8337 it != data.llSerialPorts.end();
8338 ++it)
8339 {
8340 const settings::SerialPort &s = *it;
8341
8342 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8343 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
8344 if (FAILED(rc)) return rc;
8345 }
8346
8347 // parallel ports (optional)
8348 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8349 it != data.llParallelPorts.end();
8350 ++it)
8351 {
8352 const settings::ParallelPort &p = *it;
8353
8354 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8355 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
8356 if (FAILED(rc)) return rc;
8357 }
8358
8359 /* AudioAdapter */
8360 rc = mAudioAdapter->loadSettings(data.audioAdapter);
8361 if (FAILED(rc)) return rc;
8362
8363 /* Shared folders */
8364 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8365 it != data.llSharedFolders.end();
8366 ++it)
8367 {
8368 const settings::SharedFolder &sf = *it;
8369
8370 ComObjPtr<SharedFolder> sharedFolder;
8371 /* Check for double entries. Not allowed! */
8372 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8373 if (SUCCEEDED(rc))
8374 return setError(VBOX_E_OBJECT_IN_USE,
8375 tr("Shared folder named '%s' already exists"),
8376 sf.strName.c_str());
8377
8378 /* Create the new shared folder. Don't break on error. This will be
8379 * reported when the machine starts. */
8380 sharedFolder.createObject();
8381 rc = sharedFolder->init(getMachine(),
8382 sf.strName,
8383 sf.strHostPath,
8384 RT_BOOL(sf.fWritable),
8385 RT_BOOL(sf.fAutoMount),
8386 false /* fFailOnError */);
8387 if (FAILED(rc)) return rc;
8388 mHWData->mSharedFolders.push_back(sharedFolder);
8389 }
8390
8391 // Clipboard
8392 mHWData->mClipboardMode = data.clipboardMode;
8393
8394 // guest settings
8395 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8396
8397 // IO settings
8398 mHWData->mIoCacheEnabled = data.ioSettings.fIoCacheEnabled;
8399 mHWData->mIoCacheSize = data.ioSettings.ulIoCacheSize;
8400
8401 // Host PCI devices
8402 for (settings::HostPciDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8403 it != data.pciAttachments.end();
8404 ++it)
8405 {
8406 const settings::HostPciDeviceAttachment &hpda = *it;
8407 ComObjPtr<PciDeviceAttachment> pda;
8408
8409 pda.createObject();
8410 pda->loadSettings(this, hpda);
8411 mHWData->mPciDeviceAssignments.push_back(pda);
8412 }
8413
8414 /*
8415 * (The following isn't really real hardware, but it lives in HWData
8416 * for reasons of convenience.)
8417 */
8418
8419#ifdef VBOX_WITH_GUEST_PROPS
8420 /* Guest properties (optional) */
8421 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8422 it != data.llGuestProperties.end();
8423 ++it)
8424 {
8425 const settings::GuestProperty &prop = *it;
8426 uint32_t fFlags = guestProp::NILFLAG;
8427 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8428 HWData::GuestProperty property = { prop.strName, prop.strValue, (LONG64) prop.timestamp, fFlags };
8429 mHWData->mGuestProperties.push_back(property);
8430 }
8431
8432 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8433#endif /* VBOX_WITH_GUEST_PROPS defined */
8434
8435 rc = loadDebugging(pDbg);
8436 if (FAILED(rc))
8437 return rc;
8438
8439 mHWData->mAutostart = *pAutostart;
8440 }
8441 catch(std::bad_alloc &)
8442 {
8443 return E_OUTOFMEMORY;
8444 }
8445
8446 AssertComRC(rc);
8447 return rc;
8448}
8449
8450/**
8451 * Called from Machine::loadHardware() to load the debugging settings of the
8452 * machine.
8453 *
8454 * @param pDbg Pointer to the settings.
8455 */
8456HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
8457{
8458 mHWData->mDebugging = *pDbg;
8459 /* no more processing currently required, this will probably change. */
8460 return S_OK;
8461}
8462
8463/**
8464 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
8465 *
8466 * @param data
8467 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8468 * @param puuidSnapshot
8469 * @return
8470 */
8471HRESULT Machine::loadStorageControllers(const settings::Storage &data,
8472 const Guid *puuidRegistry,
8473 const Guid *puuidSnapshot)
8474{
8475 AssertReturn(!isSessionMachine(), E_FAIL);
8476
8477 HRESULT rc = S_OK;
8478
8479 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8480 it != data.llStorageControllers.end();
8481 ++it)
8482 {
8483 const settings::StorageController &ctlData = *it;
8484
8485 ComObjPtr<StorageController> pCtl;
8486 /* Try to find one with the name first. */
8487 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8488 if (SUCCEEDED(rc))
8489 return setError(VBOX_E_OBJECT_IN_USE,
8490 tr("Storage controller named '%s' already exists"),
8491 ctlData.strName.c_str());
8492
8493 pCtl.createObject();
8494 rc = pCtl->init(this,
8495 ctlData.strName,
8496 ctlData.storageBus,
8497 ctlData.ulInstance,
8498 ctlData.fBootable);
8499 if (FAILED(rc)) return rc;
8500
8501 mStorageControllers->push_back(pCtl);
8502
8503 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8504 if (FAILED(rc)) return rc;
8505
8506 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8507 if (FAILED(rc)) return rc;
8508
8509 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8510 if (FAILED(rc)) return rc;
8511
8512 /* Set IDE emulation settings (only for AHCI controller). */
8513 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8514 {
8515 if ( (FAILED(rc = pCtl->SetIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8516 || (FAILED(rc = pCtl->SetIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8517 || (FAILED(rc = pCtl->SetIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8518 || (FAILED(rc = pCtl->SetIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8519 )
8520 return rc;
8521 }
8522
8523 /* Load the attached devices now. */
8524 rc = loadStorageDevices(pCtl,
8525 ctlData,
8526 puuidRegistry,
8527 puuidSnapshot);
8528 if (FAILED(rc)) return rc;
8529 }
8530
8531 return S_OK;
8532}
8533
8534/**
8535 * Called from loadStorageControllers for a controller's devices.
8536 *
8537 * @param aStorageController
8538 * @param data
8539 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8540 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
8541 * @return
8542 */
8543HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
8544 const settings::StorageController &data,
8545 const Guid *puuidRegistry,
8546 const Guid *puuidSnapshot)
8547{
8548 HRESULT rc = S_OK;
8549
8550 /* paranoia: detect duplicate attachments */
8551 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8552 it != data.llAttachedDevices.end();
8553 ++it)
8554 {
8555 const settings::AttachedDevice &ad = *it;
8556
8557 for (settings::AttachedDevicesList::const_iterator it2 = it;
8558 it2 != data.llAttachedDevices.end();
8559 ++it2)
8560 {
8561 if (it == it2)
8562 continue;
8563
8564 const settings::AttachedDevice &ad2 = *it2;
8565
8566 if ( ad.lPort == ad2.lPort
8567 && ad.lDevice == ad2.lDevice)
8568 {
8569 return setError(E_FAIL,
8570 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
8571 aStorageController->getName().c_str(),
8572 ad.lPort,
8573 ad.lDevice,
8574 mUserData->s.strName.c_str());
8575 }
8576 }
8577 }
8578
8579 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8580 it != data.llAttachedDevices.end();
8581 ++it)
8582 {
8583 const settings::AttachedDevice &dev = *it;
8584 ComObjPtr<Medium> medium;
8585
8586 switch (dev.deviceType)
8587 {
8588 case DeviceType_Floppy:
8589 case DeviceType_DVD:
8590 if (dev.strHostDriveSrc.isNotEmpty())
8591 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
8592 else
8593 rc = mParent->findRemoveableMedium(dev.deviceType,
8594 dev.uuid,
8595 false /* fRefresh */,
8596 false /* aSetError */,
8597 medium);
8598 if (rc == VBOX_E_OBJECT_NOT_FOUND)
8599 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
8600 rc = S_OK;
8601 break;
8602
8603 case DeviceType_HardDisk:
8604 {
8605 /* find a hard disk by UUID */
8606 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
8607 if (FAILED(rc))
8608 {
8609 if (isSnapshotMachine())
8610 {
8611 // wrap another error message around the "cannot find hard disk" set by findHardDisk
8612 // so the user knows that the bad disk is in a snapshot somewhere
8613 com::ErrorInfo info;
8614 return setError(E_FAIL,
8615 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
8616 puuidSnapshot->raw(),
8617 info.getText().raw());
8618 }
8619 else
8620 return rc;
8621 }
8622
8623 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
8624
8625 if (medium->getType() == MediumType_Immutable)
8626 {
8627 if (isSnapshotMachine())
8628 return setError(E_FAIL,
8629 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8630 "of the virtual machine '%s' ('%s')"),
8631 medium->getLocationFull().c_str(),
8632 dev.uuid.raw(),
8633 puuidSnapshot->raw(),
8634 mUserData->s.strName.c_str(),
8635 mData->m_strConfigFileFull.c_str());
8636
8637 return setError(E_FAIL,
8638 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8639 medium->getLocationFull().c_str(),
8640 dev.uuid.raw(),
8641 mUserData->s.strName.c_str(),
8642 mData->m_strConfigFileFull.c_str());
8643 }
8644
8645 if (medium->getType() == MediumType_MultiAttach)
8646 {
8647 if (isSnapshotMachine())
8648 return setError(E_FAIL,
8649 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8650 "of the virtual machine '%s' ('%s')"),
8651 medium->getLocationFull().c_str(),
8652 dev.uuid.raw(),
8653 puuidSnapshot->raw(),
8654 mUserData->s.strName.c_str(),
8655 mData->m_strConfigFileFull.c_str());
8656
8657 return setError(E_FAIL,
8658 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8659 medium->getLocationFull().c_str(),
8660 dev.uuid.raw(),
8661 mUserData->s.strName.c_str(),
8662 mData->m_strConfigFileFull.c_str());
8663 }
8664
8665 if ( !isSnapshotMachine()
8666 && medium->getChildren().size() != 0
8667 )
8668 return setError(E_FAIL,
8669 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
8670 "because it has %d differencing child hard disks"),
8671 medium->getLocationFull().c_str(),
8672 dev.uuid.raw(),
8673 mUserData->s.strName.c_str(),
8674 mData->m_strConfigFileFull.c_str(),
8675 medium->getChildren().size());
8676
8677 if (findAttachment(mMediaData->mAttachments,
8678 medium))
8679 return setError(E_FAIL,
8680 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
8681 medium->getLocationFull().c_str(),
8682 dev.uuid.raw(),
8683 mUserData->s.strName.c_str(),
8684 mData->m_strConfigFileFull.c_str());
8685
8686 break;
8687 }
8688
8689 default:
8690 return setError(E_FAIL,
8691 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
8692 medium->getLocationFull().c_str(),
8693 mUserData->s.strName.c_str(),
8694 mData->m_strConfigFileFull.c_str());
8695 }
8696
8697 if (FAILED(rc))
8698 break;
8699
8700 /* Bandwidth groups are loaded at this point. */
8701 ComObjPtr<BandwidthGroup> pBwGroup;
8702
8703 if (!dev.strBwGroup.isEmpty())
8704 {
8705 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
8706 if (FAILED(rc))
8707 return setError(E_FAIL,
8708 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
8709 medium->getLocationFull().c_str(),
8710 dev.strBwGroup.c_str(),
8711 mUserData->s.strName.c_str(),
8712 mData->m_strConfigFileFull.c_str());
8713 pBwGroup->reference();
8714 }
8715
8716 const Bstr controllerName = aStorageController->getName();
8717 ComObjPtr<MediumAttachment> pAttachment;
8718 pAttachment.createObject();
8719 rc = pAttachment->init(this,
8720 medium,
8721 controllerName,
8722 dev.lPort,
8723 dev.lDevice,
8724 dev.deviceType,
8725 false,
8726 dev.fPassThrough,
8727 dev.fTempEject,
8728 dev.fNonRotational,
8729 dev.fDiscard,
8730 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
8731 if (FAILED(rc)) break;
8732
8733 /* associate the medium with this machine and snapshot */
8734 if (!medium.isNull())
8735 {
8736 AutoCaller medCaller(medium);
8737 if (FAILED(medCaller.rc())) return medCaller.rc();
8738 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
8739
8740 if (isSnapshotMachine())
8741 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
8742 else
8743 rc = medium->addBackReference(mData->mUuid);
8744 /* If the medium->addBackReference fails it sets an appropriate
8745 * error message, so no need to do any guesswork here. */
8746
8747 if (puuidRegistry)
8748 // caller wants registry ID to be set on all attached media (OVF import case)
8749 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
8750 }
8751
8752 if (FAILED(rc))
8753 break;
8754
8755 /* back up mMediaData to let registeredInit() properly rollback on failure
8756 * (= limited accessibility) */
8757 setModified(IsModified_Storage);
8758 mMediaData.backup();
8759 mMediaData->mAttachments.push_back(pAttachment);
8760 }
8761
8762 return rc;
8763}
8764
8765/**
8766 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
8767 *
8768 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
8769 * @param aSnapshot where to return the found snapshot
8770 * @param aSetError true to set extended error info on failure
8771 */
8772HRESULT Machine::findSnapshotById(const Guid &aId,
8773 ComObjPtr<Snapshot> &aSnapshot,
8774 bool aSetError /* = false */)
8775{
8776 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
8777
8778 if (!mData->mFirstSnapshot)
8779 {
8780 if (aSetError)
8781 return setError(E_FAIL, tr("This machine does not have any snapshots"));
8782 return E_FAIL;
8783 }
8784
8785 if (aId.isEmpty())
8786 aSnapshot = mData->mFirstSnapshot;
8787 else
8788 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
8789
8790 if (!aSnapshot)
8791 {
8792 if (aSetError)
8793 return setError(E_FAIL,
8794 tr("Could not find a snapshot with UUID {%s}"),
8795 aId.toString().c_str());
8796 return E_FAIL;
8797 }
8798
8799 return S_OK;
8800}
8801
8802/**
8803 * Returns the snapshot with the given name or fails of no such snapshot.
8804 *
8805 * @param aName snapshot name to find
8806 * @param aSnapshot where to return the found snapshot
8807 * @param aSetError true to set extended error info on failure
8808 */
8809HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
8810 ComObjPtr<Snapshot> &aSnapshot,
8811 bool aSetError /* = false */)
8812{
8813 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
8814
8815 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
8816
8817 if (!mData->mFirstSnapshot)
8818 {
8819 if (aSetError)
8820 return setError(VBOX_E_OBJECT_NOT_FOUND,
8821 tr("This machine does not have any snapshots"));
8822 return VBOX_E_OBJECT_NOT_FOUND;
8823 }
8824
8825 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
8826
8827 if (!aSnapshot)
8828 {
8829 if (aSetError)
8830 return setError(VBOX_E_OBJECT_NOT_FOUND,
8831 tr("Could not find a snapshot named '%s'"), strName.c_str());
8832 return VBOX_E_OBJECT_NOT_FOUND;
8833 }
8834
8835 return S_OK;
8836}
8837
8838/**
8839 * Returns a storage controller object with the given name.
8840 *
8841 * @param aName storage controller name to find
8842 * @param aStorageController where to return the found storage controller
8843 * @param aSetError true to set extended error info on failure
8844 */
8845HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
8846 ComObjPtr<StorageController> &aStorageController,
8847 bool aSetError /* = false */)
8848{
8849 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
8850
8851 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
8852 it != mStorageControllers->end();
8853 ++it)
8854 {
8855 if ((*it)->getName() == aName)
8856 {
8857 aStorageController = (*it);
8858 return S_OK;
8859 }
8860 }
8861
8862 if (aSetError)
8863 return setError(VBOX_E_OBJECT_NOT_FOUND,
8864 tr("Could not find a storage controller named '%s'"),
8865 aName.c_str());
8866 return VBOX_E_OBJECT_NOT_FOUND;
8867}
8868
8869HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
8870 MediaData::AttachmentList &atts)
8871{
8872 AutoCaller autoCaller(this);
8873 if (FAILED(autoCaller.rc())) return autoCaller.rc();
8874
8875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8876
8877 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
8878 it != mMediaData->mAttachments.end();
8879 ++it)
8880 {
8881 const ComObjPtr<MediumAttachment> &pAtt = *it;
8882
8883 // should never happen, but deal with NULL pointers in the list.
8884 AssertStmt(!pAtt.isNull(), continue);
8885
8886 // getControllerName() needs caller+read lock
8887 AutoCaller autoAttCaller(pAtt);
8888 if (FAILED(autoAttCaller.rc()))
8889 {
8890 atts.clear();
8891 return autoAttCaller.rc();
8892 }
8893 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
8894
8895 if (pAtt->getControllerName() == aName)
8896 atts.push_back(pAtt);
8897 }
8898
8899 return S_OK;
8900}
8901
8902/**
8903 * Helper for #saveSettings. Cares about renaming the settings directory and
8904 * file if the machine name was changed and about creating a new settings file
8905 * if this is a new machine.
8906 *
8907 * @note Must be never called directly but only from #saveSettings().
8908 */
8909HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
8910{
8911 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
8912
8913 HRESULT rc = S_OK;
8914
8915 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
8916
8917 /* attempt to rename the settings file if machine name is changed */
8918 if ( mUserData->s.fNameSync
8919 && mUserData.isBackedUp()
8920 && mUserData.backedUpData()->s.strName != mUserData->s.strName
8921 )
8922 {
8923 bool dirRenamed = false;
8924 bool fileRenamed = false;
8925
8926 Utf8Str configFile, newConfigFile;
8927 Utf8Str configFilePrev, newConfigFilePrev;
8928 Utf8Str configDir, newConfigDir;
8929
8930 do
8931 {
8932 int vrc = VINF_SUCCESS;
8933
8934 Utf8Str name = mUserData.backedUpData()->s.strName;
8935 Utf8Str newName = mUserData->s.strName;
8936
8937 configFile = mData->m_strConfigFileFull;
8938
8939 /* first, rename the directory if it matches the machine name */
8940 configDir = configFile;
8941 configDir.stripFilename();
8942 newConfigDir = configDir;
8943 if (!strcmp(RTPathFilename(configDir.c_str()), name.c_str()))
8944 {
8945 newConfigDir.stripFilename();
8946 newConfigDir.append(RTPATH_DELIMITER);
8947 newConfigDir.append(newName);
8948 /* new dir and old dir cannot be equal here because of 'if'
8949 * above and because name != newName */
8950 Assert(configDir != newConfigDir);
8951 if (!fSettingsFileIsNew)
8952 {
8953 /* perform real rename only if the machine is not new */
8954 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
8955 if (RT_FAILURE(vrc))
8956 {
8957 rc = setError(E_FAIL,
8958 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
8959 configDir.c_str(),
8960 newConfigDir.c_str(),
8961 vrc);
8962 break;
8963 }
8964 dirRenamed = true;
8965 }
8966 }
8967
8968 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
8969 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
8970
8971 /* then try to rename the settings file itself */
8972 if (newConfigFile != configFile)
8973 {
8974 /* get the path to old settings file in renamed directory */
8975 configFile = Utf8StrFmt("%s%c%s",
8976 newConfigDir.c_str(),
8977 RTPATH_DELIMITER,
8978 RTPathFilename(configFile.c_str()));
8979 if (!fSettingsFileIsNew)
8980 {
8981 /* perform real rename only if the machine is not new */
8982 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
8983 if (RT_FAILURE(vrc))
8984 {
8985 rc = setError(E_FAIL,
8986 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
8987 configFile.c_str(),
8988 newConfigFile.c_str(),
8989 vrc);
8990 break;
8991 }
8992 fileRenamed = true;
8993 configFilePrev = configFile;
8994 configFilePrev += "-prev";
8995 newConfigFilePrev = newConfigFile;
8996 newConfigFilePrev += "-prev";
8997 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
8998 }
8999 }
9000
9001 // update m_strConfigFileFull amd mConfigFile
9002 mData->m_strConfigFileFull = newConfigFile;
9003 // compute the relative path too
9004 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9005
9006 // store the old and new so that VirtualBox::saveSettings() can update
9007 // the media registry
9008 if ( mData->mRegistered
9009 && configDir != newConfigDir)
9010 {
9011 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9012
9013 if (pfNeedsGlobalSaveSettings)
9014 *pfNeedsGlobalSaveSettings = true;
9015 }
9016
9017 // in the saved state file path, replace the old directory with the new directory
9018 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9019 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9020
9021 // and do the same thing for the saved state file paths of all the online snapshots
9022 if (mData->mFirstSnapshot)
9023 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9024 newConfigDir.c_str());
9025 }
9026 while (0);
9027
9028 if (FAILED(rc))
9029 {
9030 /* silently try to rename everything back */
9031 if (fileRenamed)
9032 {
9033 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9034 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9035 }
9036 if (dirRenamed)
9037 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9038 }
9039
9040 if (FAILED(rc)) return rc;
9041 }
9042
9043 if (fSettingsFileIsNew)
9044 {
9045 /* create a virgin config file */
9046 int vrc = VINF_SUCCESS;
9047
9048 /* ensure the settings directory exists */
9049 Utf8Str path(mData->m_strConfigFileFull);
9050 path.stripFilename();
9051 if (!RTDirExists(path.c_str()))
9052 {
9053 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9054 if (RT_FAILURE(vrc))
9055 {
9056 return setError(E_FAIL,
9057 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9058 path.c_str(),
9059 vrc);
9060 }
9061 }
9062
9063 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9064 path = Utf8Str(mData->m_strConfigFileFull);
9065 RTFILE f = NIL_RTFILE;
9066 vrc = RTFileOpen(&f, path.c_str(),
9067 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9068 if (RT_FAILURE(vrc))
9069 return setError(E_FAIL,
9070 tr("Could not create the settings file '%s' (%Rrc)"),
9071 path.c_str(),
9072 vrc);
9073 RTFileClose(f);
9074 }
9075
9076 return rc;
9077}
9078
9079/**
9080 * Saves and commits machine data, user data and hardware data.
9081 *
9082 * Note that on failure, the data remains uncommitted.
9083 *
9084 * @a aFlags may combine the following flags:
9085 *
9086 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9087 * Used when saving settings after an operation that makes them 100%
9088 * correspond to the settings from the current snapshot.
9089 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9090 * #isReallyModified() returns false. This is necessary for cases when we
9091 * change machine data directly, not through the backup()/commit() mechanism.
9092 * - SaveS_Force: settings will be saved without doing a deep compare of the
9093 * settings structures. This is used when this is called because snapshots
9094 * have changed to avoid the overhead of the deep compare.
9095 *
9096 * @note Must be called from under this object's write lock. Locks children for
9097 * writing.
9098 *
9099 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9100 * initialized to false and that will be set to true by this function if
9101 * the caller must invoke VirtualBox::saveSettings() because the global
9102 * settings have changed. This will happen if a machine rename has been
9103 * saved and the global machine and media registries will therefore need
9104 * updating.
9105 */
9106HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9107 int aFlags /*= 0*/)
9108{
9109 LogFlowThisFuncEnter();
9110
9111 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9112
9113 /* make sure child objects are unable to modify the settings while we are
9114 * saving them */
9115 ensureNoStateDependencies();
9116
9117 AssertReturn(!isSnapshotMachine(),
9118 E_FAIL);
9119
9120 HRESULT rc = S_OK;
9121 bool fNeedsWrite = false;
9122
9123 /* First, prepare to save settings. It will care about renaming the
9124 * settings directory and file if the machine name was changed and about
9125 * creating a new settings file if this is a new machine. */
9126 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9127 if (FAILED(rc)) return rc;
9128
9129 // keep a pointer to the current settings structures
9130 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9131 settings::MachineConfigFile *pNewConfig = NULL;
9132
9133 try
9134 {
9135 // make a fresh one to have everyone write stuff into
9136 pNewConfig = new settings::MachineConfigFile(NULL);
9137 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9138
9139 // now go and copy all the settings data from COM to the settings structures
9140 // (this calles saveSettings() on all the COM objects in the machine)
9141 copyMachineDataToSettings(*pNewConfig);
9142
9143 if (aFlags & SaveS_ResetCurStateModified)
9144 {
9145 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9146 mData->mCurrentStateModified = FALSE;
9147 fNeedsWrite = true; // always, no need to compare
9148 }
9149 else if (aFlags & SaveS_Force)
9150 {
9151 fNeedsWrite = true; // always, no need to compare
9152 }
9153 else
9154 {
9155 if (!mData->mCurrentStateModified)
9156 {
9157 // do a deep compare of the settings that we just saved with the settings
9158 // previously stored in the config file; this invokes MachineConfigFile::operator==
9159 // which does a deep compare of all the settings, which is expensive but less expensive
9160 // than writing out XML in vain
9161 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9162
9163 // could still be modified if any settings changed
9164 mData->mCurrentStateModified = fAnySettingsChanged;
9165
9166 fNeedsWrite = fAnySettingsChanged;
9167 }
9168 else
9169 fNeedsWrite = true;
9170 }
9171
9172 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9173
9174 if (fNeedsWrite)
9175 // now spit it all out!
9176 pNewConfig->write(mData->m_strConfigFileFull);
9177
9178 mData->pMachineConfigFile = pNewConfig;
9179 delete pOldConfig;
9180 commit();
9181
9182 // after saving settings, we are no longer different from the XML on disk
9183 mData->flModifications = 0;
9184 }
9185 catch (HRESULT err)
9186 {
9187 // we assume that error info is set by the thrower
9188 rc = err;
9189
9190 // restore old config
9191 delete pNewConfig;
9192 mData->pMachineConfigFile = pOldConfig;
9193 }
9194 catch (...)
9195 {
9196 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9197 }
9198
9199 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9200 {
9201 /* Fire the data change event, even on failure (since we've already
9202 * committed all data). This is done only for SessionMachines because
9203 * mutable Machine instances are always not registered (i.e. private
9204 * to the client process that creates them) and thus don't need to
9205 * inform callbacks. */
9206 if (isSessionMachine())
9207 mParent->onMachineDataChange(mData->mUuid);
9208 }
9209
9210 LogFlowThisFunc(("rc=%08X\n", rc));
9211 LogFlowThisFuncLeave();
9212 return rc;
9213}
9214
9215/**
9216 * Implementation for saving the machine settings into the given
9217 * settings::MachineConfigFile instance. This copies machine extradata
9218 * from the previous machine config file in the instance data, if any.
9219 *
9220 * This gets called from two locations:
9221 *
9222 * -- Machine::saveSettings(), during the regular XML writing;
9223 *
9224 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9225 * exported to OVF and we write the VirtualBox proprietary XML
9226 * into a <vbox:Machine> tag.
9227 *
9228 * This routine fills all the fields in there, including snapshots, *except*
9229 * for the following:
9230 *
9231 * -- fCurrentStateModified. There is some special logic associated with that.
9232 *
9233 * The caller can then call MachineConfigFile::write() or do something else
9234 * with it.
9235 *
9236 * Caller must hold the machine lock!
9237 *
9238 * This throws XML errors and HRESULT, so the caller must have a catch block!
9239 */
9240void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
9241{
9242 // deep copy extradata
9243 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9244
9245 config.uuid = mData->mUuid;
9246
9247 // copy name, description, OS type, teleport, UTC etc.
9248 config.machineUserData = mUserData->s;
9249
9250 if ( mData->mMachineState == MachineState_Saved
9251 || mData->mMachineState == MachineState_Restoring
9252 // when deleting a snapshot we may or may not have a saved state in the current state,
9253 // so let's not assert here please
9254 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9255 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9256 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9257 && (!mSSData->strStateFilePath.isEmpty())
9258 )
9259 )
9260 {
9261 Assert(!mSSData->strStateFilePath.isEmpty());
9262 /* try to make the file name relative to the settings file dir */
9263 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9264 }
9265 else
9266 {
9267 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9268 config.strStateFile.setNull();
9269 }
9270
9271 if (mData->mCurrentSnapshot)
9272 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
9273 else
9274 config.uuidCurrentSnapshot.clear();
9275
9276 config.timeLastStateChange = mData->mLastStateChange;
9277 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9278 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9279
9280 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9281 if (FAILED(rc)) throw rc;
9282
9283 rc = saveStorageControllers(config.storageMachine);
9284 if (FAILED(rc)) throw rc;
9285
9286 // save machine's media registry if this is VirtualBox 4.0 or later
9287 if (config.canHaveOwnMediaRegistry())
9288 {
9289 // determine machine folder
9290 Utf8Str strMachineFolder = getSettingsFileFull();
9291 strMachineFolder.stripFilename();
9292 mParent->saveMediaRegistry(config.mediaRegistry,
9293 getId(), // only media with registry ID == machine UUID
9294 strMachineFolder);
9295 // this throws HRESULT
9296 }
9297
9298 // save snapshots
9299 rc = saveAllSnapshots(config);
9300 if (FAILED(rc)) throw rc;
9301}
9302
9303/**
9304 * Saves all snapshots of the machine into the given machine config file. Called
9305 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9306 * @param config
9307 * @return
9308 */
9309HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
9310{
9311 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9312
9313 HRESULT rc = S_OK;
9314
9315 try
9316 {
9317 config.llFirstSnapshot.clear();
9318
9319 if (mData->mFirstSnapshot)
9320 {
9321 settings::Snapshot snapNew;
9322 config.llFirstSnapshot.push_back(snapNew);
9323
9324 // get reference to the fresh copy of the snapshot on the list and
9325 // work on that copy directly to avoid excessive copying later
9326 settings::Snapshot &snap = config.llFirstSnapshot.front();
9327
9328 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
9329 if (FAILED(rc)) throw rc;
9330 }
9331
9332// if (mType == IsSessionMachine)
9333// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9334
9335 }
9336 catch (HRESULT err)
9337 {
9338 /* we assume that error info is set by the thrower */
9339 rc = err;
9340 }
9341 catch (...)
9342 {
9343 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9344 }
9345
9346 return rc;
9347}
9348
9349/**
9350 * Saves the VM hardware configuration. It is assumed that the
9351 * given node is empty.
9352 *
9353 * @param data Reference to the settings object for the hardware config.
9354 * @param pDbg Pointer to the settings object for the debugging config
9355 * which happens to live in mHWData.
9356 * @param pAutostart Pointer to the settings object for the autostart config
9357 * which happens to live in mHWData.
9358 */
9359HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9360 settings::Autostart *pAutostart)
9361{
9362 HRESULT rc = S_OK;
9363
9364 try
9365 {
9366 /* The hardware version attribute (optional).
9367 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9368 if ( mHWData->mHWVersion == "1"
9369 && mSSData->strStateFilePath.isEmpty()
9370 )
9371 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. */
9372
9373 data.strVersion = mHWData->mHWVersion;
9374 data.uuid = mHWData->mHardwareUUID;
9375
9376 // CPU
9377 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9378 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
9379 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9380 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9381 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9382 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9383 data.fPAE = !!mHWData->mPAEEnabled;
9384 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9385
9386 /* Standard and Extended CPUID leafs. */
9387 data.llCpuIdLeafs.clear();
9388 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
9389 {
9390 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9391 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9392 }
9393 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
9394 {
9395 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9396 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9397 }
9398
9399 data.cCPUs = mHWData->mCPUCount;
9400 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9401 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9402
9403 data.llCpus.clear();
9404 if (data.fCpuHotPlug)
9405 {
9406 for (unsigned idx = 0; idx < data.cCPUs; idx++)
9407 {
9408 if (mHWData->mCPUAttached[idx])
9409 {
9410 settings::Cpu cpu;
9411 cpu.ulId = idx;
9412 data.llCpus.push_back(cpu);
9413 }
9414 }
9415 }
9416
9417 // memory
9418 data.ulMemorySizeMB = mHWData->mMemorySize;
9419 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9420
9421 // firmware
9422 data.firmwareType = mHWData->mFirmwareType;
9423
9424 // HID
9425 data.pointingHidType = mHWData->mPointingHidType;
9426 data.keyboardHidType = mHWData->mKeyboardHidType;
9427
9428 // chipset
9429 data.chipsetType = mHWData->mChipsetType;
9430
9431 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9432
9433 // HPET
9434 data.fHpetEnabled = !!mHWData->mHpetEnabled;
9435
9436 // boot order
9437 data.mapBootOrder.clear();
9438 for (size_t i = 0;
9439 i < RT_ELEMENTS(mHWData->mBootOrder);
9440 ++i)
9441 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9442
9443 // display
9444 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9445 data.cMonitors = mHWData->mMonitorCount;
9446 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9447 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9448
9449 /* VRDEServer settings (optional) */
9450 rc = mVRDEServer->saveSettings(data.vrdeSettings);
9451 if (FAILED(rc)) throw rc;
9452
9453 /* BIOS (required) */
9454 rc = mBIOSSettings->saveSettings(data.biosSettings);
9455 if (FAILED(rc)) throw rc;
9456
9457 /* USB Controller (required) */
9458 rc = mUSBController->saveSettings(data.usbController);
9459 if (FAILED(rc)) throw rc;
9460
9461 /* Network adapters (required) */
9462 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
9463 data.llNetworkAdapters.clear();
9464 /* Write out only the nominal number of network adapters for this
9465 * chipset type. Since Machine::commit() hasn't been called there
9466 * may be extra NIC settings in the vector. */
9467 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
9468 {
9469 settings::NetworkAdapter nic;
9470 nic.ulSlot = slot;
9471 /* paranoia check... must not be NULL, but must not crash either. */
9472 if (mNetworkAdapters[slot])
9473 {
9474 rc = mNetworkAdapters[slot]->saveSettings(nic);
9475 if (FAILED(rc)) throw rc;
9476
9477 data.llNetworkAdapters.push_back(nic);
9478 }
9479 }
9480
9481 /* Serial ports */
9482 data.llSerialPorts.clear();
9483 for (ULONG slot = 0;
9484 slot < RT_ELEMENTS(mSerialPorts);
9485 ++slot)
9486 {
9487 settings::SerialPort s;
9488 s.ulSlot = slot;
9489 rc = mSerialPorts[slot]->saveSettings(s);
9490 if (FAILED(rc)) return rc;
9491
9492 data.llSerialPorts.push_back(s);
9493 }
9494
9495 /* Parallel ports */
9496 data.llParallelPorts.clear();
9497 for (ULONG slot = 0;
9498 slot < RT_ELEMENTS(mParallelPorts);
9499 ++slot)
9500 {
9501 settings::ParallelPort p;
9502 p.ulSlot = slot;
9503 rc = mParallelPorts[slot]->saveSettings(p);
9504 if (FAILED(rc)) return rc;
9505
9506 data.llParallelPorts.push_back(p);
9507 }
9508
9509 /* Audio adapter */
9510 rc = mAudioAdapter->saveSettings(data.audioAdapter);
9511 if (FAILED(rc)) return rc;
9512
9513 /* Shared folders */
9514 data.llSharedFolders.clear();
9515 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
9516 it != mHWData->mSharedFolders.end();
9517 ++it)
9518 {
9519 SharedFolder *pSF = *it;
9520 AutoCaller sfCaller(pSF);
9521 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
9522 settings::SharedFolder sf;
9523 sf.strName = pSF->getName();
9524 sf.strHostPath = pSF->getHostPath();
9525 sf.fWritable = !!pSF->isWritable();
9526 sf.fAutoMount = !!pSF->isAutoMounted();
9527
9528 data.llSharedFolders.push_back(sf);
9529 }
9530
9531 // clipboard
9532 data.clipboardMode = mHWData->mClipboardMode;
9533
9534 /* Guest */
9535 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
9536
9537 // IO settings
9538 data.ioSettings.fIoCacheEnabled = !!mHWData->mIoCacheEnabled;
9539 data.ioSettings.ulIoCacheSize = mHWData->mIoCacheSize;
9540
9541 /* BandwidthControl (required) */
9542 rc = mBandwidthControl->saveSettings(data.ioSettings);
9543 if (FAILED(rc)) throw rc;
9544
9545 /* Host PCI devices */
9546 for (HWData::PciDeviceAssignmentList::const_iterator it = mHWData->mPciDeviceAssignments.begin();
9547 it != mHWData->mPciDeviceAssignments.end();
9548 ++it)
9549 {
9550 ComObjPtr<PciDeviceAttachment> pda = *it;
9551 settings::HostPciDeviceAttachment hpda;
9552
9553 rc = pda->saveSettings(hpda);
9554 if (FAILED(rc)) throw rc;
9555
9556 data.pciAttachments.push_back(hpda);
9557 }
9558
9559
9560 // guest properties
9561 data.llGuestProperties.clear();
9562#ifdef VBOX_WITH_GUEST_PROPS
9563 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
9564 it != mHWData->mGuestProperties.end();
9565 ++it)
9566 {
9567 HWData::GuestProperty property = *it;
9568
9569 /* Remove transient guest properties at shutdown unless we
9570 * are saving state */
9571 if ( ( mData->mMachineState == MachineState_PoweredOff
9572 || mData->mMachineState == MachineState_Aborted
9573 || mData->mMachineState == MachineState_Teleported)
9574 && ( property.mFlags & guestProp::TRANSIENT
9575 || property.mFlags & guestProp::TRANSRESET))
9576 continue;
9577 settings::GuestProperty prop;
9578 prop.strName = property.strName;
9579 prop.strValue = property.strValue;
9580 prop.timestamp = property.mTimestamp;
9581 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
9582 guestProp::writeFlags(property.mFlags, szFlags);
9583 prop.strFlags = szFlags;
9584
9585 data.llGuestProperties.push_back(prop);
9586 }
9587
9588 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
9589 /* I presume this doesn't require a backup(). */
9590 mData->mGuestPropertiesModified = FALSE;
9591#endif /* VBOX_WITH_GUEST_PROPS defined */
9592
9593 *pDbg = mHWData->mDebugging;
9594 *pAutostart = mHWData->mAutostart;
9595 }
9596 catch(std::bad_alloc &)
9597 {
9598 return E_OUTOFMEMORY;
9599 }
9600
9601 AssertComRC(rc);
9602 return rc;
9603}
9604
9605/**
9606 * Saves the storage controller configuration.
9607 *
9608 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
9609 */
9610HRESULT Machine::saveStorageControllers(settings::Storage &data)
9611{
9612 data.llStorageControllers.clear();
9613
9614 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9615 it != mStorageControllers->end();
9616 ++it)
9617 {
9618 HRESULT rc;
9619 ComObjPtr<StorageController> pCtl = *it;
9620
9621 settings::StorageController ctl;
9622 ctl.strName = pCtl->getName();
9623 ctl.controllerType = pCtl->getControllerType();
9624 ctl.storageBus = pCtl->getStorageBus();
9625 ctl.ulInstance = pCtl->getInstance();
9626 ctl.fBootable = pCtl->getBootable();
9627
9628 /* Save the port count. */
9629 ULONG portCount;
9630 rc = pCtl->COMGETTER(PortCount)(&portCount);
9631 ComAssertComRCRet(rc, rc);
9632 ctl.ulPortCount = portCount;
9633
9634 /* Save fUseHostIOCache */
9635 BOOL fUseHostIOCache;
9636 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
9637 ComAssertComRCRet(rc, rc);
9638 ctl.fUseHostIOCache = !!fUseHostIOCache;
9639
9640 /* Save IDE emulation settings. */
9641 if (ctl.controllerType == StorageControllerType_IntelAhci)
9642 {
9643 if ( (FAILED(rc = pCtl->GetIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
9644 || (FAILED(rc = pCtl->GetIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
9645 || (FAILED(rc = pCtl->GetIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
9646 || (FAILED(rc = pCtl->GetIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
9647 )
9648 ComAssertComRCRet(rc, rc);
9649 }
9650
9651 /* save the devices now. */
9652 rc = saveStorageDevices(pCtl, ctl);
9653 ComAssertComRCRet(rc, rc);
9654
9655 data.llStorageControllers.push_back(ctl);
9656 }
9657
9658 return S_OK;
9659}
9660
9661/**
9662 * Saves the hard disk configuration.
9663 */
9664HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
9665 settings::StorageController &data)
9666{
9667 MediaData::AttachmentList atts;
9668
9669 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
9670 if (FAILED(rc)) return rc;
9671
9672 data.llAttachedDevices.clear();
9673 for (MediaData::AttachmentList::const_iterator it = atts.begin();
9674 it != atts.end();
9675 ++it)
9676 {
9677 settings::AttachedDevice dev;
9678
9679 MediumAttachment *pAttach = *it;
9680 Medium *pMedium = pAttach->getMedium();
9681
9682 dev.deviceType = pAttach->getType();
9683 dev.lPort = pAttach->getPort();
9684 dev.lDevice = pAttach->getDevice();
9685 if (pMedium)
9686 {
9687 if (pMedium->isHostDrive())
9688 dev.strHostDriveSrc = pMedium->getLocationFull();
9689 else
9690 dev.uuid = pMedium->getId();
9691 dev.fPassThrough = pAttach->getPassthrough();
9692 dev.fTempEject = pAttach->getTempEject();
9693 dev.fDiscard = pAttach->getDiscard();
9694 }
9695
9696 dev.strBwGroup = pAttach->getBandwidthGroup();
9697
9698 data.llAttachedDevices.push_back(dev);
9699 }
9700
9701 return S_OK;
9702}
9703
9704/**
9705 * Saves machine state settings as defined by aFlags
9706 * (SaveSTS_* values).
9707 *
9708 * @param aFlags Combination of SaveSTS_* flags.
9709 *
9710 * @note Locks objects for writing.
9711 */
9712HRESULT Machine::saveStateSettings(int aFlags)
9713{
9714 if (aFlags == 0)
9715 return S_OK;
9716
9717 AutoCaller autoCaller(this);
9718 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9719
9720 /* This object's write lock is also necessary to serialize file access
9721 * (prevent concurrent reads and writes) */
9722 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9723
9724 HRESULT rc = S_OK;
9725
9726 Assert(mData->pMachineConfigFile);
9727
9728 try
9729 {
9730 if (aFlags & SaveSTS_CurStateModified)
9731 mData->pMachineConfigFile->fCurrentStateModified = true;
9732
9733 if (aFlags & SaveSTS_StateFilePath)
9734 {
9735 if (!mSSData->strStateFilePath.isEmpty())
9736 /* try to make the file name relative to the settings file dir */
9737 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
9738 else
9739 mData->pMachineConfigFile->strStateFile.setNull();
9740 }
9741
9742 if (aFlags & SaveSTS_StateTimeStamp)
9743 {
9744 Assert( mData->mMachineState != MachineState_Aborted
9745 || mSSData->strStateFilePath.isEmpty());
9746
9747 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
9748
9749 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
9750//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
9751 }
9752
9753 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
9754 }
9755 catch (...)
9756 {
9757 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9758 }
9759
9760 return rc;
9761}
9762
9763/**
9764 * Ensures that the given medium is added to a media registry. If this machine
9765 * was created with 4.0 or later, then the machine registry is used. Otherwise
9766 * the global VirtualBox media registry is used.
9767 *
9768 * Caller must NOT hold machine lock, media tree or any medium locks!
9769 *
9770 * @param pMedium
9771 */
9772void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
9773{
9774 /* Paranoia checks: do not hold machine or media tree locks. */
9775 AssertReturnVoid(!isWriteLockOnCurrentThread());
9776 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
9777
9778 ComObjPtr<Medium> pBase;
9779 {
9780 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9781 pBase = pMedium->getBase();
9782 }
9783
9784 /* Paranoia checks: do not hold medium locks. */
9785 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
9786 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
9787
9788 // decide which medium registry to use now that the medium is attached:
9789 Guid uuid;
9790 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
9791 // machine XML is VirtualBox 4.0 or higher:
9792 uuid = getId(); // machine UUID
9793 else
9794 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
9795
9796 if (pMedium->addRegistry(uuid, false /* fRecurse */))
9797 mParent->markRegistryModified(uuid);
9798
9799 /* For more complex hard disk structures it can happen that the base
9800 * medium isn't yet associated with any medium registry. Do that now. */
9801 if (pMedium != pBase)
9802 {
9803 if (pBase->addRegistry(uuid, true /* fRecurse */))
9804 mParent->markRegistryModified(uuid);
9805 }
9806}
9807
9808/**
9809 * Creates differencing hard disks for all normal hard disks attached to this
9810 * machine and a new set of attachments to refer to created disks.
9811 *
9812 * Used when taking a snapshot or when deleting the current state. Gets called
9813 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
9814 *
9815 * This method assumes that mMediaData contains the original hard disk attachments
9816 * it needs to create diffs for. On success, these attachments will be replaced
9817 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
9818 * called to delete created diffs which will also rollback mMediaData and restore
9819 * whatever was backed up before calling this method.
9820 *
9821 * Attachments with non-normal hard disks are left as is.
9822 *
9823 * If @a aOnline is @c false then the original hard disks that require implicit
9824 * diffs will be locked for reading. Otherwise it is assumed that they are
9825 * already locked for writing (when the VM was started). Note that in the latter
9826 * case it is responsibility of the caller to lock the newly created diffs for
9827 * writing if this method succeeds.
9828 *
9829 * @param aProgress Progress object to run (must contain at least as
9830 * many operations left as the number of hard disks
9831 * attached).
9832 * @param aOnline Whether the VM was online prior to this operation.
9833 *
9834 * @note The progress object is not marked as completed, neither on success nor
9835 * on failure. This is a responsibility of the caller.
9836 *
9837 * @note Locks this object for writing.
9838 */
9839HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
9840 ULONG aWeight,
9841 bool aOnline)
9842{
9843 LogFlowThisFunc(("aOnline=%d\n", aOnline));
9844
9845 AutoCaller autoCaller(this);
9846 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9847
9848 AutoMultiWriteLock2 alock(this->lockHandle(),
9849 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9850
9851 /* must be in a protective state because we release the lock below */
9852 AssertReturn( mData->mMachineState == MachineState_Saving
9853 || mData->mMachineState == MachineState_LiveSnapshotting
9854 || mData->mMachineState == MachineState_RestoringSnapshot
9855 || mData->mMachineState == MachineState_DeletingSnapshot
9856 , E_FAIL);
9857
9858 HRESULT rc = S_OK;
9859
9860 MediumLockListMap lockedMediaOffline;
9861 MediumLockListMap *lockedMediaMap;
9862 if (aOnline)
9863 lockedMediaMap = &mData->mSession.mLockedMedia;
9864 else
9865 lockedMediaMap = &lockedMediaOffline;
9866
9867 try
9868 {
9869 if (!aOnline)
9870 {
9871 /* lock all attached hard disks early to detect "in use"
9872 * situations before creating actual diffs */
9873 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
9874 it != mMediaData->mAttachments.end();
9875 ++it)
9876 {
9877 MediumAttachment* pAtt = *it;
9878 if (pAtt->getType() == DeviceType_HardDisk)
9879 {
9880 Medium* pMedium = pAtt->getMedium();
9881 Assert(pMedium);
9882
9883 MediumLockList *pMediumLockList(new MediumLockList());
9884 alock.release();
9885 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
9886 false /* fMediumLockWrite */,
9887 NULL,
9888 *pMediumLockList);
9889 alock.acquire();
9890 if (FAILED(rc))
9891 {
9892 delete pMediumLockList;
9893 throw rc;
9894 }
9895 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
9896 if (FAILED(rc))
9897 {
9898 throw setError(rc,
9899 tr("Collecting locking information for all attached media failed"));
9900 }
9901 }
9902 }
9903
9904 /* Now lock all media. If this fails, nothing is locked. */
9905 alock.release();
9906 rc = lockedMediaMap->Lock();
9907 alock.acquire();
9908 if (FAILED(rc))
9909 {
9910 throw setError(rc,
9911 tr("Locking of attached media failed"));
9912 }
9913 }
9914
9915 /* remember the current list (note that we don't use backup() since
9916 * mMediaData may be already backed up) */
9917 MediaData::AttachmentList atts = mMediaData->mAttachments;
9918
9919 /* start from scratch */
9920 mMediaData->mAttachments.clear();
9921
9922 /* go through remembered attachments and create diffs for normal hard
9923 * disks and attach them */
9924 for (MediaData::AttachmentList::const_iterator it = atts.begin();
9925 it != atts.end();
9926 ++it)
9927 {
9928 MediumAttachment* pAtt = *it;
9929
9930 DeviceType_T devType = pAtt->getType();
9931 Medium* pMedium = pAtt->getMedium();
9932
9933 if ( devType != DeviceType_HardDisk
9934 || pMedium == NULL
9935 || pMedium->getType() != MediumType_Normal)
9936 {
9937 /* copy the attachment as is */
9938
9939 /** @todo the progress object created in Console::TakeSnaphot
9940 * only expects operations for hard disks. Later other
9941 * device types need to show up in the progress as well. */
9942 if (devType == DeviceType_HardDisk)
9943 {
9944 if (pMedium == NULL)
9945 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
9946 aWeight); // weight
9947 else
9948 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
9949 pMedium->getBase()->getName().c_str()).raw(),
9950 aWeight); // weight
9951 }
9952
9953 mMediaData->mAttachments.push_back(pAtt);
9954 continue;
9955 }
9956
9957 /* need a diff */
9958 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
9959 pMedium->getBase()->getName().c_str()).raw(),
9960 aWeight); // weight
9961
9962 Utf8Str strFullSnapshotFolder;
9963 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
9964
9965 ComObjPtr<Medium> diff;
9966 diff.createObject();
9967 // store the diff in the same registry as the parent
9968 // (this cannot fail here because we can't create implicit diffs for
9969 // unregistered images)
9970 Guid uuidRegistryParent;
9971 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
9972 Assert(fInRegistry); NOREF(fInRegistry);
9973 rc = diff->init(mParent,
9974 pMedium->getPreferredDiffFormat(),
9975 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
9976 uuidRegistryParent);
9977 if (FAILED(rc)) throw rc;
9978
9979 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
9980 * the push_back? Looks like we're going to release medium with the
9981 * wrong kind of lock (general issue with if we fail anywhere at all)
9982 * and an orphaned VDI in the snapshots folder. */
9983
9984 /* update the appropriate lock list */
9985 MediumLockList *pMediumLockList;
9986 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
9987 AssertComRCThrowRC(rc);
9988 if (aOnline)
9989 {
9990 alock.release();
9991 rc = pMediumLockList->Update(pMedium, false);
9992 alock.acquire();
9993 AssertComRCThrowRC(rc);
9994 }
9995
9996 /* release the locks before the potentially lengthy operation */
9997 alock.release();
9998 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
9999 pMediumLockList,
10000 NULL /* aProgress */,
10001 true /* aWait */);
10002 alock.acquire();
10003 if (FAILED(rc)) throw rc;
10004
10005 rc = lockedMediaMap->Unlock();
10006 AssertComRCThrowRC(rc);
10007 alock.release();
10008 rc = pMediumLockList->Append(diff, true);
10009 alock.acquire();
10010 AssertComRCThrowRC(rc);
10011 alock.release();
10012 rc = lockedMediaMap->Lock();
10013 alock.acquire();
10014 AssertComRCThrowRC(rc);
10015
10016 rc = diff->addBackReference(mData->mUuid);
10017 AssertComRCThrowRC(rc);
10018
10019 /* add a new attachment */
10020 ComObjPtr<MediumAttachment> attachment;
10021 attachment.createObject();
10022 rc = attachment->init(this,
10023 diff,
10024 pAtt->getControllerName(),
10025 pAtt->getPort(),
10026 pAtt->getDevice(),
10027 DeviceType_HardDisk,
10028 true /* aImplicit */,
10029 false /* aPassthrough */,
10030 false /* aTempEject */,
10031 pAtt->getNonRotational(),
10032 pAtt->getDiscard(),
10033 pAtt->getBandwidthGroup());
10034 if (FAILED(rc)) throw rc;
10035
10036 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10037 AssertComRCThrowRC(rc);
10038 mMediaData->mAttachments.push_back(attachment);
10039 }
10040 }
10041 catch (HRESULT aRC) { rc = aRC; }
10042
10043 /* unlock all hard disks we locked */
10044 if (!aOnline)
10045 {
10046 ErrorInfoKeeper eik;
10047
10048 HRESULT rc1 = lockedMediaMap->Clear();
10049 AssertComRC(rc1);
10050 }
10051
10052 if (FAILED(rc))
10053 {
10054 MultiResult mrc = rc;
10055
10056 alock.release();
10057 mrc = deleteImplicitDiffs();
10058 }
10059
10060 return rc;
10061}
10062
10063/**
10064 * Deletes implicit differencing hard disks created either by
10065 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10066 *
10067 * Note that to delete hard disks created by #AttachDevice() this method is
10068 * called from #fixupMedia() when the changes are rolled back.
10069 *
10070 * @note Locks this object for writing.
10071 */
10072HRESULT Machine::deleteImplicitDiffs()
10073{
10074 AutoCaller autoCaller(this);
10075 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10076
10077 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10078 LogFlowThisFuncEnter();
10079
10080 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10081
10082 HRESULT rc = S_OK;
10083
10084 MediaData::AttachmentList implicitAtts;
10085
10086 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10087
10088 /* enumerate new attachments */
10089 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10090 it != mMediaData->mAttachments.end();
10091 ++it)
10092 {
10093 ComObjPtr<Medium> hd = (*it)->getMedium();
10094 if (hd.isNull())
10095 continue;
10096
10097 if ((*it)->isImplicit())
10098 {
10099 /* deassociate and mark for deletion */
10100 LogFlowThisFunc(("Detaching '%s', pending deletion\n", (*it)->getLogName()));
10101 rc = hd->removeBackReference(mData->mUuid);
10102 AssertComRC(rc);
10103 implicitAtts.push_back(*it);
10104 continue;
10105 }
10106
10107 /* was this hard disk attached before? */
10108 if (!findAttachment(oldAtts, hd))
10109 {
10110 /* no: de-associate */
10111 LogFlowThisFunc(("Detaching '%s', no deletion\n", (*it)->getLogName()));
10112 rc = hd->removeBackReference(mData->mUuid);
10113 AssertComRC(rc);
10114 continue;
10115 }
10116 LogFlowThisFunc(("Not detaching '%s'\n", (*it)->getLogName()));
10117 }
10118
10119 /* rollback hard disk changes */
10120 mMediaData.rollback();
10121
10122 MultiResult mrc(S_OK);
10123
10124 /* delete unused implicit diffs */
10125 if (implicitAtts.size() != 0)
10126 {
10127 /* will release the lock before the potentially lengthy
10128 * operation, so protect with the special state (unless already
10129 * protected) */
10130 MachineState_T oldState = mData->mMachineState;
10131 if ( oldState != MachineState_Saving
10132 && oldState != MachineState_LiveSnapshotting
10133 && oldState != MachineState_RestoringSnapshot
10134 && oldState != MachineState_DeletingSnapshot
10135 && oldState != MachineState_DeletingSnapshotOnline
10136 && oldState != MachineState_DeletingSnapshotPaused
10137 )
10138 setMachineState(MachineState_SettingUp);
10139
10140 alock.release();
10141
10142 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
10143 it != implicitAtts.end();
10144 ++it)
10145 {
10146 LogFlowThisFunc(("Deleting '%s'\n", (*it)->getLogName()));
10147 ComObjPtr<Medium> hd = (*it)->getMedium();
10148
10149 rc = hd->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10150 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, (*it)->getLogName(), hd->getLocationFull().c_str() ));
10151 mrc = rc;
10152 }
10153
10154 alock.acquire();
10155
10156 if (mData->mMachineState == MachineState_SettingUp)
10157 setMachineState(oldState);
10158 }
10159
10160 return mrc;
10161}
10162
10163/**
10164 * Looks through the given list of media attachments for one with the given parameters
10165 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10166 * can be searched as well if needed.
10167 *
10168 * @param list
10169 * @param aControllerName
10170 * @param aControllerPort
10171 * @param aDevice
10172 * @return
10173 */
10174MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10175 IN_BSTR aControllerName,
10176 LONG aControllerPort,
10177 LONG aDevice)
10178{
10179 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10180 it != ll.end();
10181 ++it)
10182 {
10183 MediumAttachment *pAttach = *it;
10184 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
10185 return pAttach;
10186 }
10187
10188 return NULL;
10189}
10190
10191/**
10192 * Looks through the given list of media attachments for one with the given parameters
10193 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10194 * can be searched as well if needed.
10195 *
10196 * @param list
10197 * @param aControllerName
10198 * @param aControllerPort
10199 * @param aDevice
10200 * @return
10201 */
10202MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10203 ComObjPtr<Medium> pMedium)
10204{
10205 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10206 it != ll.end();
10207 ++it)
10208 {
10209 MediumAttachment *pAttach = *it;
10210 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10211 if (pMediumThis == pMedium)
10212 return pAttach;
10213 }
10214
10215 return NULL;
10216}
10217
10218/**
10219 * Looks through the given list of media attachments for one with the given parameters
10220 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10221 * can be searched as well if needed.
10222 *
10223 * @param list
10224 * @param aControllerName
10225 * @param aControllerPort
10226 * @param aDevice
10227 * @return
10228 */
10229MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10230 Guid &id)
10231{
10232 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10233 it != ll.end();
10234 ++it)
10235 {
10236 MediumAttachment *pAttach = *it;
10237 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10238 if (pMediumThis->getId() == id)
10239 return pAttach;
10240 }
10241
10242 return NULL;
10243}
10244
10245/**
10246 * Main implementation for Machine::DetachDevice. This also gets called
10247 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10248 *
10249 * @param pAttach Medium attachment to detach.
10250 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10251 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
10252 * @return
10253 */
10254HRESULT Machine::detachDevice(MediumAttachment *pAttach,
10255 AutoWriteLock &writeLock,
10256 Snapshot *pSnapshot)
10257{
10258 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
10259 DeviceType_T mediumType = pAttach->getType();
10260
10261 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
10262
10263 if (pAttach->isImplicit())
10264 {
10265 /* attempt to implicitly delete the implicitly created diff */
10266
10267 /// @todo move the implicit flag from MediumAttachment to Medium
10268 /// and forbid any hard disk operation when it is implicit. Or maybe
10269 /// a special media state for it to make it even more simple.
10270
10271 Assert(mMediaData.isBackedUp());
10272
10273 /* will release the lock before the potentially lengthy operation, so
10274 * protect with the special state */
10275 MachineState_T oldState = mData->mMachineState;
10276 setMachineState(MachineState_SettingUp);
10277
10278 writeLock.release();
10279
10280 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
10281 true /*aWait*/);
10282
10283 writeLock.acquire();
10284
10285 setMachineState(oldState);
10286
10287 if (FAILED(rc)) return rc;
10288 }
10289
10290 setModified(IsModified_Storage);
10291 mMediaData.backup();
10292 mMediaData->mAttachments.remove(pAttach);
10293
10294 if (!oldmedium.isNull())
10295 {
10296 // if this is from a snapshot, do not defer detachment to commitMedia()
10297 if (pSnapshot)
10298 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
10299 // else if non-hard disk media, do not defer detachment to commitMedia() either
10300 else if (mediumType != DeviceType_HardDisk)
10301 oldmedium->removeBackReference(mData->mUuid);
10302 }
10303
10304 return S_OK;
10305}
10306
10307/**
10308 * Goes thru all media of the given list and
10309 *
10310 * 1) calls detachDevice() on each of them for this machine and
10311 * 2) adds all Medium objects found in the process to the given list,
10312 * depending on cleanupMode.
10313 *
10314 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10315 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10316 * media to the list.
10317 *
10318 * This gets called from Machine::Unregister, both for the actual Machine and
10319 * the SnapshotMachine objects that might be found in the snapshots.
10320 *
10321 * Requires caller and locking. The machine lock must be passed in because it
10322 * will be passed on to detachDevice which needs it for temporary unlocking.
10323 *
10324 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
10325 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
10326 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
10327 * otherwise no media get added.
10328 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
10329 * @return
10330 */
10331HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
10332 Snapshot *pSnapshot,
10333 CleanupMode_T cleanupMode,
10334 MediaList &llMedia)
10335{
10336 Assert(isWriteLockOnCurrentThread());
10337
10338 HRESULT rc;
10339
10340 // make a temporary list because detachDevice invalidates iterators into
10341 // mMediaData->mAttachments
10342 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
10343
10344 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
10345 it != llAttachments2.end();
10346 ++it)
10347 {
10348 ComObjPtr<MediumAttachment> &pAttach = *it;
10349 ComObjPtr<Medium> pMedium = pAttach->getMedium();
10350
10351 if (!pMedium.isNull())
10352 {
10353 AutoCaller mac(pMedium);
10354 if (FAILED(mac.rc())) return mac.rc();
10355 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
10356 DeviceType_T devType = pMedium->getDeviceType();
10357 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
10358 && devType == DeviceType_HardDisk)
10359 || (cleanupMode == CleanupMode_Full)
10360 )
10361 {
10362 llMedia.push_back(pMedium);
10363 ComObjPtr<Medium> pParent = pMedium->getParent();
10364 /*
10365 * Search for medias which are not attached to any machine, but
10366 * in the chain to an attached disk. Mediums are only consided
10367 * if they are:
10368 * - have only one child
10369 * - no references to any machines
10370 * - are of normal medium type
10371 */
10372 while (!pParent.isNull())
10373 {
10374 AutoCaller mac1(pParent);
10375 if (FAILED(mac1.rc())) return mac1.rc();
10376 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
10377 if (pParent->getChildren().size() == 1)
10378 {
10379 if ( pParent->getMachineBackRefCount() == 0
10380 && pParent->getType() == MediumType_Normal
10381 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
10382 llMedia.push_back(pParent);
10383 }else
10384 break;
10385 pParent = pParent->getParent();
10386 }
10387 }
10388 }
10389
10390 // real machine: then we need to use the proper method
10391 rc = detachDevice(pAttach, writeLock, pSnapshot);
10392
10393 if (FAILED(rc))
10394 return rc;
10395 }
10396
10397 return S_OK;
10398}
10399
10400/**
10401 * Perform deferred hard disk detachments.
10402 *
10403 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10404 * backed up).
10405 *
10406 * If @a aOnline is @c true then this method will also unlock the old hard disks
10407 * for which the new implicit diffs were created and will lock these new diffs for
10408 * writing.
10409 *
10410 * @param aOnline Whether the VM was online prior to this operation.
10411 *
10412 * @note Locks this object for writing!
10413 */
10414void Machine::commitMedia(bool aOnline /*= false*/)
10415{
10416 AutoCaller autoCaller(this);
10417 AssertComRCReturnVoid(autoCaller.rc());
10418
10419 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10420
10421 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
10422
10423 HRESULT rc = S_OK;
10424
10425 /* no attach/detach operations -- nothing to do */
10426 if (!mMediaData.isBackedUp())
10427 return;
10428
10429 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10430 bool fMediaNeedsLocking = false;
10431
10432 /* enumerate new attachments */
10433 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10434 it != mMediaData->mAttachments.end();
10435 ++it)
10436 {
10437 MediumAttachment *pAttach = *it;
10438
10439 pAttach->commit();
10440
10441 Medium* pMedium = pAttach->getMedium();
10442 bool fImplicit = pAttach->isImplicit();
10443
10444 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
10445 (pMedium) ? pMedium->getName().c_str() : "NULL",
10446 fImplicit));
10447
10448 /** @todo convert all this Machine-based voodoo to MediumAttachment
10449 * based commit logic. */
10450 if (fImplicit)
10451 {
10452 /* convert implicit attachment to normal */
10453 pAttach->setImplicit(false);
10454
10455 if ( aOnline
10456 && pMedium
10457 && pAttach->getType() == DeviceType_HardDisk
10458 )
10459 {
10460 ComObjPtr<Medium> parent = pMedium->getParent();
10461 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
10462
10463 /* update the appropriate lock list */
10464 MediumLockList *pMediumLockList;
10465 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10466 AssertComRC(rc);
10467 if (pMediumLockList)
10468 {
10469 /* unlock if there's a need to change the locking */
10470 if (!fMediaNeedsLocking)
10471 {
10472 rc = mData->mSession.mLockedMedia.Unlock();
10473 AssertComRC(rc);
10474 fMediaNeedsLocking = true;
10475 }
10476 rc = pMediumLockList->Update(parent, false);
10477 AssertComRC(rc);
10478 rc = pMediumLockList->Append(pMedium, true);
10479 AssertComRC(rc);
10480 }
10481 }
10482
10483 continue;
10484 }
10485
10486 if (pMedium)
10487 {
10488 /* was this medium attached before? */
10489 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
10490 oldIt != oldAtts.end();
10491 ++oldIt)
10492 {
10493 MediumAttachment *pOldAttach = *oldIt;
10494 if (pOldAttach->getMedium() == pMedium)
10495 {
10496 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
10497
10498 /* yes: remove from old to avoid de-association */
10499 oldAtts.erase(oldIt);
10500 break;
10501 }
10502 }
10503 }
10504 }
10505
10506 /* enumerate remaining old attachments and de-associate from the
10507 * current machine state */
10508 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
10509 it != oldAtts.end();
10510 ++it)
10511 {
10512 MediumAttachment *pAttach = *it;
10513 Medium* pMedium = pAttach->getMedium();
10514
10515 /* Detach only hard disks, since DVD/floppy media is detached
10516 * instantly in MountMedium. */
10517 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
10518 {
10519 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
10520
10521 /* now de-associate from the current machine state */
10522 rc = pMedium->removeBackReference(mData->mUuid);
10523 AssertComRC(rc);
10524
10525 if (aOnline)
10526 {
10527 /* unlock since medium is not used anymore */
10528 MediumLockList *pMediumLockList;
10529 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10530 AssertComRC(rc);
10531 if (pMediumLockList)
10532 {
10533 rc = mData->mSession.mLockedMedia.Remove(pAttach);
10534 AssertComRC(rc);
10535 }
10536 }
10537 }
10538 }
10539
10540 /* take media locks again so that the locking state is consistent */
10541 if (fMediaNeedsLocking)
10542 {
10543 Assert(aOnline);
10544 rc = mData->mSession.mLockedMedia.Lock();
10545 AssertComRC(rc);
10546 }
10547
10548 /* commit the hard disk changes */
10549 mMediaData.commit();
10550
10551 if (isSessionMachine())
10552 {
10553 /*
10554 * Update the parent machine to point to the new owner.
10555 * This is necessary because the stored parent will point to the
10556 * session machine otherwise and cause crashes or errors later
10557 * when the session machine gets invalid.
10558 */
10559 /** @todo Change the MediumAttachment class to behave like any other
10560 * class in this regard by creating peer MediumAttachment
10561 * objects for session machines and share the data with the peer
10562 * machine.
10563 */
10564 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10565 it != mMediaData->mAttachments.end();
10566 ++it)
10567 {
10568 (*it)->updateParentMachine(mPeer);
10569 }
10570
10571 /* attach new data to the primary machine and reshare it */
10572 mPeer->mMediaData.attach(mMediaData);
10573 }
10574
10575 return;
10576}
10577
10578/**
10579 * Perform deferred deletion of implicitly created diffs.
10580 *
10581 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10582 * backed up).
10583 *
10584 * @note Locks this object for writing!
10585 */
10586void Machine::rollbackMedia()
10587{
10588 AutoCaller autoCaller(this);
10589 AssertComRCReturnVoid (autoCaller.rc());
10590
10591 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10592
10593 LogFlowThisFunc(("Entering\n"));
10594
10595 HRESULT rc = S_OK;
10596
10597 /* no attach/detach operations -- nothing to do */
10598 if (!mMediaData.isBackedUp())
10599 return;
10600
10601 /* enumerate new attachments */
10602 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10603 it != mMediaData->mAttachments.end();
10604 ++it)
10605 {
10606 MediumAttachment *pAttach = *it;
10607 /* Fix up the backrefs for DVD/floppy media. */
10608 if (pAttach->getType() != DeviceType_HardDisk)
10609 {
10610 Medium* pMedium = pAttach->getMedium();
10611 if (pMedium)
10612 {
10613 rc = pMedium->removeBackReference(mData->mUuid);
10614 AssertComRC(rc);
10615 }
10616 }
10617
10618 (*it)->rollback();
10619
10620 pAttach = *it;
10621 /* Fix up the backrefs for DVD/floppy media. */
10622 if (pAttach->getType() != DeviceType_HardDisk)
10623 {
10624 Medium* pMedium = pAttach->getMedium();
10625 if (pMedium)
10626 {
10627 rc = pMedium->addBackReference(mData->mUuid);
10628 AssertComRC(rc);
10629 }
10630 }
10631 }
10632
10633 /** @todo convert all this Machine-based voodoo to MediumAttachment
10634 * based rollback logic. */
10635 deleteImplicitDiffs();
10636
10637 return;
10638}
10639
10640/**
10641 * Returns true if the settings file is located in the directory named exactly
10642 * as the machine; this means, among other things, that the machine directory
10643 * should be auto-renamed.
10644 *
10645 * @param aSettingsDir if not NULL, the full machine settings file directory
10646 * name will be assigned there.
10647 *
10648 * @note Doesn't lock anything.
10649 * @note Not thread safe (must be called from this object's lock).
10650 */
10651bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
10652{
10653 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
10654 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
10655 if (aSettingsDir)
10656 *aSettingsDir = strMachineDirName;
10657 strMachineDirName.stripPath(); // vmname
10658 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
10659 strConfigFileOnly.stripPath() // vmname.vbox
10660 .stripExt(); // vmname
10661
10662 AssertReturn(!strMachineDirName.isEmpty(), false);
10663 AssertReturn(!strConfigFileOnly.isEmpty(), false);
10664
10665 return strMachineDirName == strConfigFileOnly;
10666}
10667
10668/**
10669 * Discards all changes to machine settings.
10670 *
10671 * @param aNotify Whether to notify the direct session about changes or not.
10672 *
10673 * @note Locks objects for writing!
10674 */
10675void Machine::rollback(bool aNotify)
10676{
10677 AutoCaller autoCaller(this);
10678 AssertComRCReturn(autoCaller.rc(), (void)0);
10679
10680 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10681
10682 if (!mStorageControllers.isNull())
10683 {
10684 if (mStorageControllers.isBackedUp())
10685 {
10686 /* unitialize all new devices (absent in the backed up list). */
10687 StorageControllerList::const_iterator it = mStorageControllers->begin();
10688 StorageControllerList *backedList = mStorageControllers.backedUpData();
10689 while (it != mStorageControllers->end())
10690 {
10691 if ( std::find(backedList->begin(), backedList->end(), *it)
10692 == backedList->end()
10693 )
10694 {
10695 (*it)->uninit();
10696 }
10697 ++it;
10698 }
10699
10700 /* restore the list */
10701 mStorageControllers.rollback();
10702 }
10703
10704 /* rollback any changes to devices after restoring the list */
10705 if (mData->flModifications & IsModified_Storage)
10706 {
10707 StorageControllerList::const_iterator it = mStorageControllers->begin();
10708 while (it != mStorageControllers->end())
10709 {
10710 (*it)->rollback();
10711 ++it;
10712 }
10713 }
10714 }
10715
10716 mUserData.rollback();
10717
10718 mHWData.rollback();
10719
10720 if (mData->flModifications & IsModified_Storage)
10721 rollbackMedia();
10722
10723 if (mBIOSSettings)
10724 mBIOSSettings->rollback();
10725
10726 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
10727 mVRDEServer->rollback();
10728
10729 if (mAudioAdapter)
10730 mAudioAdapter->rollback();
10731
10732 if (mUSBController && (mData->flModifications & IsModified_USB))
10733 mUSBController->rollback();
10734
10735 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
10736 mBandwidthControl->rollback();
10737
10738 if (!mHWData.isNull())
10739 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
10740 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
10741 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
10742 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
10743
10744 if (mData->flModifications & IsModified_NetworkAdapters)
10745 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
10746 if ( mNetworkAdapters[slot]
10747 && mNetworkAdapters[slot]->isModified())
10748 {
10749 mNetworkAdapters[slot]->rollback();
10750 networkAdapters[slot] = mNetworkAdapters[slot];
10751 }
10752
10753 if (mData->flModifications & IsModified_SerialPorts)
10754 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
10755 if ( mSerialPorts[slot]
10756 && mSerialPorts[slot]->isModified())
10757 {
10758 mSerialPorts[slot]->rollback();
10759 serialPorts[slot] = mSerialPorts[slot];
10760 }
10761
10762 if (mData->flModifications & IsModified_ParallelPorts)
10763 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
10764 if ( mParallelPorts[slot]
10765 && mParallelPorts[slot]->isModified())
10766 {
10767 mParallelPorts[slot]->rollback();
10768 parallelPorts[slot] = mParallelPorts[slot];
10769 }
10770
10771 if (aNotify)
10772 {
10773 /* inform the direct session about changes */
10774
10775 ComObjPtr<Machine> that = this;
10776 uint32_t flModifications = mData->flModifications;
10777 alock.release();
10778
10779 if (flModifications & IsModified_SharedFolders)
10780 that->onSharedFolderChange();
10781
10782 if (flModifications & IsModified_VRDEServer)
10783 that->onVRDEServerChange(/* aRestart */ TRUE);
10784 if (flModifications & IsModified_USB)
10785 that->onUSBControllerChange();
10786
10787 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
10788 if (networkAdapters[slot])
10789 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
10790 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
10791 if (serialPorts[slot])
10792 that->onSerialPortChange(serialPorts[slot]);
10793 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
10794 if (parallelPorts[slot])
10795 that->onParallelPortChange(parallelPorts[slot]);
10796
10797 if (flModifications & IsModified_Storage)
10798 that->onStorageControllerChange();
10799
10800#if 0
10801 if (flModifications & IsModified_BandwidthControl)
10802 that->onBandwidthControlChange();
10803#endif
10804 }
10805}
10806
10807/**
10808 * Commits all the changes to machine settings.
10809 *
10810 * Note that this operation is supposed to never fail.
10811 *
10812 * @note Locks this object and children for writing.
10813 */
10814void Machine::commit()
10815{
10816 AutoCaller autoCaller(this);
10817 AssertComRCReturnVoid(autoCaller.rc());
10818
10819 AutoCaller peerCaller(mPeer);
10820 AssertComRCReturnVoid(peerCaller.rc());
10821
10822 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
10823
10824 /*
10825 * use safe commit to ensure Snapshot machines (that share mUserData)
10826 * will still refer to a valid memory location
10827 */
10828 mUserData.commitCopy();
10829
10830 mHWData.commit();
10831
10832 if (mMediaData.isBackedUp())
10833 commitMedia();
10834
10835 mBIOSSettings->commit();
10836 mVRDEServer->commit();
10837 mAudioAdapter->commit();
10838 mUSBController->commit();
10839 mBandwidthControl->commit();
10840
10841 /* Keep the original network adapter count until this point, so that
10842 * discarding a chipset type change will not lose settings. */
10843 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
10844 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
10845 mNetworkAdapters[slot]->commit();
10846 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
10847 mSerialPorts[slot]->commit();
10848 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
10849 mParallelPorts[slot]->commit();
10850
10851 bool commitStorageControllers = false;
10852
10853 if (mStorageControllers.isBackedUp())
10854 {
10855 mStorageControllers.commit();
10856
10857 if (mPeer)
10858 {
10859 AutoWriteLock peerlock(mPeer COMMA_LOCKVAL_SRC_POS);
10860
10861 /* Commit all changes to new controllers (this will reshare data with
10862 * peers for those who have peers) */
10863 StorageControllerList *newList = new StorageControllerList();
10864 StorageControllerList::const_iterator it = mStorageControllers->begin();
10865 while (it != mStorageControllers->end())
10866 {
10867 (*it)->commit();
10868
10869 /* look if this controller has a peer device */
10870 ComObjPtr<StorageController> peer = (*it)->getPeer();
10871 if (!peer)
10872 {
10873 /* no peer means the device is a newly created one;
10874 * create a peer owning data this device share it with */
10875 peer.createObject();
10876 peer->init(mPeer, *it, true /* aReshare */);
10877 }
10878 else
10879 {
10880 /* remove peer from the old list */
10881 mPeer->mStorageControllers->remove(peer);
10882 }
10883 /* and add it to the new list */
10884 newList->push_back(peer);
10885
10886 ++it;
10887 }
10888
10889 /* uninit old peer's controllers that are left */
10890 it = mPeer->mStorageControllers->begin();
10891 while (it != mPeer->mStorageControllers->end())
10892 {
10893 (*it)->uninit();
10894 ++it;
10895 }
10896
10897 /* attach new list of controllers to our peer */
10898 mPeer->mStorageControllers.attach(newList);
10899 }
10900 else
10901 {
10902 /* we have no peer (our parent is the newly created machine);
10903 * just commit changes to devices */
10904 commitStorageControllers = true;
10905 }
10906 }
10907 else
10908 {
10909 /* the list of controllers itself is not changed,
10910 * just commit changes to controllers themselves */
10911 commitStorageControllers = true;
10912 }
10913
10914 if (commitStorageControllers)
10915 {
10916 StorageControllerList::const_iterator it = mStorageControllers->begin();
10917 while (it != mStorageControllers->end())
10918 {
10919 (*it)->commit();
10920 ++it;
10921 }
10922 }
10923
10924 if (isSessionMachine())
10925 {
10926 /* attach new data to the primary machine and reshare it */
10927 mPeer->mUserData.attach(mUserData);
10928 mPeer->mHWData.attach(mHWData);
10929 /* mMediaData is reshared by fixupMedia */
10930 // mPeer->mMediaData.attach(mMediaData);
10931 Assert(mPeer->mMediaData.data() == mMediaData.data());
10932 }
10933}
10934
10935/**
10936 * Copies all the hardware data from the given machine.
10937 *
10938 * Currently, only called when the VM is being restored from a snapshot. In
10939 * particular, this implies that the VM is not running during this method's
10940 * call.
10941 *
10942 * @note This method must be called from under this object's lock.
10943 *
10944 * @note This method doesn't call #commit(), so all data remains backed up and
10945 * unsaved.
10946 */
10947void Machine::copyFrom(Machine *aThat)
10948{
10949 AssertReturnVoid(!isSnapshotMachine());
10950 AssertReturnVoid(aThat->isSnapshotMachine());
10951
10952 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
10953
10954 mHWData.assignCopy(aThat->mHWData);
10955
10956 // create copies of all shared folders (mHWData after attaching a copy
10957 // contains just references to original objects)
10958 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
10959 it != mHWData->mSharedFolders.end();
10960 ++it)
10961 {
10962 ComObjPtr<SharedFolder> folder;
10963 folder.createObject();
10964 HRESULT rc = folder->initCopy(getMachine(), *it);
10965 AssertComRC(rc);
10966 *it = folder;
10967 }
10968
10969 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
10970 mVRDEServer->copyFrom(aThat->mVRDEServer);
10971 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
10972 mUSBController->copyFrom(aThat->mUSBController);
10973 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
10974
10975 /* create private copies of all controllers */
10976 mStorageControllers.backup();
10977 mStorageControllers->clear();
10978 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
10979 it != aThat->mStorageControllers->end();
10980 ++it)
10981 {
10982 ComObjPtr<StorageController> ctrl;
10983 ctrl.createObject();
10984 ctrl->initCopy(this, *it);
10985 mStorageControllers->push_back(ctrl);
10986 }
10987
10988 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
10989 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
10990 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
10991 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
10992 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
10993 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
10994}
10995
10996/**
10997 * Returns whether the given storage controller is hotplug capable.
10998 *
10999 * @returns true if the controller supports hotplugging
11000 * false otherwise.
11001 * @param enmCtrlType The controller type to check for.
11002 */
11003bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11004{
11005 switch (enmCtrlType)
11006 {
11007 case StorageControllerType_IntelAhci:
11008 return true;
11009 case StorageControllerType_LsiLogic:
11010 case StorageControllerType_LsiLogicSas:
11011 case StorageControllerType_BusLogic:
11012 case StorageControllerType_PIIX3:
11013 case StorageControllerType_PIIX4:
11014 case StorageControllerType_ICH6:
11015 case StorageControllerType_I82078:
11016 default:
11017 return false;
11018 }
11019}
11020
11021#ifdef VBOX_WITH_RESOURCE_USAGE_API
11022
11023void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11024{
11025 AssertReturnVoid(isWriteLockOnCurrentThread());
11026 AssertPtrReturnVoid(aCollector);
11027
11028 pm::CollectorHAL *hal = aCollector->getHAL();
11029 /* Create sub metrics */
11030 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11031 "Percentage of processor time spent in user mode by the VM process.");
11032 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11033 "Percentage of processor time spent in kernel mode by the VM process.");
11034 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11035 "Size of resident portion of VM process in memory.");
11036 /* Create and register base metrics */
11037 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11038 cpuLoadUser, cpuLoadKernel);
11039 aCollector->registerBaseMetric(cpuLoad);
11040 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11041 ramUsageUsed);
11042 aCollector->registerBaseMetric(ramUsage);
11043
11044 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11045 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11046 new pm::AggregateAvg()));
11047 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11048 new pm::AggregateMin()));
11049 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11050 new pm::AggregateMax()));
11051 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11052 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11053 new pm::AggregateAvg()));
11054 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11055 new pm::AggregateMin()));
11056 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11057 new pm::AggregateMax()));
11058
11059 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11060 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11061 new pm::AggregateAvg()));
11062 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11063 new pm::AggregateMin()));
11064 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11065 new pm::AggregateMax()));
11066
11067
11068 /* Guest metrics collector */
11069 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11070 aCollector->registerGuest(mCollectorGuest);
11071 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11072 this, __PRETTY_FUNCTION__, mCollectorGuest));
11073
11074 /* Create sub metrics */
11075 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11076 "Percentage of processor time spent in user mode as seen by the guest.");
11077 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11078 "Percentage of processor time spent in kernel mode as seen by the guest.");
11079 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11080 "Percentage of processor time spent idling as seen by the guest.");
11081
11082 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11083 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11084 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11085 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11086 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11087 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11088
11089 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11090
11091 /* Create and register base metrics */
11092 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11093 guestLoadUser, guestLoadKernel, guestLoadIdle);
11094 aCollector->registerBaseMetric(guestCpuLoad);
11095
11096 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11097 guestMemTotal, guestMemFree,
11098 guestMemBalloon, guestMemShared,
11099 guestMemCache, guestPagedTotal);
11100 aCollector->registerBaseMetric(guestCpuMem);
11101
11102 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11103 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11104 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11105 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11106
11107 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11108 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11109 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11110 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11111
11112 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11113 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11114 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11115 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11116
11117 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11118 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11119 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11120 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11121
11122 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11123 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11124 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11125 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11126
11127 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11128 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11129 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11130 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11131
11132 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11133 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11134 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11135 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11136
11137 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11138 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11139 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11140 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11141
11142 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11143 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11144 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11145 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11146}
11147
11148void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11149{
11150 AssertReturnVoid(isWriteLockOnCurrentThread());
11151
11152 if (aCollector)
11153 {
11154 aCollector->unregisterMetricsFor(aMachine);
11155 aCollector->unregisterBaseMetricsFor(aMachine);
11156 }
11157}
11158
11159#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11160
11161
11162////////////////////////////////////////////////////////////////////////////////
11163
11164DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11165
11166HRESULT SessionMachine::FinalConstruct()
11167{
11168 LogFlowThisFunc(("\n"));
11169
11170#if defined(RT_OS_WINDOWS)
11171 mIPCSem = NULL;
11172#elif defined(RT_OS_OS2)
11173 mIPCSem = NULLHANDLE;
11174#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11175 mIPCSem = -1;
11176#else
11177# error "Port me!"
11178#endif
11179
11180 return BaseFinalConstruct();
11181}
11182
11183void SessionMachine::FinalRelease()
11184{
11185 LogFlowThisFunc(("\n"));
11186
11187 uninit(Uninit::Unexpected);
11188
11189 BaseFinalRelease();
11190}
11191
11192/**
11193 * @note Must be called only by Machine::openSession() from its own write lock.
11194 */
11195HRESULT SessionMachine::init(Machine *aMachine)
11196{
11197 LogFlowThisFuncEnter();
11198 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
11199
11200 AssertReturn(aMachine, E_INVALIDARG);
11201
11202 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
11203
11204 /* Enclose the state transition NotReady->InInit->Ready */
11205 AutoInitSpan autoInitSpan(this);
11206 AssertReturn(autoInitSpan.isOk(), E_FAIL);
11207
11208 /* create the interprocess semaphore */
11209#if defined(RT_OS_WINDOWS)
11210 mIPCSemName = aMachine->mData->m_strConfigFileFull;
11211 for (size_t i = 0; i < mIPCSemName.length(); i++)
11212 if (mIPCSemName.raw()[i] == '\\')
11213 mIPCSemName.raw()[i] = '/';
11214 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
11215 ComAssertMsgRet(mIPCSem,
11216 ("Cannot create IPC mutex '%ls', err=%d",
11217 mIPCSemName.raw(), ::GetLastError()),
11218 E_FAIL);
11219#elif defined(RT_OS_OS2)
11220 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
11221 aMachine->mData->mUuid.raw());
11222 mIPCSemName = ipcSem;
11223 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
11224 ComAssertMsgRet(arc == NO_ERROR,
11225 ("Cannot create IPC mutex '%s', arc=%ld",
11226 ipcSem.c_str(), arc),
11227 E_FAIL);
11228#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11229# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11230# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
11231 /** @todo Check that this still works correctly. */
11232 AssertCompileSize(key_t, 8);
11233# else
11234 AssertCompileSize(key_t, 4);
11235# endif
11236 key_t key;
11237 mIPCSem = -1;
11238 mIPCKey = "0";
11239 for (uint32_t i = 0; i < 1 << 24; i++)
11240 {
11241 key = ((uint32_t)'V' << 24) | i;
11242 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
11243 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
11244 {
11245 mIPCSem = sem;
11246 if (sem >= 0)
11247 mIPCKey = BstrFmt("%u", key);
11248 break;
11249 }
11250 }
11251# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11252 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
11253 char *pszSemName = NULL;
11254 RTStrUtf8ToCurrentCP(&pszSemName, semName);
11255 key_t key = ::ftok(pszSemName, 'V');
11256 RTStrFree(pszSemName);
11257
11258 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
11259# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11260
11261 int errnoSave = errno;
11262 if (mIPCSem < 0 && errnoSave == ENOSYS)
11263 {
11264 setError(E_FAIL,
11265 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
11266 "support for SysV IPC. Check the host kernel configuration for "
11267 "CONFIG_SYSVIPC=y"));
11268 return E_FAIL;
11269 }
11270 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
11271 * the IPC semaphores */
11272 if (mIPCSem < 0 && errnoSave == ENOSPC)
11273 {
11274#ifdef RT_OS_LINUX
11275 setError(E_FAIL,
11276 tr("Cannot create IPC semaphore because the system limit for the "
11277 "maximum number of semaphore sets (SEMMNI), or the system wide "
11278 "maximum number of semaphores (SEMMNS) would be exceeded. The "
11279 "current set of SysV IPC semaphores can be determined from "
11280 "the file /proc/sysvipc/sem"));
11281#else
11282 setError(E_FAIL,
11283 tr("Cannot create IPC semaphore because the system-imposed limit "
11284 "on the maximum number of allowed semaphores or semaphore "
11285 "identifiers system-wide would be exceeded"));
11286#endif
11287 return E_FAIL;
11288 }
11289 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
11290 E_FAIL);
11291 /* set the initial value to 1 */
11292 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
11293 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
11294 E_FAIL);
11295#else
11296# error "Port me!"
11297#endif
11298
11299 /* memorize the peer Machine */
11300 unconst(mPeer) = aMachine;
11301 /* share the parent pointer */
11302 unconst(mParent) = aMachine->mParent;
11303
11304 /* take the pointers to data to share */
11305 mData.share(aMachine->mData);
11306 mSSData.share(aMachine->mSSData);
11307
11308 mUserData.share(aMachine->mUserData);
11309 mHWData.share(aMachine->mHWData);
11310 mMediaData.share(aMachine->mMediaData);
11311
11312 mStorageControllers.allocate();
11313 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
11314 it != aMachine->mStorageControllers->end();
11315 ++it)
11316 {
11317 ComObjPtr<StorageController> ctl;
11318 ctl.createObject();
11319 ctl->init(this, *it);
11320 mStorageControllers->push_back(ctl);
11321 }
11322
11323 unconst(mBIOSSettings).createObject();
11324 mBIOSSettings->init(this, aMachine->mBIOSSettings);
11325 /* create another VRDEServer object that will be mutable */
11326 unconst(mVRDEServer).createObject();
11327 mVRDEServer->init(this, aMachine->mVRDEServer);
11328 /* create another audio adapter object that will be mutable */
11329 unconst(mAudioAdapter).createObject();
11330 mAudioAdapter->init(this, aMachine->mAudioAdapter);
11331 /* create a list of serial ports that will be mutable */
11332 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11333 {
11334 unconst(mSerialPorts[slot]).createObject();
11335 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
11336 }
11337 /* create a list of parallel ports that will be mutable */
11338 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11339 {
11340 unconst(mParallelPorts[slot]).createObject();
11341 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
11342 }
11343 /* create another USB controller object that will be mutable */
11344 unconst(mUSBController).createObject();
11345 mUSBController->init(this, aMachine->mUSBController);
11346
11347 /* create a list of network adapters that will be mutable */
11348 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
11349 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11350 {
11351 unconst(mNetworkAdapters[slot]).createObject();
11352 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
11353 }
11354
11355 /* create another bandwidth control object that will be mutable */
11356 unconst(mBandwidthControl).createObject();
11357 mBandwidthControl->init(this, aMachine->mBandwidthControl);
11358
11359 /* default is to delete saved state on Saved -> PoweredOff transition */
11360 mRemoveSavedState = true;
11361
11362 /* Confirm a successful initialization when it's the case */
11363 autoInitSpan.setSucceeded();
11364
11365 LogFlowThisFuncLeave();
11366 return S_OK;
11367}
11368
11369/**
11370 * Uninitializes this session object. If the reason is other than
11371 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
11372 *
11373 * @param aReason uninitialization reason
11374 *
11375 * @note Locks mParent + this object for writing.
11376 */
11377void SessionMachine::uninit(Uninit::Reason aReason)
11378{
11379 LogFlowThisFuncEnter();
11380 LogFlowThisFunc(("reason=%d\n", aReason));
11381
11382 /*
11383 * Strongly reference ourselves to prevent this object deletion after
11384 * mData->mSession.mMachine.setNull() below (which can release the last
11385 * reference and call the destructor). Important: this must be done before
11386 * accessing any members (and before AutoUninitSpan that does it as well).
11387 * This self reference will be released as the very last step on return.
11388 */
11389 ComObjPtr<SessionMachine> selfRef = this;
11390
11391 /* Enclose the state transition Ready->InUninit->NotReady */
11392 AutoUninitSpan autoUninitSpan(this);
11393 if (autoUninitSpan.uninitDone())
11394 {
11395 LogFlowThisFunc(("Already uninitialized\n"));
11396 LogFlowThisFuncLeave();
11397 return;
11398 }
11399
11400 if (autoUninitSpan.initFailed())
11401 {
11402 /* We've been called by init() because it's failed. It's not really
11403 * necessary (nor it's safe) to perform the regular uninit sequence
11404 * below, the following is enough.
11405 */
11406 LogFlowThisFunc(("Initialization failed.\n"));
11407#if defined(RT_OS_WINDOWS)
11408 if (mIPCSem)
11409 ::CloseHandle(mIPCSem);
11410 mIPCSem = NULL;
11411#elif defined(RT_OS_OS2)
11412 if (mIPCSem != NULLHANDLE)
11413 ::DosCloseMutexSem(mIPCSem);
11414 mIPCSem = NULLHANDLE;
11415#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11416 if (mIPCSem >= 0)
11417 ::semctl(mIPCSem, 0, IPC_RMID);
11418 mIPCSem = -1;
11419# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11420 mIPCKey = "0";
11421# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
11422#else
11423# error "Port me!"
11424#endif
11425 uninitDataAndChildObjects();
11426 mData.free();
11427 unconst(mParent) = NULL;
11428 unconst(mPeer) = NULL;
11429 LogFlowThisFuncLeave();
11430 return;
11431 }
11432
11433 MachineState_T lastState;
11434 {
11435 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
11436 lastState = mData->mMachineState;
11437 }
11438 NOREF(lastState);
11439
11440#ifdef VBOX_WITH_USB
11441 // release all captured USB devices, but do this before requesting the locks below
11442 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
11443 {
11444 /* Console::captureUSBDevices() is called in the VM process only after
11445 * setting the machine state to Starting or Restoring.
11446 * Console::detachAllUSBDevices() will be called upon successful
11447 * termination. So, we need to release USB devices only if there was
11448 * an abnormal termination of a running VM.
11449 *
11450 * This is identical to SessionMachine::DetachAllUSBDevices except
11451 * for the aAbnormal argument. */
11452 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
11453 AssertComRC(rc);
11454 NOREF(rc);
11455
11456 USBProxyService *service = mParent->host()->usbProxyService();
11457 if (service)
11458 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
11459 }
11460#endif /* VBOX_WITH_USB */
11461
11462 // we need to lock this object in uninit() because the lock is shared
11463 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
11464 // and others need mParent lock, and USB needs host lock.
11465 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
11466
11467#if 0
11468 // Trigger async cleanup tasks, avoid doing things here which are not
11469 // vital to be done immediately and maybe need more locks. This calls
11470 // Machine::unregisterMetrics().
11471 mParent->onMachineUninit(mPeer);
11472#else
11473 /*
11474 * It is safe to call Machine::unregisterMetrics() here because
11475 * PerformanceCollector::samplerCallback no longer accesses guest methods
11476 * holding the lock.
11477 */
11478 unregisterMetrics(mParent->performanceCollector(), mPeer);
11479#endif
11480 /* The guest must be unregistered after its metrics (@bugref{5949}). */
11481 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11482 this, __PRETTY_FUNCTION__, mCollectorGuest));
11483 if (mCollectorGuest)
11484 {
11485 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
11486 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
11487 mCollectorGuest = NULL;
11488 }
11489
11490 if (aReason == Uninit::Abnormal)
11491 {
11492 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
11493 Global::IsOnlineOrTransient(lastState)));
11494
11495 /* reset the state to Aborted */
11496 if (mData->mMachineState != MachineState_Aborted)
11497 setMachineState(MachineState_Aborted);
11498 }
11499
11500 // any machine settings modified?
11501 if (mData->flModifications)
11502 {
11503 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
11504 rollback(false /* aNotify */);
11505 }
11506
11507 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
11508 || !mConsoleTaskData.mSnapshot);
11509 if (!mConsoleTaskData.strStateFilePath.isEmpty())
11510 {
11511 LogWarningThisFunc(("canceling failed save state request!\n"));
11512 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
11513 }
11514 else if (!mConsoleTaskData.mSnapshot.isNull())
11515 {
11516 LogWarningThisFunc(("canceling untaken snapshot!\n"));
11517
11518 /* delete all differencing hard disks created (this will also attach
11519 * their parents back by rolling back mMediaData) */
11520 rollbackMedia();
11521
11522 // delete the saved state file (it might have been already created)
11523 // AFTER killing the snapshot so that releaseSavedStateFile() won't
11524 // think it's still in use
11525 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
11526 mConsoleTaskData.mSnapshot->uninit();
11527 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
11528 }
11529
11530 if (!mData->mSession.mType.isEmpty())
11531 {
11532 /* mType is not null when this machine's process has been started by
11533 * Machine::LaunchVMProcess(), therefore it is our child. We
11534 * need to queue the PID to reap the process (and avoid zombies on
11535 * Linux). */
11536 Assert(mData->mSession.mPid != NIL_RTPROCESS);
11537 mParent->addProcessToReap(mData->mSession.mPid);
11538 }
11539
11540 mData->mSession.mPid = NIL_RTPROCESS;
11541
11542 if (aReason == Uninit::Unexpected)
11543 {
11544 /* Uninitialization didn't come from #checkForDeath(), so tell the
11545 * client watcher thread to update the set of machines that have open
11546 * sessions. */
11547 mParent->updateClientWatcher();
11548 }
11549
11550 /* uninitialize all remote controls */
11551 if (mData->mSession.mRemoteControls.size())
11552 {
11553 LogFlowThisFunc(("Closing remote sessions (%d):\n",
11554 mData->mSession.mRemoteControls.size()));
11555
11556 Data::Session::RemoteControlList::iterator it =
11557 mData->mSession.mRemoteControls.begin();
11558 while (it != mData->mSession.mRemoteControls.end())
11559 {
11560 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
11561 HRESULT rc = (*it)->Uninitialize();
11562 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
11563 if (FAILED(rc))
11564 LogWarningThisFunc(("Forgot to close the remote session?\n"));
11565 ++it;
11566 }
11567 mData->mSession.mRemoteControls.clear();
11568 }
11569
11570 /*
11571 * An expected uninitialization can come only from #checkForDeath().
11572 * Otherwise it means that something's gone really wrong (for example,
11573 * the Session implementation has released the VirtualBox reference
11574 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
11575 * etc). However, it's also possible, that the client releases the IPC
11576 * semaphore correctly (i.e. before it releases the VirtualBox reference),
11577 * but the VirtualBox release event comes first to the server process.
11578 * This case is practically possible, so we should not assert on an
11579 * unexpected uninit, just log a warning.
11580 */
11581
11582 if ((aReason == Uninit::Unexpected))
11583 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
11584
11585 if (aReason != Uninit::Normal)
11586 {
11587 mData->mSession.mDirectControl.setNull();
11588 }
11589 else
11590 {
11591 /* this must be null here (see #OnSessionEnd()) */
11592 Assert(mData->mSession.mDirectControl.isNull());
11593 Assert(mData->mSession.mState == SessionState_Unlocking);
11594 Assert(!mData->mSession.mProgress.isNull());
11595 }
11596 if (mData->mSession.mProgress)
11597 {
11598 if (aReason == Uninit::Normal)
11599 mData->mSession.mProgress->notifyComplete(S_OK);
11600 else
11601 mData->mSession.mProgress->notifyComplete(E_FAIL,
11602 COM_IIDOF(ISession),
11603 getComponentName(),
11604 tr("The VM session was aborted"));
11605 mData->mSession.mProgress.setNull();
11606 }
11607
11608 /* remove the association between the peer machine and this session machine */
11609 Assert( (SessionMachine*)mData->mSession.mMachine == this
11610 || aReason == Uninit::Unexpected);
11611
11612 /* reset the rest of session data */
11613 mData->mSession.mMachine.setNull();
11614 mData->mSession.mState = SessionState_Unlocked;
11615 mData->mSession.mType.setNull();
11616
11617 /* close the interprocess semaphore before leaving the exclusive lock */
11618#if defined(RT_OS_WINDOWS)
11619 if (mIPCSem)
11620 ::CloseHandle(mIPCSem);
11621 mIPCSem = NULL;
11622#elif defined(RT_OS_OS2)
11623 if (mIPCSem != NULLHANDLE)
11624 ::DosCloseMutexSem(mIPCSem);
11625 mIPCSem = NULLHANDLE;
11626#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11627 if (mIPCSem >= 0)
11628 ::semctl(mIPCSem, 0, IPC_RMID);
11629 mIPCSem = -1;
11630# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11631 mIPCKey = "0";
11632# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
11633#else
11634# error "Port me!"
11635#endif
11636
11637 /* fire an event */
11638 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
11639
11640 uninitDataAndChildObjects();
11641
11642 /* free the essential data structure last */
11643 mData.free();
11644
11645 /* release the exclusive lock before setting the below two to NULL */
11646 multilock.release();
11647
11648 unconst(mParent) = NULL;
11649 unconst(mPeer) = NULL;
11650
11651 LogFlowThisFuncLeave();
11652}
11653
11654// util::Lockable interface
11655////////////////////////////////////////////////////////////////////////////////
11656
11657/**
11658 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
11659 * with the primary Machine instance (mPeer).
11660 */
11661RWLockHandle *SessionMachine::lockHandle() const
11662{
11663 AssertReturn(mPeer != NULL, NULL);
11664 return mPeer->lockHandle();
11665}
11666
11667// IInternalMachineControl methods
11668////////////////////////////////////////////////////////////////////////////////
11669
11670/**
11671 * Passes collected guest statistics to performance collector object
11672 */
11673STDMETHODIMP SessionMachine::ReportGuestStatistics(ULONG aValidStats, ULONG aCpuUser,
11674 ULONG aCpuKernel, ULONG aCpuIdle,
11675 ULONG aMemTotal, ULONG aMemFree,
11676 ULONG aMemBalloon, ULONG aMemShared,
11677 ULONG aMemCache, ULONG aPageTotal,
11678 ULONG aAllocVMM, ULONG aFreeVMM,
11679 ULONG aBalloonedVMM, ULONG aSharedVMM)
11680{
11681 if (mCollectorGuest)
11682 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
11683 aMemTotal, aMemFree, aMemBalloon, aMemShared,
11684 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
11685 aBalloonedVMM, aSharedVMM);
11686
11687 return S_OK;
11688}
11689
11690/**
11691 * @note Locks this object for writing.
11692 */
11693STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
11694{
11695 AutoCaller autoCaller(this);
11696 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11697
11698 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11699
11700 mRemoveSavedState = aRemove;
11701
11702 return S_OK;
11703}
11704
11705/**
11706 * @note Locks the same as #setMachineState() does.
11707 */
11708STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
11709{
11710 return setMachineState(aMachineState);
11711}
11712
11713/**
11714 * @note Locks this object for reading.
11715 */
11716STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
11717{
11718 AutoCaller autoCaller(this);
11719 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11720
11721 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11722
11723#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
11724 mIPCSemName.cloneTo(aId);
11725 return S_OK;
11726#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11727# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11728 mIPCKey.cloneTo(aId);
11729# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11730 mData->m_strConfigFileFull.cloneTo(aId);
11731# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11732 return S_OK;
11733#else
11734# error "Port me!"
11735#endif
11736}
11737
11738/**
11739 * @note Locks this object for writing.
11740 */
11741STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
11742{
11743 LogFlowThisFunc(("aProgress=%p\n", aProgress));
11744 AutoCaller autoCaller(this);
11745 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11746
11747 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11748
11749 if (mData->mSession.mState != SessionState_Locked)
11750 return VBOX_E_INVALID_OBJECT_STATE;
11751
11752 if (!mData->mSession.mProgress.isNull())
11753 mData->mSession.mProgress->setOtherProgressObject(aProgress);
11754
11755 LogFlowThisFunc(("returns S_OK.\n"));
11756 return S_OK;
11757}
11758
11759/**
11760 * @note Locks this object for writing.
11761 */
11762STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
11763{
11764 AutoCaller autoCaller(this);
11765 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11766
11767 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11768
11769 if (mData->mSession.mState != SessionState_Locked)
11770 return VBOX_E_INVALID_OBJECT_STATE;
11771
11772 /* Finalize the LaunchVMProcess progress object. */
11773 if (mData->mSession.mProgress)
11774 {
11775 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
11776 mData->mSession.mProgress.setNull();
11777 }
11778
11779 if (SUCCEEDED((HRESULT)iResult))
11780 {
11781#ifdef VBOX_WITH_RESOURCE_USAGE_API
11782 /* The VM has been powered up successfully, so it makes sense
11783 * now to offer the performance metrics for a running machine
11784 * object. Doing it earlier wouldn't be safe. */
11785 registerMetrics(mParent->performanceCollector(), mPeer,
11786 mData->mSession.mPid);
11787#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11788 }
11789
11790 return S_OK;
11791}
11792
11793/**
11794 * @note Locks this object for writing.
11795 */
11796STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
11797{
11798 LogFlowThisFuncEnter();
11799
11800 CheckComArgOutPointerValid(aProgress);
11801
11802 AutoCaller autoCaller(this);
11803 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11804
11805 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11806
11807 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
11808 E_FAIL);
11809
11810 /* create a progress object to track operation completion */
11811 ComObjPtr<Progress> pProgress;
11812 pProgress.createObject();
11813 pProgress->init(getVirtualBox(),
11814 static_cast<IMachine *>(this) /* aInitiator */,
11815 Bstr(tr("Stopping the virtual machine")).raw(),
11816 FALSE /* aCancelable */);
11817
11818 /* fill in the console task data */
11819 mConsoleTaskData.mLastState = mData->mMachineState;
11820 mConsoleTaskData.mProgress = pProgress;
11821
11822 /* set the state to Stopping (this is expected by Console::PowerDown()) */
11823 setMachineState(MachineState_Stopping);
11824
11825 pProgress.queryInterfaceTo(aProgress);
11826
11827 return S_OK;
11828}
11829
11830/**
11831 * @note Locks this object for writing.
11832 */
11833STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
11834{
11835 LogFlowThisFuncEnter();
11836
11837 AutoCaller autoCaller(this);
11838 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11839
11840 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11841
11842 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
11843 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
11844 && mConsoleTaskData.mLastState != MachineState_Null,
11845 E_FAIL);
11846
11847 /*
11848 * On failure, set the state to the state we had when BeginPoweringDown()
11849 * was called (this is expected by Console::PowerDown() and the associated
11850 * task). On success the VM process already changed the state to
11851 * MachineState_PoweredOff, so no need to do anything.
11852 */
11853 if (FAILED(iResult))
11854 setMachineState(mConsoleTaskData.mLastState);
11855
11856 /* notify the progress object about operation completion */
11857 Assert(mConsoleTaskData.mProgress);
11858 if (SUCCEEDED(iResult))
11859 mConsoleTaskData.mProgress->notifyComplete(S_OK);
11860 else
11861 {
11862 Utf8Str strErrMsg(aErrMsg);
11863 if (strErrMsg.length())
11864 mConsoleTaskData.mProgress->notifyComplete(iResult,
11865 COM_IIDOF(ISession),
11866 getComponentName(),
11867 strErrMsg.c_str());
11868 else
11869 mConsoleTaskData.mProgress->notifyComplete(iResult);
11870 }
11871
11872 /* clear out the temporary saved state data */
11873 mConsoleTaskData.mLastState = MachineState_Null;
11874 mConsoleTaskData.mProgress.setNull();
11875
11876 LogFlowThisFuncLeave();
11877 return S_OK;
11878}
11879
11880
11881/**
11882 * Goes through the USB filters of the given machine to see if the given
11883 * device matches any filter or not.
11884 *
11885 * @note Locks the same as USBController::hasMatchingFilter() does.
11886 */
11887STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
11888 BOOL *aMatched,
11889 ULONG *aMaskedIfs)
11890{
11891 LogFlowThisFunc(("\n"));
11892
11893 CheckComArgNotNull(aUSBDevice);
11894 CheckComArgOutPointerValid(aMatched);
11895
11896 AutoCaller autoCaller(this);
11897 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11898
11899#ifdef VBOX_WITH_USB
11900 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
11901#else
11902 NOREF(aUSBDevice);
11903 NOREF(aMaskedIfs);
11904 *aMatched = FALSE;
11905#endif
11906
11907 return S_OK;
11908}
11909
11910/**
11911 * @note Locks the same as Host::captureUSBDevice() does.
11912 */
11913STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
11914{
11915 LogFlowThisFunc(("\n"));
11916
11917 AutoCaller autoCaller(this);
11918 AssertComRCReturnRC(autoCaller.rc());
11919
11920#ifdef VBOX_WITH_USB
11921 /* if captureDeviceForVM() fails, it must have set extended error info */
11922 clearError();
11923 MultiResult rc = mParent->host()->checkUSBProxyService();
11924 if (FAILED(rc)) return rc;
11925
11926 USBProxyService *service = mParent->host()->usbProxyService();
11927 AssertReturn(service, E_FAIL);
11928 return service->captureDeviceForVM(this, Guid(aId).ref());
11929#else
11930 NOREF(aId);
11931 return E_NOTIMPL;
11932#endif
11933}
11934
11935/**
11936 * @note Locks the same as Host::detachUSBDevice() does.
11937 */
11938STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
11939{
11940 LogFlowThisFunc(("\n"));
11941
11942 AutoCaller autoCaller(this);
11943 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11944
11945#ifdef VBOX_WITH_USB
11946 USBProxyService *service = mParent->host()->usbProxyService();
11947 AssertReturn(service, E_FAIL);
11948 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
11949#else
11950 NOREF(aId);
11951 NOREF(aDone);
11952 return E_NOTIMPL;
11953#endif
11954}
11955
11956/**
11957 * Inserts all machine filters to the USB proxy service and then calls
11958 * Host::autoCaptureUSBDevices().
11959 *
11960 * Called by Console from the VM process upon VM startup.
11961 *
11962 * @note Locks what called methods lock.
11963 */
11964STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
11965{
11966 LogFlowThisFunc(("\n"));
11967
11968 AutoCaller autoCaller(this);
11969 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11970
11971#ifdef VBOX_WITH_USB
11972 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
11973 AssertComRC(rc);
11974 NOREF(rc);
11975
11976 USBProxyService *service = mParent->host()->usbProxyService();
11977 AssertReturn(service, E_FAIL);
11978 return service->autoCaptureDevicesForVM(this);
11979#else
11980 return S_OK;
11981#endif
11982}
11983
11984/**
11985 * Removes all machine filters from the USB proxy service and then calls
11986 * Host::detachAllUSBDevices().
11987 *
11988 * Called by Console from the VM process upon normal VM termination or by
11989 * SessionMachine::uninit() upon abnormal VM termination (from under the
11990 * Machine/SessionMachine lock).
11991 *
11992 * @note Locks what called methods lock.
11993 */
11994STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
11995{
11996 LogFlowThisFunc(("\n"));
11997
11998 AutoCaller autoCaller(this);
11999 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12000
12001#ifdef VBOX_WITH_USB
12002 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12003 AssertComRC(rc);
12004 NOREF(rc);
12005
12006 USBProxyService *service = mParent->host()->usbProxyService();
12007 AssertReturn(service, E_FAIL);
12008 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12009#else
12010 NOREF(aDone);
12011 return S_OK;
12012#endif
12013}
12014
12015/**
12016 * @note Locks this object for writing.
12017 */
12018STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12019 IProgress **aProgress)
12020{
12021 LogFlowThisFuncEnter();
12022
12023 AssertReturn(aSession, E_INVALIDARG);
12024 AssertReturn(aProgress, E_INVALIDARG);
12025
12026 AutoCaller autoCaller(this);
12027
12028 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
12029 /*
12030 * We don't assert below because it might happen that a non-direct session
12031 * informs us it is closed right after we've been uninitialized -- it's ok.
12032 */
12033 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12034
12035 /* get IInternalSessionControl interface */
12036 ComPtr<IInternalSessionControl> control(aSession);
12037
12038 ComAssertRet(!control.isNull(), E_INVALIDARG);
12039
12040 /* Creating a Progress object requires the VirtualBox lock, and
12041 * thus locking it here is required by the lock order rules. */
12042 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12043
12044 if (control == mData->mSession.mDirectControl)
12045 {
12046 ComAssertRet(aProgress, E_POINTER);
12047
12048 /* The direct session is being normally closed by the client process
12049 * ----------------------------------------------------------------- */
12050
12051 /* go to the closing state (essential for all open*Session() calls and
12052 * for #checkForDeath()) */
12053 Assert(mData->mSession.mState == SessionState_Locked);
12054 mData->mSession.mState = SessionState_Unlocking;
12055
12056 /* set direct control to NULL to release the remote instance */
12057 mData->mSession.mDirectControl.setNull();
12058 LogFlowThisFunc(("Direct control is set to NULL\n"));
12059
12060 if (mData->mSession.mProgress)
12061 {
12062 /* finalize the progress, someone might wait if a frontend
12063 * closes the session before powering on the VM. */
12064 mData->mSession.mProgress->notifyComplete(E_FAIL,
12065 COM_IIDOF(ISession),
12066 getComponentName(),
12067 tr("The VM session was closed before any attempt to power it on"));
12068 mData->mSession.mProgress.setNull();
12069 }
12070
12071 /* Create the progress object the client will use to wait until
12072 * #checkForDeath() is called to uninitialize this session object after
12073 * it releases the IPC semaphore.
12074 * Note! Because we're "reusing" mProgress here, this must be a proxy
12075 * object just like for LaunchVMProcess. */
12076 Assert(mData->mSession.mProgress.isNull());
12077 ComObjPtr<ProgressProxy> progress;
12078 progress.createObject();
12079 ComPtr<IUnknown> pPeer(mPeer);
12080 progress->init(mParent, pPeer,
12081 Bstr(tr("Closing session")).raw(),
12082 FALSE /* aCancelable */);
12083 progress.queryInterfaceTo(aProgress);
12084 mData->mSession.mProgress = progress;
12085 }
12086 else
12087 {
12088 /* the remote session is being normally closed */
12089 Data::Session::RemoteControlList::iterator it =
12090 mData->mSession.mRemoteControls.begin();
12091 while (it != mData->mSession.mRemoteControls.end())
12092 {
12093 if (control == *it)
12094 break;
12095 ++it;
12096 }
12097 BOOL found = it != mData->mSession.mRemoteControls.end();
12098 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12099 E_INVALIDARG);
12100 // This MUST be erase(it), not remove(*it) as the latter triggers a
12101 // very nasty use after free due to the place where the value "lives".
12102 mData->mSession.mRemoteControls.erase(it);
12103 }
12104
12105 LogFlowThisFuncLeave();
12106 return S_OK;
12107}
12108
12109/**
12110 * @note Locks this object for writing.
12111 */
12112STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12113{
12114 LogFlowThisFuncEnter();
12115
12116 CheckComArgOutPointerValid(aProgress);
12117 CheckComArgOutPointerValid(aStateFilePath);
12118
12119 AutoCaller autoCaller(this);
12120 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12121
12122 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12123
12124 AssertReturn( mData->mMachineState == MachineState_Paused
12125 && mConsoleTaskData.mLastState == MachineState_Null
12126 && mConsoleTaskData.strStateFilePath.isEmpty(),
12127 E_FAIL);
12128
12129 /* create a progress object to track operation completion */
12130 ComObjPtr<Progress> pProgress;
12131 pProgress.createObject();
12132 pProgress->init(getVirtualBox(),
12133 static_cast<IMachine *>(this) /* aInitiator */,
12134 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12135 FALSE /* aCancelable */);
12136
12137 Utf8Str strStateFilePath;
12138 /* stateFilePath is null when the machine is not running */
12139 if (mData->mMachineState == MachineState_Paused)
12140 composeSavedStateFilename(strStateFilePath);
12141
12142 /* fill in the console task data */
12143 mConsoleTaskData.mLastState = mData->mMachineState;
12144 mConsoleTaskData.strStateFilePath = strStateFilePath;
12145 mConsoleTaskData.mProgress = pProgress;
12146
12147 /* set the state to Saving (this is expected by Console::SaveState()) */
12148 setMachineState(MachineState_Saving);
12149
12150 strStateFilePath.cloneTo(aStateFilePath);
12151 pProgress.queryInterfaceTo(aProgress);
12152
12153 return S_OK;
12154}
12155
12156/**
12157 * @note Locks mParent + this object for writing.
12158 */
12159STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12160{
12161 LogFlowThisFunc(("\n"));
12162
12163 AutoCaller autoCaller(this);
12164 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12165
12166 /* endSavingState() need mParent lock */
12167 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12168
12169 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12170 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12171 && mConsoleTaskData.mLastState != MachineState_Null
12172 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12173 E_FAIL);
12174
12175 /*
12176 * On failure, set the state to the state we had when BeginSavingState()
12177 * was called (this is expected by Console::SaveState() and the associated
12178 * task). On success the VM process already changed the state to
12179 * MachineState_Saved, so no need to do anything.
12180 */
12181 if (FAILED(iResult))
12182 setMachineState(mConsoleTaskData.mLastState);
12183
12184 return endSavingState(iResult, aErrMsg);
12185}
12186
12187/**
12188 * @note Locks this object for writing.
12189 */
12190STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12191{
12192 LogFlowThisFunc(("\n"));
12193
12194 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12195
12196 AutoCaller autoCaller(this);
12197 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12198
12199 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12200
12201 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12202 || mData->mMachineState == MachineState_Teleported
12203 || mData->mMachineState == MachineState_Aborted
12204 , E_FAIL); /** @todo setError. */
12205
12206 Utf8Str stateFilePathFull = aSavedStateFile;
12207 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
12208 if (RT_FAILURE(vrc))
12209 return setError(VBOX_E_FILE_ERROR,
12210 tr("Invalid saved state file path '%ls' (%Rrc)"),
12211 aSavedStateFile,
12212 vrc);
12213
12214 mSSData->strStateFilePath = stateFilePathFull;
12215
12216 /* The below setMachineState() will detect the state transition and will
12217 * update the settings file */
12218
12219 return setMachineState(MachineState_Saved);
12220}
12221
12222STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
12223 ComSafeArrayOut(BSTR, aValues),
12224 ComSafeArrayOut(LONG64, aTimestamps),
12225 ComSafeArrayOut(BSTR, aFlags))
12226{
12227 LogFlowThisFunc(("\n"));
12228
12229#ifdef VBOX_WITH_GUEST_PROPS
12230 using namespace guestProp;
12231
12232 AutoCaller autoCaller(this);
12233 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12234
12235 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12236
12237 AssertReturn(!ComSafeArrayOutIsNull(aNames), E_POINTER);
12238 AssertReturn(!ComSafeArrayOutIsNull(aValues), E_POINTER);
12239 AssertReturn(!ComSafeArrayOutIsNull(aTimestamps), E_POINTER);
12240 AssertReturn(!ComSafeArrayOutIsNull(aFlags), E_POINTER);
12241
12242 size_t cEntries = mHWData->mGuestProperties.size();
12243 com::SafeArray<BSTR> names(cEntries);
12244 com::SafeArray<BSTR> values(cEntries);
12245 com::SafeArray<LONG64> timestamps(cEntries);
12246 com::SafeArray<BSTR> flags(cEntries);
12247 unsigned i = 0;
12248 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
12249 it != mHWData->mGuestProperties.end();
12250 ++it)
12251 {
12252 char szFlags[MAX_FLAGS_LEN + 1];
12253 it->strName.cloneTo(&names[i]);
12254 it->strValue.cloneTo(&values[i]);
12255 timestamps[i] = it->mTimestamp;
12256 /* If it is NULL, keep it NULL. */
12257 if (it->mFlags)
12258 {
12259 writeFlags(it->mFlags, szFlags);
12260 Bstr(szFlags).cloneTo(&flags[i]);
12261 }
12262 else
12263 flags[i] = NULL;
12264 ++i;
12265 }
12266 names.detachTo(ComSafeArrayOutArg(aNames));
12267 values.detachTo(ComSafeArrayOutArg(aValues));
12268 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
12269 flags.detachTo(ComSafeArrayOutArg(aFlags));
12270 return S_OK;
12271#else
12272 ReturnComNotImplemented();
12273#endif
12274}
12275
12276STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
12277 IN_BSTR aValue,
12278 LONG64 aTimestamp,
12279 IN_BSTR aFlags)
12280{
12281 LogFlowThisFunc(("\n"));
12282
12283#ifdef VBOX_WITH_GUEST_PROPS
12284 using namespace guestProp;
12285
12286 CheckComArgStrNotEmptyOrNull(aName);
12287 CheckComArgNotNull(aValue);
12288 CheckComArgNotNull(aFlags);
12289
12290 try
12291 {
12292 /*
12293 * Convert input up front.
12294 */
12295 Utf8Str utf8Name(aName);
12296 uint32_t fFlags = NILFLAG;
12297 if (aFlags)
12298 {
12299 Utf8Str utf8Flags(aFlags);
12300 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
12301 AssertRCReturn(vrc, E_INVALIDARG);
12302 }
12303
12304 /*
12305 * Now grab the object lock, validate the state and do the update.
12306 */
12307 AutoCaller autoCaller(this);
12308 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12309
12310 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12311
12312 switch (mData->mMachineState)
12313 {
12314 case MachineState_Paused:
12315 case MachineState_Running:
12316 case MachineState_Teleporting:
12317 case MachineState_TeleportingPausedVM:
12318 case MachineState_LiveSnapshotting:
12319 case MachineState_DeletingSnapshotOnline:
12320 case MachineState_DeletingSnapshotPaused:
12321 case MachineState_Saving:
12322 break;
12323
12324 default:
12325#ifndef DEBUG_sunlover
12326 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
12327 VBOX_E_INVALID_VM_STATE);
12328#else
12329 return VBOX_E_INVALID_VM_STATE;
12330#endif
12331 }
12332
12333 setModified(IsModified_MachineData);
12334 mHWData.backup();
12335
12336 /** @todo r=bird: The careful memory handling doesn't work out here because
12337 * the catch block won't undo any damage we've done. So, if push_back throws
12338 * bad_alloc then you've lost the value.
12339 *
12340 * Another thing. Doing a linear search here isn't extremely efficient, esp.
12341 * since values that changes actually bubbles to the end of the list. Using
12342 * something that has an efficient lookup and can tolerate a bit of updates
12343 * would be nice. RTStrSpace is one suggestion (it's not perfect). Some
12344 * combination of RTStrCache (for sharing names and getting uniqueness into
12345 * the bargain) and hash/tree is another. */
12346 for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();
12347 iter != mHWData->mGuestProperties.end();
12348 ++iter)
12349 if (utf8Name == iter->strName)
12350 {
12351 mHWData->mGuestProperties.erase(iter);
12352 mData->mGuestPropertiesModified = TRUE;
12353 break;
12354 }
12355 if (aValue != NULL)
12356 {
12357 HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };
12358 mHWData->mGuestProperties.push_back(property);
12359 mData->mGuestPropertiesModified = TRUE;
12360 }
12361
12362 /*
12363 * Send a callback notification if appropriate
12364 */
12365 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
12366 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
12367 RTSTR_MAX,
12368 utf8Name.c_str(),
12369 RTSTR_MAX, NULL)
12370 )
12371 {
12372 alock.release();
12373
12374 mParent->onGuestPropertyChange(mData->mUuid,
12375 aName,
12376 aValue,
12377 aFlags);
12378 }
12379 }
12380 catch (...)
12381 {
12382 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
12383 }
12384 return S_OK;
12385#else
12386 ReturnComNotImplemented();
12387#endif
12388}
12389
12390STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
12391 IMediumAttachment **aNewAttachment)
12392{
12393 CheckComArgNotNull(aAttachment);
12394 CheckComArgOutPointerValid(aNewAttachment);
12395
12396 AutoCaller autoCaller(this);
12397 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12398
12399 // request the host lock first, since might be calling Host methods for getting host drives;
12400 // next, protect the media tree all the while we're in here, as well as our member variables
12401 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
12402 this->lockHandle(),
12403 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
12404
12405 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
12406
12407 Bstr ctrlName;
12408 LONG lPort;
12409 LONG lDevice;
12410 bool fTempEject;
12411 {
12412 AutoCaller autoAttachCaller(this);
12413 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12414
12415 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12416
12417 /* Need to query the details first, as the IMediumAttachment reference
12418 * might be to the original settings, which we are going to change. */
12419 ctrlName = pAttach->getControllerName();
12420 lPort = pAttach->getPort();
12421 lDevice = pAttach->getDevice();
12422 fTempEject = pAttach->getTempEject();
12423 }
12424
12425 if (!fTempEject)
12426 {
12427 /* Remember previously mounted medium. The medium before taking the
12428 * backup is not necessarily the same thing. */
12429 ComObjPtr<Medium> oldmedium;
12430 oldmedium = pAttach->getMedium();
12431
12432 setModified(IsModified_Storage);
12433 mMediaData.backup();
12434
12435 // The backup operation makes the pAttach reference point to the
12436 // old settings. Re-get the correct reference.
12437 pAttach = findAttachment(mMediaData->mAttachments,
12438 ctrlName.raw(),
12439 lPort,
12440 lDevice);
12441
12442 {
12443 AutoCaller autoAttachCaller(this);
12444 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12445
12446 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12447 if (!oldmedium.isNull())
12448 oldmedium->removeBackReference(mData->mUuid);
12449
12450 pAttach->updateMedium(NULL);
12451 pAttach->updateEjected();
12452 }
12453
12454 setModified(IsModified_Storage);
12455 }
12456 else
12457 {
12458 {
12459 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12460 pAttach->updateEjected();
12461 }
12462 }
12463
12464 pAttach.queryInterfaceTo(aNewAttachment);
12465
12466 return S_OK;
12467}
12468
12469// public methods only for internal purposes
12470/////////////////////////////////////////////////////////////////////////////
12471
12472/**
12473 * Called from the client watcher thread to check for expected or unexpected
12474 * death of the client process that has a direct session to this machine.
12475 *
12476 * On Win32 and on OS/2, this method is called only when we've got the
12477 * mutex (i.e. the client has either died or terminated normally) so it always
12478 * returns @c true (the client is terminated, the session machine is
12479 * uninitialized).
12480 *
12481 * On other platforms, the method returns @c true if the client process has
12482 * terminated normally or abnormally and the session machine was uninitialized,
12483 * and @c false if the client process is still alive.
12484 *
12485 * @note Locks this object for writing.
12486 */
12487bool SessionMachine::checkForDeath()
12488{
12489 Uninit::Reason reason;
12490 bool terminated = false;
12491
12492 /* Enclose autoCaller with a block because calling uninit() from under it
12493 * will deadlock. */
12494 {
12495 AutoCaller autoCaller(this);
12496 if (!autoCaller.isOk())
12497 {
12498 /* return true if not ready, to cause the client watcher to exclude
12499 * the corresponding session from watching */
12500 LogFlowThisFunc(("Already uninitialized!\n"));
12501 return true;
12502 }
12503
12504 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12505
12506 /* Determine the reason of death: if the session state is Closing here,
12507 * everything is fine. Otherwise it means that the client did not call
12508 * OnSessionEnd() before it released the IPC semaphore. This may happen
12509 * either because the client process has abnormally terminated, or
12510 * because it simply forgot to call ISession::Close() before exiting. We
12511 * threat the latter also as an abnormal termination (see
12512 * Session::uninit() for details). */
12513 reason = mData->mSession.mState == SessionState_Unlocking ?
12514 Uninit::Normal :
12515 Uninit::Abnormal;
12516
12517#if defined(RT_OS_WINDOWS)
12518
12519 AssertMsg(mIPCSem, ("semaphore must be created"));
12520
12521 /* release the IPC mutex */
12522 ::ReleaseMutex(mIPCSem);
12523
12524 terminated = true;
12525
12526#elif defined(RT_OS_OS2)
12527
12528 AssertMsg(mIPCSem, ("semaphore must be created"));
12529
12530 /* release the IPC mutex */
12531 ::DosReleaseMutexSem(mIPCSem);
12532
12533 terminated = true;
12534
12535#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12536
12537 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
12538
12539 int val = ::semctl(mIPCSem, 0, GETVAL);
12540 if (val > 0)
12541 {
12542 /* the semaphore is signaled, meaning the session is terminated */
12543 terminated = true;
12544 }
12545
12546#else
12547# error "Port me!"
12548#endif
12549
12550 } /* AutoCaller block */
12551
12552 if (terminated)
12553 uninit(reason);
12554
12555 return terminated;
12556}
12557
12558/**
12559 * @note Locks this object for reading.
12560 */
12561HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
12562{
12563 LogFlowThisFunc(("\n"));
12564
12565 AutoCaller autoCaller(this);
12566 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12567
12568 ComPtr<IInternalSessionControl> directControl;
12569 {
12570 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12571 directControl = mData->mSession.mDirectControl;
12572 }
12573
12574 /* ignore notifications sent after #OnSessionEnd() is called */
12575 if (!directControl)
12576 return S_OK;
12577
12578 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
12579}
12580
12581/**
12582 * @note Locks this object for reading.
12583 */
12584HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
12585 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
12586{
12587 LogFlowThisFunc(("\n"));
12588
12589 AutoCaller autoCaller(this);
12590 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12591
12592 ComPtr<IInternalSessionControl> directControl;
12593 {
12594 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12595 directControl = mData->mSession.mDirectControl;
12596 }
12597
12598 /* ignore notifications sent after #OnSessionEnd() is called */
12599 if (!directControl)
12600 return S_OK;
12601 /*
12602 * instead acting like callback we ask IVirtualBox deliver corresponding event
12603 */
12604
12605 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, aHostPort, aGuestIp, aGuestPort);
12606 return S_OK;
12607}
12608
12609/**
12610 * @note Locks this object for reading.
12611 */
12612HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
12613{
12614 LogFlowThisFunc(("\n"));
12615
12616 AutoCaller autoCaller(this);
12617 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12618
12619 ComPtr<IInternalSessionControl> directControl;
12620 {
12621 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12622 directControl = mData->mSession.mDirectControl;
12623 }
12624
12625 /* ignore notifications sent after #OnSessionEnd() is called */
12626 if (!directControl)
12627 return S_OK;
12628
12629 return directControl->OnSerialPortChange(serialPort);
12630}
12631
12632/**
12633 * @note Locks this object for reading.
12634 */
12635HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
12636{
12637 LogFlowThisFunc(("\n"));
12638
12639 AutoCaller autoCaller(this);
12640 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12641
12642 ComPtr<IInternalSessionControl> directControl;
12643 {
12644 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12645 directControl = mData->mSession.mDirectControl;
12646 }
12647
12648 /* ignore notifications sent after #OnSessionEnd() is called */
12649 if (!directControl)
12650 return S_OK;
12651
12652 return directControl->OnParallelPortChange(parallelPort);
12653}
12654
12655/**
12656 * @note Locks this object for reading.
12657 */
12658HRESULT SessionMachine::onStorageControllerChange()
12659{
12660 LogFlowThisFunc(("\n"));
12661
12662 AutoCaller autoCaller(this);
12663 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12664
12665 ComPtr<IInternalSessionControl> directControl;
12666 {
12667 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12668 directControl = mData->mSession.mDirectControl;
12669 }
12670
12671 /* ignore notifications sent after #OnSessionEnd() is called */
12672 if (!directControl)
12673 return S_OK;
12674
12675 return directControl->OnStorageControllerChange();
12676}
12677
12678/**
12679 * @note Locks this object for reading.
12680 */
12681HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
12682{
12683 LogFlowThisFunc(("\n"));
12684
12685 AutoCaller autoCaller(this);
12686 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12687
12688 ComPtr<IInternalSessionControl> directControl;
12689 {
12690 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12691 directControl = mData->mSession.mDirectControl;
12692 }
12693
12694 /* ignore notifications sent after #OnSessionEnd() is called */
12695 if (!directControl)
12696 return S_OK;
12697
12698 return directControl->OnMediumChange(aAttachment, aForce);
12699}
12700
12701/**
12702 * @note Locks this object for reading.
12703 */
12704HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
12705{
12706 LogFlowThisFunc(("\n"));
12707
12708 AutoCaller autoCaller(this);
12709 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
12710
12711 ComPtr<IInternalSessionControl> directControl;
12712 {
12713 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12714 directControl = mData->mSession.mDirectControl;
12715 }
12716
12717 /* ignore notifications sent after #OnSessionEnd() is called */
12718 if (!directControl)
12719 return S_OK;
12720
12721 return directControl->OnCPUChange(aCPU, aRemove);
12722}
12723
12724HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
12725{
12726 LogFlowThisFunc(("\n"));
12727
12728 AutoCaller autoCaller(this);
12729 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
12730
12731 ComPtr<IInternalSessionControl> directControl;
12732 {
12733 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12734 directControl = mData->mSession.mDirectControl;
12735 }
12736
12737 /* ignore notifications sent after #OnSessionEnd() is called */
12738 if (!directControl)
12739 return S_OK;
12740
12741 return directControl->OnCPUExecutionCapChange(aExecutionCap);
12742}
12743
12744/**
12745 * @note Locks this object for reading.
12746 */
12747HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
12748{
12749 LogFlowThisFunc(("\n"));
12750
12751 AutoCaller autoCaller(this);
12752 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12753
12754 ComPtr<IInternalSessionControl> directControl;
12755 {
12756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12757 directControl = mData->mSession.mDirectControl;
12758 }
12759
12760 /* ignore notifications sent after #OnSessionEnd() is called */
12761 if (!directControl)
12762 return S_OK;
12763
12764 return directControl->OnVRDEServerChange(aRestart);
12765}
12766
12767/**
12768 * @note Locks this object for reading.
12769 */
12770HRESULT SessionMachine::onUSBControllerChange()
12771{
12772 LogFlowThisFunc(("\n"));
12773
12774 AutoCaller autoCaller(this);
12775 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12776
12777 ComPtr<IInternalSessionControl> directControl;
12778 {
12779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12780 directControl = mData->mSession.mDirectControl;
12781 }
12782
12783 /* ignore notifications sent after #OnSessionEnd() is called */
12784 if (!directControl)
12785 return S_OK;
12786
12787 return directControl->OnUSBControllerChange();
12788}
12789
12790/**
12791 * @note Locks this object for reading.
12792 */
12793HRESULT SessionMachine::onSharedFolderChange()
12794{
12795 LogFlowThisFunc(("\n"));
12796
12797 AutoCaller autoCaller(this);
12798 AssertComRCReturnRC(autoCaller.rc());
12799
12800 ComPtr<IInternalSessionControl> directControl;
12801 {
12802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12803 directControl = mData->mSession.mDirectControl;
12804 }
12805
12806 /* ignore notifications sent after #OnSessionEnd() is called */
12807 if (!directControl)
12808 return S_OK;
12809
12810 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
12811}
12812
12813/**
12814 * @note Locks this object for reading.
12815 */
12816HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
12817{
12818 LogFlowThisFunc(("\n"));
12819
12820 AutoCaller autoCaller(this);
12821 AssertComRCReturnRC(autoCaller.rc());
12822
12823 ComPtr<IInternalSessionControl> directControl;
12824 {
12825 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12826 directControl = mData->mSession.mDirectControl;
12827 }
12828
12829 /* ignore notifications sent after #OnSessionEnd() is called */
12830 if (!directControl)
12831 return S_OK;
12832
12833 return directControl->OnClipboardModeChange(aClipboardMode);
12834}
12835
12836/**
12837 * @note Locks this object for reading.
12838 */
12839HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
12840{
12841 LogFlowThisFunc(("\n"));
12842
12843 AutoCaller autoCaller(this);
12844 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
12845
12846 ComPtr<IInternalSessionControl> directControl;
12847 {
12848 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12849 directControl = mData->mSession.mDirectControl;
12850 }
12851
12852 /* ignore notifications sent after #OnSessionEnd() is called */
12853 if (!directControl)
12854 return S_OK;
12855
12856 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
12857}
12858
12859/**
12860 * @note Locks this object for reading.
12861 */
12862HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove)
12863{
12864 LogFlowThisFunc(("\n"));
12865
12866 AutoCaller autoCaller(this);
12867 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12868
12869 ComPtr<IInternalSessionControl> directControl;
12870 {
12871 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12872 directControl = mData->mSession.mDirectControl;
12873 }
12874
12875 /* ignore notifications sent after #OnSessionEnd() is called */
12876 if (!directControl)
12877 return S_OK;
12878
12879 return directControl->OnStorageDeviceChange(aAttachment, aRemove);
12880}
12881
12882/**
12883 * Returns @c true if this machine's USB controller reports it has a matching
12884 * filter for the given USB device and @c false otherwise.
12885 *
12886 * @note locks this object for reading.
12887 */
12888bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
12889{
12890 AutoCaller autoCaller(this);
12891 /* silently return if not ready -- this method may be called after the
12892 * direct machine session has been called */
12893 if (!autoCaller.isOk())
12894 return false;
12895
12896#ifdef VBOX_WITH_USB
12897 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12898
12899 switch (mData->mMachineState)
12900 {
12901 case MachineState_Starting:
12902 case MachineState_Restoring:
12903 case MachineState_TeleportingIn:
12904 case MachineState_Paused:
12905 case MachineState_Running:
12906 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
12907 * elsewhere... */
12908 alock.release();
12909 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
12910 default: break;
12911 }
12912#else
12913 NOREF(aDevice);
12914 NOREF(aMaskedIfs);
12915#endif
12916 return false;
12917}
12918
12919/**
12920 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
12921 */
12922HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
12923 IVirtualBoxErrorInfo *aError,
12924 ULONG aMaskedIfs)
12925{
12926 LogFlowThisFunc(("\n"));
12927
12928 AutoCaller autoCaller(this);
12929
12930 /* This notification may happen after the machine object has been
12931 * uninitialized (the session was closed), so don't assert. */
12932 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12933
12934 ComPtr<IInternalSessionControl> directControl;
12935 {
12936 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12937 directControl = mData->mSession.mDirectControl;
12938 }
12939
12940 /* fail on notifications sent after #OnSessionEnd() is called, it is
12941 * expected by the caller */
12942 if (!directControl)
12943 return E_FAIL;
12944
12945 /* No locks should be held at this point. */
12946 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
12947 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
12948
12949 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
12950}
12951
12952/**
12953 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
12954 */
12955HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
12956 IVirtualBoxErrorInfo *aError)
12957{
12958 LogFlowThisFunc(("\n"));
12959
12960 AutoCaller autoCaller(this);
12961
12962 /* This notification may happen after the machine object has been
12963 * uninitialized (the session was closed), so don't assert. */
12964 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12965
12966 ComPtr<IInternalSessionControl> directControl;
12967 {
12968 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12969 directControl = mData->mSession.mDirectControl;
12970 }
12971
12972 /* fail on notifications sent after #OnSessionEnd() is called, it is
12973 * expected by the caller */
12974 if (!directControl)
12975 return E_FAIL;
12976
12977 /* No locks should be held at this point. */
12978 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
12979 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
12980
12981 return directControl->OnUSBDeviceDetach(aId, aError);
12982}
12983
12984// protected methods
12985/////////////////////////////////////////////////////////////////////////////
12986
12987/**
12988 * Helper method to finalize saving the state.
12989 *
12990 * @note Must be called from under this object's lock.
12991 *
12992 * @param aRc S_OK if the snapshot has been taken successfully
12993 * @param aErrMsg human readable error message for failure
12994 *
12995 * @note Locks mParent + this objects for writing.
12996 */
12997HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
12998{
12999 LogFlowThisFuncEnter();
13000
13001 AutoCaller autoCaller(this);
13002 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13003
13004 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13005
13006 HRESULT rc = S_OK;
13007
13008 if (SUCCEEDED(aRc))
13009 {
13010 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13011
13012 /* save all VM settings */
13013 rc = saveSettings(NULL);
13014 // no need to check whether VirtualBox.xml needs saving also since
13015 // we can't have a name change pending at this point
13016 }
13017 else
13018 {
13019 // delete the saved state file (it might have been already created);
13020 // we need not check whether this is shared with a snapshot here because
13021 // we certainly created this saved state file here anew
13022 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13023 }
13024
13025 /* notify the progress object about operation completion */
13026 Assert(mConsoleTaskData.mProgress);
13027 if (SUCCEEDED(aRc))
13028 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13029 else
13030 {
13031 if (aErrMsg.length())
13032 mConsoleTaskData.mProgress->notifyComplete(aRc,
13033 COM_IIDOF(ISession),
13034 getComponentName(),
13035 aErrMsg.c_str());
13036 else
13037 mConsoleTaskData.mProgress->notifyComplete(aRc);
13038 }
13039
13040 /* clear out the temporary saved state data */
13041 mConsoleTaskData.mLastState = MachineState_Null;
13042 mConsoleTaskData.strStateFilePath.setNull();
13043 mConsoleTaskData.mProgress.setNull();
13044
13045 LogFlowThisFuncLeave();
13046 return rc;
13047}
13048
13049/**
13050 * Deletes the given file if it is no longer in use by either the current machine state
13051 * (if the machine is "saved") or any of the machine's snapshots.
13052 *
13053 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13054 * but is different for each SnapshotMachine. When calling this, the order of calling this
13055 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13056 * is therefore critical. I know, it's all rather messy.
13057 *
13058 * @param strStateFile
13059 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
13060 */
13061void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
13062 Snapshot *pSnapshotToIgnore)
13063{
13064 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13065 if ( (strStateFile.isNotEmpty())
13066 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13067 )
13068 // ... and it must also not be shared with other snapshots
13069 if ( !mData->mFirstSnapshot
13070 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13071 // this checks the SnapshotMachine's state file paths
13072 )
13073 RTFileDelete(strStateFile.c_str());
13074}
13075
13076/**
13077 * Locks the attached media.
13078 *
13079 * All attached hard disks are locked for writing and DVD/floppy are locked for
13080 * reading. Parents of attached hard disks (if any) are locked for reading.
13081 *
13082 * This method also performs accessibility check of all media it locks: if some
13083 * media is inaccessible, the method will return a failure and a bunch of
13084 * extended error info objects per each inaccessible medium.
13085 *
13086 * Note that this method is atomic: if it returns a success, all media are
13087 * locked as described above; on failure no media is locked at all (all
13088 * succeeded individual locks will be undone).
13089 *
13090 * This method is intended to be called when the machine is in Starting or
13091 * Restoring state and asserts otherwise.
13092 *
13093 * The locks made by this method must be undone by calling #unlockMedia() when
13094 * no more needed.
13095 */
13096HRESULT SessionMachine::lockMedia()
13097{
13098 AutoCaller autoCaller(this);
13099 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13100
13101 AutoMultiWriteLock2 alock(this->lockHandle(),
13102 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13103
13104 AssertReturn( mData->mMachineState == MachineState_Starting
13105 || mData->mMachineState == MachineState_Restoring
13106 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13107 /* bail out if trying to lock things with already set up locking */
13108 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13109
13110 clearError();
13111 MultiResult mrc(S_OK);
13112
13113 /* Collect locking information for all medium objects attached to the VM. */
13114 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13115 it != mMediaData->mAttachments.end();
13116 ++it)
13117 {
13118 MediumAttachment* pAtt = *it;
13119 DeviceType_T devType = pAtt->getType();
13120 Medium *pMedium = pAtt->getMedium();
13121
13122 MediumLockList *pMediumLockList(new MediumLockList());
13123 // There can be attachments without a medium (floppy/dvd), and thus
13124 // it's impossible to create a medium lock list. It still makes sense
13125 // to have the empty medium lock list in the map in case a medium is
13126 // attached later.
13127 if (pMedium != NULL)
13128 {
13129 MediumType_T mediumType = pMedium->getType();
13130 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13131 || mediumType == MediumType_Shareable;
13132 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13133
13134 alock.release();
13135 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13136 !fIsReadOnlyLock /* fMediumLockWrite */,
13137 NULL,
13138 *pMediumLockList);
13139 alock.acquire();
13140 if (FAILED(mrc))
13141 {
13142 delete pMediumLockList;
13143 mData->mSession.mLockedMedia.Clear();
13144 break;
13145 }
13146 }
13147
13148 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13149 if (FAILED(rc))
13150 {
13151 mData->mSession.mLockedMedia.Clear();
13152 mrc = setError(rc,
13153 tr("Collecting locking information for all attached media failed"));
13154 break;
13155 }
13156 }
13157
13158 if (SUCCEEDED(mrc))
13159 {
13160 /* Now lock all media. If this fails, nothing is locked. */
13161 alock.release();
13162 HRESULT rc = mData->mSession.mLockedMedia.Lock();
13163 alock.acquire();
13164 if (FAILED(rc))
13165 {
13166 mrc = setError(rc,
13167 tr("Locking of attached media failed"));
13168 }
13169 }
13170
13171 return mrc;
13172}
13173
13174/**
13175 * Undoes the locks made by by #lockMedia().
13176 */
13177void SessionMachine::unlockMedia()
13178{
13179 AutoCaller autoCaller(this);
13180 AssertComRCReturnVoid(autoCaller.rc());
13181
13182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13183
13184 /* we may be holding important error info on the current thread;
13185 * preserve it */
13186 ErrorInfoKeeper eik;
13187
13188 HRESULT rc = mData->mSession.mLockedMedia.Clear();
13189 AssertComRC(rc);
13190}
13191
13192/**
13193 * Helper to change the machine state (reimplementation).
13194 *
13195 * @note Locks this object for writing.
13196 */
13197HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
13198{
13199 LogFlowThisFuncEnter();
13200 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
13201
13202 AutoCaller autoCaller(this);
13203 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13204
13205 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13206
13207 MachineState_T oldMachineState = mData->mMachineState;
13208
13209 AssertMsgReturn(oldMachineState != aMachineState,
13210 ("oldMachineState=%s, aMachineState=%s\n",
13211 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
13212 E_FAIL);
13213
13214 HRESULT rc = S_OK;
13215
13216 int stsFlags = 0;
13217 bool deleteSavedState = false;
13218
13219 /* detect some state transitions */
13220
13221 if ( ( oldMachineState == MachineState_Saved
13222 && aMachineState == MachineState_Restoring)
13223 || ( ( oldMachineState == MachineState_PoweredOff
13224 || oldMachineState == MachineState_Teleported
13225 || oldMachineState == MachineState_Aborted
13226 )
13227 && ( aMachineState == MachineState_TeleportingIn
13228 || aMachineState == MachineState_Starting
13229 )
13230 )
13231 )
13232 {
13233 /* The EMT thread is about to start */
13234
13235 /* Nothing to do here for now... */
13236
13237 /// @todo NEWMEDIA don't let mDVDDrive and other children
13238 /// change anything when in the Starting/Restoring state
13239 }
13240 else if ( ( oldMachineState == MachineState_Running
13241 || oldMachineState == MachineState_Paused
13242 || oldMachineState == MachineState_Teleporting
13243 || oldMachineState == MachineState_LiveSnapshotting
13244 || oldMachineState == MachineState_Stuck
13245 || oldMachineState == MachineState_Starting
13246 || oldMachineState == MachineState_Stopping
13247 || oldMachineState == MachineState_Saving
13248 || oldMachineState == MachineState_Restoring
13249 || oldMachineState == MachineState_TeleportingPausedVM
13250 || oldMachineState == MachineState_TeleportingIn
13251 )
13252 && ( aMachineState == MachineState_PoweredOff
13253 || aMachineState == MachineState_Saved
13254 || aMachineState == MachineState_Teleported
13255 || aMachineState == MachineState_Aborted
13256 )
13257 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
13258 * snapshot */
13259 && ( mConsoleTaskData.mSnapshot.isNull()
13260 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
13261 )
13262 )
13263 {
13264 /* The EMT thread has just stopped, unlock attached media. Note that as
13265 * opposed to locking that is done from Console, we do unlocking here
13266 * because the VM process may have aborted before having a chance to
13267 * properly unlock all media it locked. */
13268
13269 unlockMedia();
13270 }
13271
13272 if (oldMachineState == MachineState_Restoring)
13273 {
13274 if (aMachineState != MachineState_Saved)
13275 {
13276 /*
13277 * delete the saved state file once the machine has finished
13278 * restoring from it (note that Console sets the state from
13279 * Restoring to Saved if the VM couldn't restore successfully,
13280 * to give the user an ability to fix an error and retry --
13281 * we keep the saved state file in this case)
13282 */
13283 deleteSavedState = true;
13284 }
13285 }
13286 else if ( oldMachineState == MachineState_Saved
13287 && ( aMachineState == MachineState_PoweredOff
13288 || aMachineState == MachineState_Aborted
13289 || aMachineState == MachineState_Teleported
13290 )
13291 )
13292 {
13293 /*
13294 * delete the saved state after Console::ForgetSavedState() is called
13295 * or if the VM process (owning a direct VM session) crashed while the
13296 * VM was Saved
13297 */
13298
13299 /// @todo (dmik)
13300 // Not sure that deleting the saved state file just because of the
13301 // client death before it attempted to restore the VM is a good
13302 // thing. But when it crashes we need to go to the Aborted state
13303 // which cannot have the saved state file associated... The only
13304 // way to fix this is to make the Aborted condition not a VM state
13305 // but a bool flag: i.e., when a crash occurs, set it to true and
13306 // change the state to PoweredOff or Saved depending on the
13307 // saved state presence.
13308
13309 deleteSavedState = true;
13310 mData->mCurrentStateModified = TRUE;
13311 stsFlags |= SaveSTS_CurStateModified;
13312 }
13313
13314 if ( aMachineState == MachineState_Starting
13315 || aMachineState == MachineState_Restoring
13316 || aMachineState == MachineState_TeleportingIn
13317 )
13318 {
13319 /* set the current state modified flag to indicate that the current
13320 * state is no more identical to the state in the
13321 * current snapshot */
13322 if (!mData->mCurrentSnapshot.isNull())
13323 {
13324 mData->mCurrentStateModified = TRUE;
13325 stsFlags |= SaveSTS_CurStateModified;
13326 }
13327 }
13328
13329 if (deleteSavedState)
13330 {
13331 if (mRemoveSavedState)
13332 {
13333 Assert(!mSSData->strStateFilePath.isEmpty());
13334
13335 // it is safe to delete the saved state file if ...
13336 if ( !mData->mFirstSnapshot // ... we have no snapshots or
13337 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
13338 // ... none of the snapshots share the saved state file
13339 )
13340 RTFileDelete(mSSData->strStateFilePath.c_str());
13341 }
13342
13343 mSSData->strStateFilePath.setNull();
13344 stsFlags |= SaveSTS_StateFilePath;
13345 }
13346
13347 /* redirect to the underlying peer machine */
13348 mPeer->setMachineState(aMachineState);
13349
13350 if ( aMachineState == MachineState_PoweredOff
13351 || aMachineState == MachineState_Teleported
13352 || aMachineState == MachineState_Aborted
13353 || aMachineState == MachineState_Saved)
13354 {
13355 /* the machine has stopped execution
13356 * (or the saved state file was adopted) */
13357 stsFlags |= SaveSTS_StateTimeStamp;
13358 }
13359
13360 if ( ( oldMachineState == MachineState_PoweredOff
13361 || oldMachineState == MachineState_Aborted
13362 || oldMachineState == MachineState_Teleported
13363 )
13364 && aMachineState == MachineState_Saved)
13365 {
13366 /* the saved state file was adopted */
13367 Assert(!mSSData->strStateFilePath.isEmpty());
13368 stsFlags |= SaveSTS_StateFilePath;
13369 }
13370
13371#ifdef VBOX_WITH_GUEST_PROPS
13372 if ( aMachineState == MachineState_PoweredOff
13373 || aMachineState == MachineState_Aborted
13374 || aMachineState == MachineState_Teleported)
13375 {
13376 /* Make sure any transient guest properties get removed from the
13377 * property store on shutdown. */
13378
13379 HWData::GuestPropertyList::iterator it;
13380 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
13381 if (!fNeedsSaving)
13382 for (it = mHWData->mGuestProperties.begin();
13383 it != mHWData->mGuestProperties.end(); ++it)
13384 if ( (it->mFlags & guestProp::TRANSIENT)
13385 || (it->mFlags & guestProp::TRANSRESET))
13386 {
13387 fNeedsSaving = true;
13388 break;
13389 }
13390 if (fNeedsSaving)
13391 {
13392 mData->mCurrentStateModified = TRUE;
13393 stsFlags |= SaveSTS_CurStateModified;
13394 SaveSettings(); // @todo r=dj why the public method? why first SaveSettings and then saveStateSettings?
13395 }
13396 }
13397#endif
13398
13399 rc = saveStateSettings(stsFlags);
13400
13401 if ( ( oldMachineState != MachineState_PoweredOff
13402 && oldMachineState != MachineState_Aborted
13403 && oldMachineState != MachineState_Teleported
13404 )
13405 && ( aMachineState == MachineState_PoweredOff
13406 || aMachineState == MachineState_Aborted
13407 || aMachineState == MachineState_Teleported
13408 )
13409 )
13410 {
13411 /* we've been shut down for any reason */
13412 /* no special action so far */
13413 }
13414
13415 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
13416 LogFlowThisFuncLeave();
13417 return rc;
13418}
13419
13420/**
13421 * Sends the current machine state value to the VM process.
13422 *
13423 * @note Locks this object for reading, then calls a client process.
13424 */
13425HRESULT SessionMachine::updateMachineStateOnClient()
13426{
13427 AutoCaller autoCaller(this);
13428 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13429
13430 ComPtr<IInternalSessionControl> directControl;
13431 {
13432 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13433 AssertReturn(!!mData, E_FAIL);
13434 directControl = mData->mSession.mDirectControl;
13435
13436 /* directControl may be already set to NULL here in #OnSessionEnd()
13437 * called too early by the direct session process while there is still
13438 * some operation (like deleting the snapshot) in progress. The client
13439 * process in this case is waiting inside Session::close() for the
13440 * "end session" process object to complete, while #uninit() called by
13441 * #checkForDeath() on the Watcher thread is waiting for the pending
13442 * operation to complete. For now, we accept this inconsistent behavior
13443 * and simply do nothing here. */
13444
13445 if (mData->mSession.mState == SessionState_Unlocking)
13446 return S_OK;
13447
13448 AssertReturn(!directControl.isNull(), E_FAIL);
13449 }
13450
13451 return directControl->UpdateMachineState(mData->mMachineState);
13452}
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