VirtualBox

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

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

Main/Machine: fix path comparison when detecting whether the VM lives in the expected subdirectory, necessary due to mixing / and \

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 456.6 KB
Line 
1/* $Id: MachineImpl.cpp 42757 2012-08-10 14:44:08Z 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.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
27# include <errno.h>
28# include <sys/types.h>
29# include <sys/stat.h>
30# include <sys/ipc.h>
31# include <sys/sem.h>
32#endif
33
34#include "Logging.h"
35#include "VirtualBoxImpl.h"
36#include "MachineImpl.h"
37#include "ProgressImpl.h"
38#include "ProgressProxyImpl.h"
39#include "MediumAttachmentImpl.h"
40#include "MediumImpl.h"
41#include "MediumLock.h"
42#include "USBControllerImpl.h"
43#include "HostImpl.h"
44#include "SharedFolderImpl.h"
45#include "GuestOSTypeImpl.h"
46#include "VirtualBoxErrorInfoImpl.h"
47#include "GuestImpl.h"
48#include "StorageControllerImpl.h"
49#include "DisplayImpl.h"
50#include "DisplayUtils.h"
51#include "BandwidthControlImpl.h"
52#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 mDragAndDropMode = DragAndDropMode_Disabled;
200 mGuestPropertyNotificationPatterns = "";
201
202 mFirmwareType = FirmwareType_BIOS;
203 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
204 mPointingHIDType = PointingHIDType_PS2Mouse;
205 mChipsetType = ChipsetType_PIIX3;
206 mEmulatedUSBCardReaderEnabled = FALSE;
207
208 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
209 mCPUAttached[i] = false;
210
211 mIOCacheEnabled = true;
212 mIOCacheSize = 5; /* 5MB */
213
214 /* Maximum CPU execution cap by default. */
215 mCpuExecutionCap = 100;
216}
217
218Machine::HWData::~HWData()
219{
220}
221
222/////////////////////////////////////////////////////////////////////////////
223// Machine::HDData structure
224/////////////////////////////////////////////////////////////////////////////
225
226Machine::MediaData::MediaData()
227{
228}
229
230Machine::MediaData::~MediaData()
231{
232}
233
234/////////////////////////////////////////////////////////////////////////////
235// Machine class
236/////////////////////////////////////////////////////////////////////////////
237
238// constructor / destructor
239/////////////////////////////////////////////////////////////////////////////
240
241Machine::Machine()
242 : mCollectorGuest(NULL),
243 mPeer(NULL),
244 mParent(NULL),
245 mSerialPorts(),
246 mParallelPorts(),
247 uRegistryNeedsSaving(0)
248{}
249
250Machine::~Machine()
251{}
252
253HRESULT Machine::FinalConstruct()
254{
255 LogFlowThisFunc(("\n"));
256 return BaseFinalConstruct();
257}
258
259void Machine::FinalRelease()
260{
261 LogFlowThisFunc(("\n"));
262 uninit();
263 BaseFinalRelease();
264}
265
266/**
267 * Initializes a new machine instance; this init() variant creates a new, empty machine.
268 * This gets called from VirtualBox::CreateMachine().
269 *
270 * @param aParent Associated parent object
271 * @param strConfigFile Local file system path to the VM settings file (can
272 * be relative to the VirtualBox config directory).
273 * @param strName name for the machine
274 * @param llGroups list of groups for the machine
275 * @param aOsType OS Type of this machine or NULL.
276 * @param aId UUID for the new machine.
277 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
278 *
279 * @return Success indicator. if not S_OK, the machine object is invalid
280 */
281HRESULT Machine::init(VirtualBox *aParent,
282 const Utf8Str &strConfigFile,
283 const Utf8Str &strName,
284 const StringsList &llGroups,
285 GuestOSType *aOsType,
286 const Guid &aId,
287 bool fForceOverwrite)
288{
289 LogFlowThisFuncEnter();
290 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
291
292 /* Enclose the state transition NotReady->InInit->Ready */
293 AutoInitSpan autoInitSpan(this);
294 AssertReturn(autoInitSpan.isOk(), E_FAIL);
295
296 HRESULT rc = initImpl(aParent, strConfigFile);
297 if (FAILED(rc)) return rc;
298
299 rc = tryCreateMachineConfigFile(fForceOverwrite);
300 if (FAILED(rc)) return rc;
301
302 if (SUCCEEDED(rc))
303 {
304 // create an empty machine config
305 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
306
307 rc = initDataAndChildObjects();
308 }
309
310 if (SUCCEEDED(rc))
311 {
312 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
313 mData->mAccessible = TRUE;
314
315 unconst(mData->mUuid) = aId;
316
317 mUserData->s.strName = strName;
318
319 mUserData->s.llGroups = llGroups;
320
321 // the "name sync" flag determines whether the machine directory gets renamed along
322 // with the machine file; say so if the settings file name is the same as the
323 // settings file parent directory (machine directory)
324 mUserData->s.fNameSync = isInOwnDir();
325
326 // initialize the default snapshots folder
327 rc = COMSETTER(SnapshotFolder)(NULL);
328 AssertComRC(rc);
329
330 if (aOsType)
331 {
332 /* Store OS type */
333 mUserData->s.strOsType = aOsType->id();
334
335 /* Apply BIOS defaults */
336 mBIOSSettings->applyDefaults(aOsType);
337
338 /* Apply network adapters defaults */
339 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
340 mNetworkAdapters[slot]->applyDefaults(aOsType);
341
342 /* Apply serial port defaults */
343 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
344 mSerialPorts[slot]->applyDefaults(aOsType);
345 }
346
347 /* At this point the changing of the current state modification
348 * flag is allowed. */
349 allowStateModification();
350
351 /* commit all changes made during the initialization */
352 commit();
353 }
354
355 /* Confirm a successful initialization when it's the case */
356 if (SUCCEEDED(rc))
357 {
358 if (mData->mAccessible)
359 autoInitSpan.setSucceeded();
360 else
361 autoInitSpan.setLimited();
362 }
363
364 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
365 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
366 mData->mRegistered,
367 mData->mAccessible,
368 rc));
369
370 LogFlowThisFuncLeave();
371
372 return rc;
373}
374
375/**
376 * Initializes a new instance with data from machine XML (formerly Init_Registered).
377 * Gets called in two modes:
378 *
379 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
380 * UUID is specified and we mark the machine as "registered";
381 *
382 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
383 * and the machine remains unregistered until RegisterMachine() is called.
384 *
385 * @param aParent Associated parent object
386 * @param aConfigFile Local file system path to the VM settings file (can
387 * be relative to the VirtualBox config directory).
388 * @param aId UUID of the machine or NULL (see above).
389 *
390 * @return Success indicator. if not S_OK, the machine object is invalid
391 */
392HRESULT Machine::init(VirtualBox *aParent,
393 const Utf8Str &strConfigFile,
394 const Guid *aId)
395{
396 LogFlowThisFuncEnter();
397 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
398
399 /* Enclose the state transition NotReady->InInit->Ready */
400 AutoInitSpan autoInitSpan(this);
401 AssertReturn(autoInitSpan.isOk(), E_FAIL);
402
403 HRESULT rc = initImpl(aParent, strConfigFile);
404 if (FAILED(rc)) return rc;
405
406 if (aId)
407 {
408 // loading a registered VM:
409 unconst(mData->mUuid) = *aId;
410 mData->mRegistered = TRUE;
411 // now load the settings from XML:
412 rc = registeredInit();
413 // this calls initDataAndChildObjects() and loadSettings()
414 }
415 else
416 {
417 // opening an unregistered VM (VirtualBox::OpenMachine()):
418 rc = initDataAndChildObjects();
419
420 if (SUCCEEDED(rc))
421 {
422 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
423 mData->mAccessible = TRUE;
424
425 try
426 {
427 // load and parse machine XML; this will throw on XML or logic errors
428 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
429
430 // reject VM UUID duplicates, they can happen if someone
431 // tries to register an already known VM config again
432 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
433 true /* fPermitInaccessible */,
434 false /* aDoSetError */,
435 NULL) != VBOX_E_OBJECT_NOT_FOUND)
436 {
437 throw setError(E_FAIL,
438 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
439 mData->m_strConfigFile.c_str());
440 }
441
442 // use UUID from machine config
443 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
444
445 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
446 NULL /* puuidRegistry */);
447 if (FAILED(rc)) throw rc;
448
449 /* At this point the changing of the current state modification
450 * flag is allowed. */
451 allowStateModification();
452
453 commit();
454 }
455 catch (HRESULT err)
456 {
457 /* we assume that error info is set by the thrower */
458 rc = err;
459 }
460 catch (...)
461 {
462 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
463 }
464 }
465 }
466
467 /* Confirm a successful initialization when it's the case */
468 if (SUCCEEDED(rc))
469 {
470 if (mData->mAccessible)
471 autoInitSpan.setSucceeded();
472 else
473 {
474 autoInitSpan.setLimited();
475
476 // uninit media from this machine's media registry, or else
477 // reloading the settings will fail
478 mParent->unregisterMachineMedia(getId());
479 }
480 }
481
482 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
483 "rc=%08X\n",
484 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
485 mData->mRegistered, mData->mAccessible, rc));
486
487 LogFlowThisFuncLeave();
488
489 return rc;
490}
491
492/**
493 * Initializes a new instance from a machine config that is already in memory
494 * (import OVF case). Since we are importing, the UUID in the machine
495 * config is ignored and we always generate a fresh one.
496 *
497 * @param strName Name for the new machine; this overrides what is specified in config and is used
498 * for the settings file as well.
499 * @param config Machine configuration loaded and parsed from XML.
500 *
501 * @return Success indicator. if not S_OK, the machine object is invalid
502 */
503HRESULT Machine::init(VirtualBox *aParent,
504 const Utf8Str &strName,
505 const settings::MachineConfigFile &config)
506{
507 LogFlowThisFuncEnter();
508
509 /* Enclose the state transition NotReady->InInit->Ready */
510 AutoInitSpan autoInitSpan(this);
511 AssertReturn(autoInitSpan.isOk(), E_FAIL);
512
513 Utf8Str strConfigFile;
514 aParent->getDefaultMachineFolder(strConfigFile);
515 strConfigFile.append(RTPATH_DELIMITER);
516 strConfigFile.append(strName);
517 strConfigFile.append(RTPATH_DELIMITER);
518 strConfigFile.append(strName);
519 strConfigFile.append(".vbox");
520
521 HRESULT rc = initImpl(aParent, strConfigFile);
522 if (FAILED(rc)) return rc;
523
524 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
525 if (FAILED(rc)) return rc;
526
527 rc = initDataAndChildObjects();
528
529 if (SUCCEEDED(rc))
530 {
531 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
532 mData->mAccessible = TRUE;
533
534 // create empty machine config for instance data
535 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
536
537 // generate fresh UUID, ignore machine config
538 unconst(mData->mUuid).create();
539
540 rc = loadMachineDataFromSettings(config,
541 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
542
543 // override VM name as well, it may be different
544 mUserData->s.strName = strName;
545
546 if (SUCCEEDED(rc))
547 {
548 /* At this point the changing of the current state modification
549 * flag is allowed. */
550 allowStateModification();
551
552 /* commit all changes made during the initialization */
553 commit();
554 }
555 }
556
557 /* Confirm a successful initialization when it's the case */
558 if (SUCCEEDED(rc))
559 {
560 if (mData->mAccessible)
561 autoInitSpan.setSucceeded();
562 else
563 {
564 autoInitSpan.setLimited();
565
566 // uninit media from this machine's media registry, or else
567 // reloading the settings will fail
568 mParent->unregisterMachineMedia(getId());
569 }
570 }
571
572 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
573 "rc=%08X\n",
574 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
575 mData->mRegistered, mData->mAccessible, rc));
576
577 LogFlowThisFuncLeave();
578
579 return rc;
580}
581
582/**
583 * Shared code between the various init() implementations.
584 * @param aParent
585 * @return
586 */
587HRESULT Machine::initImpl(VirtualBox *aParent,
588 const Utf8Str &strConfigFile)
589{
590 LogFlowThisFuncEnter();
591
592 AssertReturn(aParent, E_INVALIDARG);
593 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
594
595 HRESULT rc = S_OK;
596
597 /* share the parent weakly */
598 unconst(mParent) = aParent;
599
600 /* allocate the essential machine data structure (the rest will be
601 * allocated later by initDataAndChildObjects() */
602 mData.allocate();
603
604 /* memorize the config file name (as provided) */
605 mData->m_strConfigFile = strConfigFile;
606
607 /* get the full file name */
608 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
609 if (RT_FAILURE(vrc1))
610 return setError(VBOX_E_FILE_ERROR,
611 tr("Invalid machine settings file name '%s' (%Rrc)"),
612 strConfigFile.c_str(),
613 vrc1);
614
615 LogFlowThisFuncLeave();
616
617 return rc;
618}
619
620/**
621 * Tries to create a machine settings file in the path stored in the machine
622 * instance data. Used when a new machine is created to fail gracefully if
623 * the settings file could not be written (e.g. because machine dir is read-only).
624 * @return
625 */
626HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
627{
628 HRESULT rc = S_OK;
629
630 // when we create a new machine, we must be able to create the settings file
631 RTFILE f = NIL_RTFILE;
632 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
633 if ( RT_SUCCESS(vrc)
634 || vrc == VERR_SHARING_VIOLATION
635 )
636 {
637 if (RT_SUCCESS(vrc))
638 RTFileClose(f);
639 if (!fForceOverwrite)
640 rc = setError(VBOX_E_FILE_ERROR,
641 tr("Machine settings file '%s' already exists"),
642 mData->m_strConfigFileFull.c_str());
643 else
644 {
645 /* try to delete the config file, as otherwise the creation
646 * of a new settings file will fail. */
647 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
648 if (RT_FAILURE(vrc2))
649 rc = setError(VBOX_E_FILE_ERROR,
650 tr("Could not delete the existing settings file '%s' (%Rrc)"),
651 mData->m_strConfigFileFull.c_str(), vrc2);
652 }
653 }
654 else if ( vrc != VERR_FILE_NOT_FOUND
655 && vrc != VERR_PATH_NOT_FOUND
656 )
657 rc = setError(VBOX_E_FILE_ERROR,
658 tr("Invalid machine settings file name '%s' (%Rrc)"),
659 mData->m_strConfigFileFull.c_str(),
660 vrc);
661 return rc;
662}
663
664/**
665 * Initializes the registered machine by loading the settings file.
666 * This method is separated from #init() in order to make it possible to
667 * retry the operation after VirtualBox startup instead of refusing to
668 * startup the whole VirtualBox server in case if the settings file of some
669 * registered VM is invalid or inaccessible.
670 *
671 * @note Must be always called from this object's write lock
672 * (unless called from #init() that doesn't need any locking).
673 * @note Locks the mUSBController method for writing.
674 * @note Subclasses must not call this method.
675 */
676HRESULT Machine::registeredInit()
677{
678 AssertReturn(!isSessionMachine(), E_FAIL);
679 AssertReturn(!isSnapshotMachine(), E_FAIL);
680 AssertReturn(!mData->mUuid.isEmpty(), E_FAIL);
681 AssertReturn(!mData->mAccessible, E_FAIL);
682
683 HRESULT rc = initDataAndChildObjects();
684
685 if (SUCCEEDED(rc))
686 {
687 /* Temporarily reset the registered flag in order to let setters
688 * potentially called from loadSettings() succeed (isMutable() used in
689 * all setters will return FALSE for a Machine instance if mRegistered
690 * is TRUE). */
691 mData->mRegistered = FALSE;
692
693 try
694 {
695 // load and parse machine XML; this will throw on XML or logic errors
696 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
697
698 if (mData->mUuid != mData->pMachineConfigFile->uuid)
699 throw setError(E_FAIL,
700 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
701 mData->pMachineConfigFile->uuid.raw(),
702 mData->m_strConfigFileFull.c_str(),
703 mData->mUuid.toString().c_str(),
704 mParent->settingsFilePath().c_str());
705
706 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
707 NULL /* const Guid *puuidRegistry */);
708 if (FAILED(rc)) throw rc;
709 }
710 catch (HRESULT err)
711 {
712 /* we assume that error info is set by the thrower */
713 rc = err;
714 }
715 catch (...)
716 {
717 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
718 }
719
720 /* Restore the registered flag (even on failure) */
721 mData->mRegistered = TRUE;
722 }
723
724 if (SUCCEEDED(rc))
725 {
726 /* Set mAccessible to TRUE only if we successfully locked and loaded
727 * the settings file */
728 mData->mAccessible = TRUE;
729
730 /* commit all changes made during loading the settings file */
731 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
732 /// @todo r=klaus for some reason the settings loading logic backs up
733 // the settings, and therefore a commit is needed. Should probably be changed.
734 }
735 else
736 {
737 /* If the machine is registered, then, instead of returning a
738 * failure, we mark it as inaccessible and set the result to
739 * success to give it a try later */
740
741 /* fetch the current error info */
742 mData->mAccessError = com::ErrorInfo();
743 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
744 mData->mUuid.raw(),
745 mData->mAccessError.getText().raw()));
746
747 /* rollback all changes */
748 rollback(false /* aNotify */);
749
750 // uninit media from this machine's media registry, or else
751 // reloading the settings will fail
752 mParent->unregisterMachineMedia(getId());
753
754 /* uninitialize the common part to make sure all data is reset to
755 * default (null) values */
756 uninitDataAndChildObjects();
757
758 rc = S_OK;
759 }
760
761 return rc;
762}
763
764/**
765 * Uninitializes the instance.
766 * Called either from FinalRelease() or by the parent when it gets destroyed.
767 *
768 * @note The caller of this method must make sure that this object
769 * a) doesn't have active callers on the current thread and b) is not locked
770 * by the current thread; otherwise uninit() will hang either a) due to
771 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
772 * a dead-lock caused by this thread waiting for all callers on the other
773 * threads are done but preventing them from doing so by holding a lock.
774 */
775void Machine::uninit()
776{
777 LogFlowThisFuncEnter();
778
779 Assert(!isWriteLockOnCurrentThread());
780
781 Assert(!uRegistryNeedsSaving);
782 if (uRegistryNeedsSaving)
783 {
784 AutoCaller autoCaller(this);
785 if (SUCCEEDED(autoCaller.rc()))
786 {
787 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
788 saveSettings(NULL, Machine::SaveS_Force);
789 }
790 }
791
792 /* Enclose the state transition Ready->InUninit->NotReady */
793 AutoUninitSpan autoUninitSpan(this);
794 if (autoUninitSpan.uninitDone())
795 return;
796
797 Assert(!isSnapshotMachine());
798 Assert(!isSessionMachine());
799 Assert(!!mData);
800
801 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
802 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
803
804 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
805
806 if (!mData->mSession.mMachine.isNull())
807 {
808 /* Theoretically, this can only happen if the VirtualBox server has been
809 * terminated while there were clients running that owned open direct
810 * sessions. Since in this case we are definitely called by
811 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
812 * won't happen on the client watcher thread (because it does
813 * VirtualBox::addCaller() for the duration of the
814 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
815 * cannot happen until the VirtualBox caller is released). This is
816 * important, because SessionMachine::uninit() cannot correctly operate
817 * after we return from this method (it expects the Machine instance is
818 * still valid). We'll call it ourselves below.
819 */
820 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
821 (SessionMachine*)mData->mSession.mMachine));
822
823 if (Global::IsOnlineOrTransient(mData->mMachineState))
824 {
825 LogWarningThisFunc(("Setting state to Aborted!\n"));
826 /* set machine state using SessionMachine reimplementation */
827 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
828 }
829
830 /*
831 * Uninitialize SessionMachine using public uninit() to indicate
832 * an unexpected uninitialization.
833 */
834 mData->mSession.mMachine->uninit();
835 /* SessionMachine::uninit() must set mSession.mMachine to null */
836 Assert(mData->mSession.mMachine.isNull());
837 }
838
839 // uninit media from this machine's media registry, if they're still there
840 Guid uuidMachine(getId());
841
842 /* XXX This will fail with
843 * "cannot be closed because it is still attached to 1 virtual machines"
844 * because at this point we did not call uninitDataAndChildObjects() yet
845 * and therefore also removeBackReference() for all these mediums was not called! */
846 if (!uuidMachine.isEmpty()) // can be empty if we're called from a failure of Machine::init
847 mParent->unregisterMachineMedia(uuidMachine);
848
849 /* the lock is no more necessary (SessionMachine is uninitialized) */
850 alock.release();
851
852 // has machine been modified?
853 if (mData->flModifications)
854 {
855 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
856 rollback(false /* aNotify */);
857 }
858
859 if (mData->mAccessible)
860 uninitDataAndChildObjects();
861
862 /* free the essential data structure last */
863 mData.free();
864
865 LogFlowThisFuncLeave();
866}
867
868// IMachine properties
869/////////////////////////////////////////////////////////////////////////////
870
871STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
872{
873 CheckComArgOutPointerValid(aParent);
874
875 AutoLimitedCaller autoCaller(this);
876 if (FAILED(autoCaller.rc())) return autoCaller.rc();
877
878 /* mParent is constant during life time, no need to lock */
879 ComObjPtr<VirtualBox> pVirtualBox(mParent);
880 pVirtualBox.queryInterfaceTo(aParent);
881
882 return S_OK;
883}
884
885STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
886{
887 CheckComArgOutPointerValid(aAccessible);
888
889 AutoLimitedCaller autoCaller(this);
890 if (FAILED(autoCaller.rc())) return autoCaller.rc();
891
892 LogFlowThisFunc(("ENTER\n"));
893
894 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
895
896 HRESULT rc = S_OK;
897
898 if (!mData->mAccessible)
899 {
900 /* try to initialize the VM once more if not accessible */
901
902 AutoReinitSpan autoReinitSpan(this);
903 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
904
905#ifdef DEBUG
906 LogFlowThisFunc(("Dumping media backreferences\n"));
907 mParent->dumpAllBackRefs();
908#endif
909
910 if (mData->pMachineConfigFile)
911 {
912 // reset the XML file to force loadSettings() (called from registeredInit())
913 // to parse it again; the file might have changed
914 delete mData->pMachineConfigFile;
915 mData->pMachineConfigFile = NULL;
916 }
917
918 rc = registeredInit();
919
920 if (SUCCEEDED(rc) && mData->mAccessible)
921 {
922 autoReinitSpan.setSucceeded();
923
924 /* make sure interesting parties will notice the accessibility
925 * state change */
926 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
927 mParent->onMachineDataChange(mData->mUuid);
928 }
929 }
930
931 if (SUCCEEDED(rc))
932 *aAccessible = mData->mAccessible;
933
934 LogFlowThisFuncLeave();
935
936 return rc;
937}
938
939STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
940{
941 CheckComArgOutPointerValid(aAccessError);
942
943 AutoLimitedCaller autoCaller(this);
944 if (FAILED(autoCaller.rc())) return autoCaller.rc();
945
946 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
947
948 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
949 {
950 /* return shortly */
951 aAccessError = NULL;
952 return S_OK;
953 }
954
955 HRESULT rc = S_OK;
956
957 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
958 rc = errorInfo.createObject();
959 if (SUCCEEDED(rc))
960 {
961 errorInfo->init(mData->mAccessError.getResultCode(),
962 mData->mAccessError.getInterfaceID().ref(),
963 Utf8Str(mData->mAccessError.getComponent()).c_str(),
964 Utf8Str(mData->mAccessError.getText()));
965 rc = errorInfo.queryInterfaceTo(aAccessError);
966 }
967
968 return rc;
969}
970
971STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
972{
973 CheckComArgOutPointerValid(aName);
974
975 AutoCaller autoCaller(this);
976 if (FAILED(autoCaller.rc())) return autoCaller.rc();
977
978 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
979
980 mUserData->s.strName.cloneTo(aName);
981
982 return S_OK;
983}
984
985STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
986{
987 CheckComArgStrNotEmptyOrNull(aName);
988
989 AutoCaller autoCaller(this);
990 if (FAILED(autoCaller.rc())) return autoCaller.rc();
991
992 // prohibit setting a UUID only as the machine name, or else it can
993 // never be found by findMachine()
994 Guid test(aName);
995 if (test.isNotEmpty())
996 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
997
998 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
999
1000 HRESULT rc = checkStateDependency(MutableStateDep);
1001 if (FAILED(rc)) return rc;
1002
1003 setModified(IsModified_MachineData);
1004 mUserData.backup();
1005 mUserData->s.strName = aName;
1006
1007 return S_OK;
1008}
1009
1010STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1011{
1012 CheckComArgOutPointerValid(aDescription);
1013
1014 AutoCaller autoCaller(this);
1015 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1016
1017 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1018
1019 mUserData->s.strDescription.cloneTo(aDescription);
1020
1021 return S_OK;
1022}
1023
1024STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1025{
1026 AutoCaller autoCaller(this);
1027 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1028
1029 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1030
1031 // this can be done in principle in any state as it doesn't affect the VM
1032 // significantly, but play safe by not messing around while complex
1033 // activities are going on
1034 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1035 if (FAILED(rc)) return rc;
1036
1037 setModified(IsModified_MachineData);
1038 mUserData.backup();
1039 mUserData->s.strDescription = aDescription;
1040
1041 return S_OK;
1042}
1043
1044STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1045{
1046 CheckComArgOutPointerValid(aId);
1047
1048 AutoLimitedCaller autoCaller(this);
1049 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1050
1051 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1052
1053 mData->mUuid.toUtf16().cloneTo(aId);
1054
1055 return S_OK;
1056}
1057
1058STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1059{
1060 CheckComArgOutSafeArrayPointerValid(aGroups);
1061
1062 AutoCaller autoCaller(this);
1063 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1064
1065 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1066 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1067 size_t i = 0;
1068 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1069 it != mUserData->s.llGroups.end();
1070 ++it, i++)
1071 {
1072 Bstr tmp = *it;
1073 tmp.cloneTo(&groups[i]);
1074 }
1075 groups.detachTo(ComSafeArrayOutArg(aGroups));
1076
1077 return S_OK;
1078}
1079
1080STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1081{
1082 AutoCaller autoCaller(this);
1083 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1084
1085 StringsList llGroups;
1086 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1087 if (FAILED(rc))
1088 return rc;
1089
1090 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1091
1092 // changing machine groups is possible while the VM is offline
1093 rc = checkStateDependency(OfflineStateDep);
1094 if (FAILED(rc)) return rc;
1095
1096 setModified(IsModified_MachineData);
1097 mUserData.backup();
1098 mUserData->s.llGroups = llGroups;
1099
1100 return S_OK;
1101}
1102
1103STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1104{
1105 CheckComArgOutPointerValid(aOSTypeId);
1106
1107 AutoCaller autoCaller(this);
1108 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1109
1110 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1111
1112 mUserData->s.strOsType.cloneTo(aOSTypeId);
1113
1114 return S_OK;
1115}
1116
1117STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1118{
1119 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1120
1121 AutoCaller autoCaller(this);
1122 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1123
1124 /* look up the object by Id to check it is valid */
1125 ComPtr<IGuestOSType> guestOSType;
1126 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1127 if (FAILED(rc)) return rc;
1128
1129 /* when setting, always use the "etalon" value for consistency -- lookup
1130 * by ID is case-insensitive and the input value may have different case */
1131 Bstr osTypeId;
1132 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1133 if (FAILED(rc)) return rc;
1134
1135 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1136
1137 rc = checkStateDependency(MutableStateDep);
1138 if (FAILED(rc)) return rc;
1139
1140 setModified(IsModified_MachineData);
1141 mUserData.backup();
1142 mUserData->s.strOsType = osTypeId;
1143
1144 return S_OK;
1145}
1146
1147
1148STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1149{
1150 CheckComArgOutPointerValid(aFirmwareType);
1151
1152 AutoCaller autoCaller(this);
1153 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1154
1155 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1156
1157 *aFirmwareType = mHWData->mFirmwareType;
1158
1159 return S_OK;
1160}
1161
1162STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1163{
1164 AutoCaller autoCaller(this);
1165 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1166 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1167
1168 HRESULT rc = checkStateDependency(MutableStateDep);
1169 if (FAILED(rc)) return rc;
1170
1171 setModified(IsModified_MachineData);
1172 mHWData.backup();
1173 mHWData->mFirmwareType = aFirmwareType;
1174
1175 return S_OK;
1176}
1177
1178STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1179{
1180 CheckComArgOutPointerValid(aKeyboardHIDType);
1181
1182 AutoCaller autoCaller(this);
1183 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1184
1185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1186
1187 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1188
1189 return S_OK;
1190}
1191
1192STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1193{
1194 AutoCaller autoCaller(this);
1195 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1196 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1197
1198 HRESULT rc = checkStateDependency(MutableStateDep);
1199 if (FAILED(rc)) return rc;
1200
1201 setModified(IsModified_MachineData);
1202 mHWData.backup();
1203 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1204
1205 return S_OK;
1206}
1207
1208STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1209{
1210 CheckComArgOutPointerValid(aPointingHIDType);
1211
1212 AutoCaller autoCaller(this);
1213 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1214
1215 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1216
1217 *aPointingHIDType = mHWData->mPointingHIDType;
1218
1219 return S_OK;
1220}
1221
1222STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1223{
1224 AutoCaller autoCaller(this);
1225 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1226 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1227
1228 HRESULT rc = checkStateDependency(MutableStateDep);
1229 if (FAILED(rc)) return rc;
1230
1231 setModified(IsModified_MachineData);
1232 mHWData.backup();
1233 mHWData->mPointingHIDType = aPointingHIDType;
1234
1235 return S_OK;
1236}
1237
1238STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1239{
1240 CheckComArgOutPointerValid(aChipsetType);
1241
1242 AutoCaller autoCaller(this);
1243 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1244
1245 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1246
1247 *aChipsetType = mHWData->mChipsetType;
1248
1249 return S_OK;
1250}
1251
1252STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1253{
1254 AutoCaller autoCaller(this);
1255 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1256 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1257
1258 HRESULT rc = checkStateDependency(MutableStateDep);
1259 if (FAILED(rc)) return rc;
1260
1261 if (aChipsetType != mHWData->mChipsetType)
1262 {
1263 setModified(IsModified_MachineData);
1264 mHWData.backup();
1265 mHWData->mChipsetType = aChipsetType;
1266
1267 // Resize network adapter array, to be finalized on commit/rollback.
1268 // We must not throw away entries yet, otherwise settings are lost
1269 // without a way to roll back.
1270 uint32_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1271 uint32_t oldCount = mNetworkAdapters.size();
1272 if (newCount > oldCount)
1273 {
1274 mNetworkAdapters.resize(newCount);
1275 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1276 {
1277 unconst(mNetworkAdapters[slot]).createObject();
1278 mNetworkAdapters[slot]->init(this, slot);
1279 }
1280 }
1281 }
1282
1283 return S_OK;
1284}
1285
1286STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1287{
1288 CheckComArgOutPointerValid(aHWVersion);
1289
1290 AutoCaller autoCaller(this);
1291 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1292
1293 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1294
1295 mHWData->mHWVersion.cloneTo(aHWVersion);
1296
1297 return S_OK;
1298}
1299
1300STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1301{
1302 /* check known version */
1303 Utf8Str hwVersion = aHWVersion;
1304 if ( hwVersion.compare("1") != 0
1305 && hwVersion.compare("2") != 0)
1306 return setError(E_INVALIDARG,
1307 tr("Invalid hardware version: %ls\n"), aHWVersion);
1308
1309 AutoCaller autoCaller(this);
1310 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1311
1312 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1313
1314 HRESULT rc = checkStateDependency(MutableStateDep);
1315 if (FAILED(rc)) return rc;
1316
1317 setModified(IsModified_MachineData);
1318 mHWData.backup();
1319 mHWData->mHWVersion = hwVersion;
1320
1321 return S_OK;
1322}
1323
1324STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1325{
1326 CheckComArgOutPointerValid(aUUID);
1327
1328 AutoCaller autoCaller(this);
1329 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1330
1331 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1332
1333 if (!mHWData->mHardwareUUID.isEmpty())
1334 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1335 else
1336 mData->mUuid.toUtf16().cloneTo(aUUID);
1337
1338 return S_OK;
1339}
1340
1341STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1342{
1343 Guid hardwareUUID(aUUID);
1344 if (hardwareUUID.isEmpty())
1345 return E_INVALIDARG;
1346
1347 AutoCaller autoCaller(this);
1348 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1349
1350 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1351
1352 HRESULT rc = checkStateDependency(MutableStateDep);
1353 if (FAILED(rc)) return rc;
1354
1355 setModified(IsModified_MachineData);
1356 mHWData.backup();
1357 if (hardwareUUID == mData->mUuid)
1358 mHWData->mHardwareUUID.clear();
1359 else
1360 mHWData->mHardwareUUID = hardwareUUID;
1361
1362 return S_OK;
1363}
1364
1365STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1366{
1367 CheckComArgOutPointerValid(memorySize);
1368
1369 AutoCaller autoCaller(this);
1370 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1371
1372 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1373
1374 *memorySize = mHWData->mMemorySize;
1375
1376 return S_OK;
1377}
1378
1379STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1380{
1381 /* check RAM limits */
1382 if ( memorySize < MM_RAM_MIN_IN_MB
1383 || memorySize > MM_RAM_MAX_IN_MB
1384 )
1385 return setError(E_INVALIDARG,
1386 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1387 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1388
1389 AutoCaller autoCaller(this);
1390 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1391
1392 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1393
1394 HRESULT rc = checkStateDependency(MutableStateDep);
1395 if (FAILED(rc)) return rc;
1396
1397 setModified(IsModified_MachineData);
1398 mHWData.backup();
1399 mHWData->mMemorySize = memorySize;
1400
1401 return S_OK;
1402}
1403
1404STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1405{
1406 CheckComArgOutPointerValid(CPUCount);
1407
1408 AutoCaller autoCaller(this);
1409 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1410
1411 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1412
1413 *CPUCount = mHWData->mCPUCount;
1414
1415 return S_OK;
1416}
1417
1418STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1419{
1420 /* check CPU limits */
1421 if ( CPUCount < SchemaDefs::MinCPUCount
1422 || CPUCount > SchemaDefs::MaxCPUCount
1423 )
1424 return setError(E_INVALIDARG,
1425 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1426 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1427
1428 AutoCaller autoCaller(this);
1429 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1430
1431 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1432
1433 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1434 if (mHWData->mCPUHotPlugEnabled)
1435 {
1436 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1437 {
1438 if (mHWData->mCPUAttached[idx])
1439 return setError(E_INVALIDARG,
1440 tr("There is still a CPU attached to socket %lu."
1441 "Detach the CPU before removing the socket"),
1442 CPUCount, idx+1);
1443 }
1444 }
1445
1446 HRESULT rc = checkStateDependency(MutableStateDep);
1447 if (FAILED(rc)) return rc;
1448
1449 setModified(IsModified_MachineData);
1450 mHWData.backup();
1451 mHWData->mCPUCount = CPUCount;
1452
1453 return S_OK;
1454}
1455
1456STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1457{
1458 CheckComArgOutPointerValid(aExecutionCap);
1459
1460 AutoCaller autoCaller(this);
1461 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1462
1463 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1464
1465 *aExecutionCap = mHWData->mCpuExecutionCap;
1466
1467 return S_OK;
1468}
1469
1470STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1471{
1472 HRESULT rc = S_OK;
1473
1474 /* check throttle limits */
1475 if ( aExecutionCap < 1
1476 || aExecutionCap > 100
1477 )
1478 return setError(E_INVALIDARG,
1479 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1480 aExecutionCap, 1, 100);
1481
1482 AutoCaller autoCaller(this);
1483 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1484
1485 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1486
1487 alock.release();
1488 rc = onCPUExecutionCapChange(aExecutionCap);
1489 alock.acquire();
1490 if (FAILED(rc)) return rc;
1491
1492 setModified(IsModified_MachineData);
1493 mHWData.backup();
1494 mHWData->mCpuExecutionCap = aExecutionCap;
1495
1496 /* Save settings if online - todo why is this required?? */
1497 if (Global::IsOnline(mData->mMachineState))
1498 saveSettings(NULL);
1499
1500 return S_OK;
1501}
1502
1503
1504STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *enabled)
1505{
1506 CheckComArgOutPointerValid(enabled);
1507
1508 AutoCaller autoCaller(this);
1509 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1510
1511 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1512
1513 *enabled = mHWData->mCPUHotPlugEnabled;
1514
1515 return S_OK;
1516}
1517
1518STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled)
1519{
1520 HRESULT rc = S_OK;
1521
1522 AutoCaller autoCaller(this);
1523 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1524
1525 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1526
1527 rc = checkStateDependency(MutableStateDep);
1528 if (FAILED(rc)) return rc;
1529
1530 if (mHWData->mCPUHotPlugEnabled != enabled)
1531 {
1532 if (enabled)
1533 {
1534 setModified(IsModified_MachineData);
1535 mHWData.backup();
1536
1537 /* Add the amount of CPUs currently attached */
1538 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1539 {
1540 mHWData->mCPUAttached[i] = true;
1541 }
1542 }
1543 else
1544 {
1545 /*
1546 * We can disable hotplug only if the amount of maximum CPUs is equal
1547 * to the amount of attached CPUs
1548 */
1549 unsigned cCpusAttached = 0;
1550 unsigned iHighestId = 0;
1551
1552 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1553 {
1554 if (mHWData->mCPUAttached[i])
1555 {
1556 cCpusAttached++;
1557 iHighestId = i;
1558 }
1559 }
1560
1561 if ( (cCpusAttached != mHWData->mCPUCount)
1562 || (iHighestId >= mHWData->mCPUCount))
1563 return setError(E_INVALIDARG,
1564 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1565
1566 setModified(IsModified_MachineData);
1567 mHWData.backup();
1568 }
1569 }
1570
1571 mHWData->mCPUHotPlugEnabled = enabled;
1572
1573 return rc;
1574}
1575
1576STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *enabled)
1577{
1578#ifdef VBOX_WITH_USB_CARDREADER
1579 CheckComArgOutPointerValid(enabled);
1580
1581 AutoCaller autoCaller(this);
1582 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1583
1584 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1585
1586 *enabled = mHWData->mEmulatedUSBCardReaderEnabled;
1587
1588 return S_OK;
1589#else
1590 NOREF(enabled);
1591 return E_NOTIMPL;
1592#endif
1593}
1594
1595STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL enabled)
1596{
1597#ifdef VBOX_WITH_USB_CARDREADER
1598 AutoCaller autoCaller(this);
1599 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1600 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1601
1602 HRESULT rc = checkStateDependency(MutableStateDep);
1603 if (FAILED(rc)) return rc;
1604
1605 setModified(IsModified_MachineData);
1606 mHWData.backup();
1607 mHWData->mEmulatedUSBCardReaderEnabled = enabled;
1608
1609 return S_OK;
1610#else
1611 NOREF(enabled);
1612 return E_NOTIMPL;
1613#endif
1614}
1615
1616STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *enabled)
1617{
1618 NOREF(enabled);
1619 return E_NOTIMPL;
1620}
1621
1622STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL enabled)
1623{
1624 NOREF(enabled);
1625 return E_NOTIMPL;
1626}
1627
1628STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *enabled)
1629{
1630 CheckComArgOutPointerValid(enabled);
1631
1632 AutoCaller autoCaller(this);
1633 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1634 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1635
1636 *enabled = mHWData->mHPETEnabled;
1637
1638 return S_OK;
1639}
1640
1641STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL enabled)
1642{
1643 HRESULT rc = S_OK;
1644
1645 AutoCaller autoCaller(this);
1646 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1647 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1648
1649 rc = checkStateDependency(MutableStateDep);
1650 if (FAILED(rc)) return rc;
1651
1652 setModified(IsModified_MachineData);
1653 mHWData.backup();
1654
1655 mHWData->mHPETEnabled = enabled;
1656
1657 return rc;
1658}
1659
1660STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1661{
1662 CheckComArgOutPointerValid(memorySize);
1663
1664 AutoCaller autoCaller(this);
1665 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1666
1667 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1668
1669 *memorySize = mHWData->mVRAMSize;
1670
1671 return S_OK;
1672}
1673
1674STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1675{
1676 /* check VRAM limits */
1677 if (memorySize < SchemaDefs::MinGuestVRAM ||
1678 memorySize > SchemaDefs::MaxGuestVRAM)
1679 return setError(E_INVALIDARG,
1680 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1681 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1682
1683 AutoCaller autoCaller(this);
1684 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1685
1686 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1687
1688 HRESULT rc = checkStateDependency(MutableStateDep);
1689 if (FAILED(rc)) return rc;
1690
1691 setModified(IsModified_MachineData);
1692 mHWData.backup();
1693 mHWData->mVRAMSize = memorySize;
1694
1695 return S_OK;
1696}
1697
1698/** @todo this method should not be public */
1699STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1700{
1701 CheckComArgOutPointerValid(memoryBalloonSize);
1702
1703 AutoCaller autoCaller(this);
1704 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1705
1706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1707
1708 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
1709
1710 return S_OK;
1711}
1712
1713/**
1714 * Set the memory balloon size.
1715 *
1716 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1717 * we have to make sure that we never call IGuest from here.
1718 */
1719STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
1720{
1721 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1722#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1723 /* check limits */
1724 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1725 return setError(E_INVALIDARG,
1726 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1727 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1728
1729 AutoCaller autoCaller(this);
1730 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1731
1732 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1733
1734 setModified(IsModified_MachineData);
1735 mHWData.backup();
1736 mHWData->mMemoryBalloonSize = memoryBalloonSize;
1737
1738 return S_OK;
1739#else
1740 NOREF(memoryBalloonSize);
1741 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1742#endif
1743}
1744
1745STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *enabled)
1746{
1747 CheckComArgOutPointerValid(enabled);
1748
1749 AutoCaller autoCaller(this);
1750 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1751
1752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1753
1754 *enabled = mHWData->mPageFusionEnabled;
1755 return S_OK;
1756}
1757
1758STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL enabled)
1759{
1760#ifdef VBOX_WITH_PAGE_SHARING
1761 AutoCaller autoCaller(this);
1762 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1763
1764 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1765
1766 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1767 setModified(IsModified_MachineData);
1768 mHWData.backup();
1769 mHWData->mPageFusionEnabled = enabled;
1770 return S_OK;
1771#else
1772 NOREF(enabled);
1773 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1774#endif
1775}
1776
1777STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *enabled)
1778{
1779 CheckComArgOutPointerValid(enabled);
1780
1781 AutoCaller autoCaller(this);
1782 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1783
1784 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1785
1786 *enabled = mHWData->mAccelerate3DEnabled;
1787
1788 return S_OK;
1789}
1790
1791STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
1792{
1793 AutoCaller autoCaller(this);
1794 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1795
1796 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1797
1798 HRESULT rc = checkStateDependency(MutableStateDep);
1799 if (FAILED(rc)) return rc;
1800
1801 /** @todo check validity! */
1802
1803 setModified(IsModified_MachineData);
1804 mHWData.backup();
1805 mHWData->mAccelerate3DEnabled = enable;
1806
1807 return S_OK;
1808}
1809
1810
1811STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *enabled)
1812{
1813 CheckComArgOutPointerValid(enabled);
1814
1815 AutoCaller autoCaller(this);
1816 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1817
1818 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1819
1820 *enabled = mHWData->mAccelerate2DVideoEnabled;
1821
1822 return S_OK;
1823}
1824
1825STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
1826{
1827 AutoCaller autoCaller(this);
1828 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1829
1830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1831
1832 HRESULT rc = checkStateDependency(MutableStateDep);
1833 if (FAILED(rc)) return rc;
1834
1835 /** @todo check validity! */
1836
1837 setModified(IsModified_MachineData);
1838 mHWData.backup();
1839 mHWData->mAccelerate2DVideoEnabled = enable;
1840
1841 return S_OK;
1842}
1843
1844STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
1845{
1846 CheckComArgOutPointerValid(monitorCount);
1847
1848 AutoCaller autoCaller(this);
1849 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1850
1851 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1852
1853 *monitorCount = mHWData->mMonitorCount;
1854
1855 return S_OK;
1856}
1857
1858STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
1859{
1860 /* make sure monitor count is a sensible number */
1861 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
1862 return setError(E_INVALIDARG,
1863 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1864 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
1865
1866 AutoCaller autoCaller(this);
1867 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1868
1869 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1870
1871 HRESULT rc = checkStateDependency(MutableStateDep);
1872 if (FAILED(rc)) return rc;
1873
1874 setModified(IsModified_MachineData);
1875 mHWData.backup();
1876 mHWData->mMonitorCount = monitorCount;
1877
1878 return S_OK;
1879}
1880
1881STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
1882{
1883 CheckComArgOutPointerValid(biosSettings);
1884
1885 AutoCaller autoCaller(this);
1886 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1887
1888 /* mBIOSSettings is constant during life time, no need to lock */
1889 mBIOSSettings.queryInterfaceTo(biosSettings);
1890
1891 return S_OK;
1892}
1893
1894STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
1895{
1896 CheckComArgOutPointerValid(aVal);
1897
1898 AutoCaller autoCaller(this);
1899 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1900
1901 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1902
1903 switch(property)
1904 {
1905 case CPUPropertyType_PAE:
1906 *aVal = mHWData->mPAEEnabled;
1907 break;
1908
1909 case CPUPropertyType_Synthetic:
1910 *aVal = mHWData->mSyntheticCpu;
1911 break;
1912
1913 default:
1914 return E_INVALIDARG;
1915 }
1916 return S_OK;
1917}
1918
1919STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
1920{
1921 AutoCaller autoCaller(this);
1922 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1923
1924 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1925
1926 HRESULT rc = checkStateDependency(MutableStateDep);
1927 if (FAILED(rc)) return rc;
1928
1929 switch(property)
1930 {
1931 case CPUPropertyType_PAE:
1932 setModified(IsModified_MachineData);
1933 mHWData.backup();
1934 mHWData->mPAEEnabled = !!aVal;
1935 break;
1936
1937 case CPUPropertyType_Synthetic:
1938 setModified(IsModified_MachineData);
1939 mHWData.backup();
1940 mHWData->mSyntheticCpu = !!aVal;
1941 break;
1942
1943 default:
1944 return E_INVALIDARG;
1945 }
1946 return S_OK;
1947}
1948
1949STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
1950{
1951 CheckComArgOutPointerValid(aValEax);
1952 CheckComArgOutPointerValid(aValEbx);
1953 CheckComArgOutPointerValid(aValEcx);
1954 CheckComArgOutPointerValid(aValEdx);
1955
1956 AutoCaller autoCaller(this);
1957 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1958
1959 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1960
1961 switch(aId)
1962 {
1963 case 0x0:
1964 case 0x1:
1965 case 0x2:
1966 case 0x3:
1967 case 0x4:
1968 case 0x5:
1969 case 0x6:
1970 case 0x7:
1971 case 0x8:
1972 case 0x9:
1973 case 0xA:
1974 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
1975 return E_INVALIDARG;
1976
1977 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
1978 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
1979 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
1980 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
1981 break;
1982
1983 case 0x80000000:
1984 case 0x80000001:
1985 case 0x80000002:
1986 case 0x80000003:
1987 case 0x80000004:
1988 case 0x80000005:
1989 case 0x80000006:
1990 case 0x80000007:
1991 case 0x80000008:
1992 case 0x80000009:
1993 case 0x8000000A:
1994 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
1995 return E_INVALIDARG;
1996
1997 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
1998 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
1999 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2000 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2001 break;
2002
2003 default:
2004 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2005 }
2006 return S_OK;
2007}
2008
2009STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2010{
2011 AutoCaller autoCaller(this);
2012 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2013
2014 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2015
2016 HRESULT rc = checkStateDependency(MutableStateDep);
2017 if (FAILED(rc)) return rc;
2018
2019 switch(aId)
2020 {
2021 case 0x0:
2022 case 0x1:
2023 case 0x2:
2024 case 0x3:
2025 case 0x4:
2026 case 0x5:
2027 case 0x6:
2028 case 0x7:
2029 case 0x8:
2030 case 0x9:
2031 case 0xA:
2032 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2033 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2034 setModified(IsModified_MachineData);
2035 mHWData.backup();
2036 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2037 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2038 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2039 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2040 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2041 break;
2042
2043 case 0x80000000:
2044 case 0x80000001:
2045 case 0x80000002:
2046 case 0x80000003:
2047 case 0x80000004:
2048 case 0x80000005:
2049 case 0x80000006:
2050 case 0x80000007:
2051 case 0x80000008:
2052 case 0x80000009:
2053 case 0x8000000A:
2054 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2055 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2056 setModified(IsModified_MachineData);
2057 mHWData.backup();
2058 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2059 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2060 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2061 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2062 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2063 break;
2064
2065 default:
2066 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2067 }
2068 return S_OK;
2069}
2070
2071STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2072{
2073 AutoCaller autoCaller(this);
2074 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2075
2076 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2077
2078 HRESULT rc = checkStateDependency(MutableStateDep);
2079 if (FAILED(rc)) return rc;
2080
2081 switch(aId)
2082 {
2083 case 0x0:
2084 case 0x1:
2085 case 0x2:
2086 case 0x3:
2087 case 0x4:
2088 case 0x5:
2089 case 0x6:
2090 case 0x7:
2091 case 0x8:
2092 case 0x9:
2093 case 0xA:
2094 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2095 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2096 setModified(IsModified_MachineData);
2097 mHWData.backup();
2098 /* Invalidate leaf. */
2099 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2100 break;
2101
2102 case 0x80000000:
2103 case 0x80000001:
2104 case 0x80000002:
2105 case 0x80000003:
2106 case 0x80000004:
2107 case 0x80000005:
2108 case 0x80000006:
2109 case 0x80000007:
2110 case 0x80000008:
2111 case 0x80000009:
2112 case 0x8000000A:
2113 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2114 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2115 setModified(IsModified_MachineData);
2116 mHWData.backup();
2117 /* Invalidate leaf. */
2118 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2119 break;
2120
2121 default:
2122 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2123 }
2124 return S_OK;
2125}
2126
2127STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2128{
2129 AutoCaller autoCaller(this);
2130 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2131
2132 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2133
2134 HRESULT rc = checkStateDependency(MutableStateDep);
2135 if (FAILED(rc)) return rc;
2136
2137 setModified(IsModified_MachineData);
2138 mHWData.backup();
2139
2140 /* Invalidate all standard leafs. */
2141 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2142 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2143
2144 /* Invalidate all extended leafs. */
2145 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2146 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2147
2148 return S_OK;
2149}
2150
2151STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2152{
2153 CheckComArgOutPointerValid(aVal);
2154
2155 AutoCaller autoCaller(this);
2156 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2157
2158 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2159
2160 switch(property)
2161 {
2162 case HWVirtExPropertyType_Enabled:
2163 *aVal = mHWData->mHWVirtExEnabled;
2164 break;
2165
2166 case HWVirtExPropertyType_Exclusive:
2167 *aVal = mHWData->mHWVirtExExclusive;
2168 break;
2169
2170 case HWVirtExPropertyType_VPID:
2171 *aVal = mHWData->mHWVirtExVPIDEnabled;
2172 break;
2173
2174 case HWVirtExPropertyType_NestedPaging:
2175 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2176 break;
2177
2178 case HWVirtExPropertyType_LargePages:
2179 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2180#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2181 *aVal = FALSE;
2182#endif
2183 break;
2184
2185 case HWVirtExPropertyType_Force:
2186 *aVal = mHWData->mHWVirtExForceEnabled;
2187 break;
2188
2189 default:
2190 return E_INVALIDARG;
2191 }
2192 return S_OK;
2193}
2194
2195STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2196{
2197 AutoCaller autoCaller(this);
2198 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2199
2200 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2201
2202 HRESULT rc = checkStateDependency(MutableStateDep);
2203 if (FAILED(rc)) return rc;
2204
2205 switch(property)
2206 {
2207 case HWVirtExPropertyType_Enabled:
2208 setModified(IsModified_MachineData);
2209 mHWData.backup();
2210 mHWData->mHWVirtExEnabled = !!aVal;
2211 break;
2212
2213 case HWVirtExPropertyType_Exclusive:
2214 setModified(IsModified_MachineData);
2215 mHWData.backup();
2216 mHWData->mHWVirtExExclusive = !!aVal;
2217 break;
2218
2219 case HWVirtExPropertyType_VPID:
2220 setModified(IsModified_MachineData);
2221 mHWData.backup();
2222 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2223 break;
2224
2225 case HWVirtExPropertyType_NestedPaging:
2226 setModified(IsModified_MachineData);
2227 mHWData.backup();
2228 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2229 break;
2230
2231 case HWVirtExPropertyType_LargePages:
2232 setModified(IsModified_MachineData);
2233 mHWData.backup();
2234 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2235 break;
2236
2237 case HWVirtExPropertyType_Force:
2238 setModified(IsModified_MachineData);
2239 mHWData.backup();
2240 mHWData->mHWVirtExForceEnabled = !!aVal;
2241 break;
2242
2243 default:
2244 return E_INVALIDARG;
2245 }
2246
2247 return S_OK;
2248}
2249
2250STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2251{
2252 CheckComArgOutPointerValid(aSnapshotFolder);
2253
2254 AutoCaller autoCaller(this);
2255 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2256
2257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2258
2259 Utf8Str strFullSnapshotFolder;
2260 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2261 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2262
2263 return S_OK;
2264}
2265
2266STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2267{
2268 /* @todo (r=dmik):
2269 * 1. Allow to change the name of the snapshot folder containing snapshots
2270 * 2. Rename the folder on disk instead of just changing the property
2271 * value (to be smart and not to leave garbage). Note that it cannot be
2272 * done here because the change may be rolled back. Thus, the right
2273 * place is #saveSettings().
2274 */
2275
2276 AutoCaller autoCaller(this);
2277 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2278
2279 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2280
2281 HRESULT rc = checkStateDependency(MutableStateDep);
2282 if (FAILED(rc)) return rc;
2283
2284 if (!mData->mCurrentSnapshot.isNull())
2285 return setError(E_FAIL,
2286 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2287
2288 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2289
2290 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2291 if (strSnapshotFolder.isEmpty())
2292 strSnapshotFolder = "Snapshots";
2293 int vrc = calculateFullPath(strSnapshotFolder,
2294 strSnapshotFolder);
2295 if (RT_FAILURE(vrc))
2296 return setError(E_FAIL,
2297 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2298 aSnapshotFolder, vrc);
2299
2300 setModified(IsModified_MachineData);
2301 mUserData.backup();
2302
2303 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2304
2305 return S_OK;
2306}
2307
2308STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2309{
2310 CheckComArgOutSafeArrayPointerValid(aAttachments);
2311
2312 AutoCaller autoCaller(this);
2313 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2314
2315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2316
2317 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2318 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2319
2320 return S_OK;
2321}
2322
2323STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2324{
2325 CheckComArgOutPointerValid(vrdeServer);
2326
2327 AutoCaller autoCaller(this);
2328 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2329
2330 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2331
2332 Assert(!!mVRDEServer);
2333 mVRDEServer.queryInterfaceTo(vrdeServer);
2334
2335 return S_OK;
2336}
2337
2338STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2339{
2340 CheckComArgOutPointerValid(audioAdapter);
2341
2342 AutoCaller autoCaller(this);
2343 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2344
2345 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2346
2347 mAudioAdapter.queryInterfaceTo(audioAdapter);
2348 return S_OK;
2349}
2350
2351STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2352{
2353#ifdef VBOX_WITH_VUSB
2354 CheckComArgOutPointerValid(aUSBController);
2355
2356 AutoCaller autoCaller(this);
2357 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2358
2359 clearError();
2360 MultiResult rc(S_OK);
2361
2362# ifdef VBOX_WITH_USB
2363 rc = mParent->host()->checkUSBProxyService();
2364 if (FAILED(rc)) return rc;
2365# endif
2366
2367 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2368
2369 return rc = mUSBController.queryInterfaceTo(aUSBController);
2370#else
2371 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2372 * extended error info to indicate that USB is simply not available
2373 * (w/o treating it as a failure), for example, as in OSE */
2374 NOREF(aUSBController);
2375 ReturnComNotImplemented();
2376#endif /* VBOX_WITH_VUSB */
2377}
2378
2379STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2380{
2381 CheckComArgOutPointerValid(aFilePath);
2382
2383 AutoLimitedCaller autoCaller(this);
2384 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2385
2386 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2387
2388 mData->m_strConfigFileFull.cloneTo(aFilePath);
2389 return S_OK;
2390}
2391
2392STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2393{
2394 CheckComArgOutPointerValid(aModified);
2395
2396 AutoCaller autoCaller(this);
2397 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2398
2399 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2400
2401 HRESULT rc = checkStateDependency(MutableStateDep);
2402 if (FAILED(rc)) return rc;
2403
2404 if (!mData->pMachineConfigFile->fileExists())
2405 // this is a new machine, and no config file exists yet:
2406 *aModified = TRUE;
2407 else
2408 *aModified = (mData->flModifications != 0);
2409
2410 return S_OK;
2411}
2412
2413STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2414{
2415 CheckComArgOutPointerValid(aSessionState);
2416
2417 AutoCaller autoCaller(this);
2418 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2419
2420 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2421
2422 *aSessionState = mData->mSession.mState;
2423
2424 return S_OK;
2425}
2426
2427STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2428{
2429 CheckComArgOutPointerValid(aSessionType);
2430
2431 AutoCaller autoCaller(this);
2432 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2433
2434 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2435
2436 mData->mSession.mType.cloneTo(aSessionType);
2437
2438 return S_OK;
2439}
2440
2441STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2442{
2443 CheckComArgOutPointerValid(aSessionPID);
2444
2445 AutoCaller autoCaller(this);
2446 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2447
2448 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2449
2450 *aSessionPID = mData->mSession.mPID;
2451
2452 return S_OK;
2453}
2454
2455STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2456{
2457 CheckComArgOutPointerValid(machineState);
2458
2459 AutoCaller autoCaller(this);
2460 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2461
2462 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2463
2464 *machineState = mData->mMachineState;
2465
2466 return S_OK;
2467}
2468
2469STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2470{
2471 CheckComArgOutPointerValid(aLastStateChange);
2472
2473 AutoCaller autoCaller(this);
2474 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2475
2476 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2477
2478 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2479
2480 return S_OK;
2481}
2482
2483STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2484{
2485 CheckComArgOutPointerValid(aStateFilePath);
2486
2487 AutoCaller autoCaller(this);
2488 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2489
2490 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2491
2492 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2493
2494 return S_OK;
2495}
2496
2497STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2498{
2499 CheckComArgOutPointerValid(aLogFolder);
2500
2501 AutoCaller autoCaller(this);
2502 AssertComRCReturnRC(autoCaller.rc());
2503
2504 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2505
2506 Utf8Str logFolder;
2507 getLogFolder(logFolder);
2508 logFolder.cloneTo(aLogFolder);
2509
2510 return S_OK;
2511}
2512
2513STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2514{
2515 CheckComArgOutPointerValid(aCurrentSnapshot);
2516
2517 AutoCaller autoCaller(this);
2518 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2519
2520 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2521
2522 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2523
2524 return S_OK;
2525}
2526
2527STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2528{
2529 CheckComArgOutPointerValid(aSnapshotCount);
2530
2531 AutoCaller autoCaller(this);
2532 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2533
2534 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2535
2536 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2537 ? 0
2538 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2539
2540 return S_OK;
2541}
2542
2543STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2544{
2545 CheckComArgOutPointerValid(aCurrentStateModified);
2546
2547 AutoCaller autoCaller(this);
2548 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2549
2550 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2551
2552 /* Note: for machines with no snapshots, we always return FALSE
2553 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2554 * reasons :) */
2555
2556 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2557 ? FALSE
2558 : mData->mCurrentStateModified;
2559
2560 return S_OK;
2561}
2562
2563STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2564{
2565 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2566
2567 AutoCaller autoCaller(this);
2568 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2569
2570 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2571
2572 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2573 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2574
2575 return S_OK;
2576}
2577
2578STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2579{
2580 CheckComArgOutPointerValid(aClipboardMode);
2581
2582 AutoCaller autoCaller(this);
2583 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2584
2585 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2586
2587 *aClipboardMode = mHWData->mClipboardMode;
2588
2589 return S_OK;
2590}
2591
2592STDMETHODIMP
2593Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2594{
2595 HRESULT rc = S_OK;
2596
2597 AutoCaller autoCaller(this);
2598 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2599
2600 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2601
2602 alock.release();
2603 rc = onClipboardModeChange(aClipboardMode);
2604 alock.acquire();
2605 if (FAILED(rc)) return rc;
2606
2607 setModified(IsModified_MachineData);
2608 mHWData.backup();
2609 mHWData->mClipboardMode = aClipboardMode;
2610
2611 /* Save settings if online - todo why is this required?? */
2612 if (Global::IsOnline(mData->mMachineState))
2613 saveSettings(NULL);
2614
2615 return S_OK;
2616}
2617
2618STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
2619{
2620 CheckComArgOutPointerValid(aDragAndDropMode);
2621
2622 AutoCaller autoCaller(this);
2623 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2624
2625 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2626
2627 *aDragAndDropMode = mHWData->mDragAndDropMode;
2628
2629 return S_OK;
2630}
2631
2632STDMETHODIMP
2633Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
2634{
2635 HRESULT rc = S_OK;
2636
2637 AutoCaller autoCaller(this);
2638 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2639
2640 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2641
2642 alock.release();
2643 rc = onDragAndDropModeChange(aDragAndDropMode);
2644 alock.acquire();
2645 if (FAILED(rc)) return rc;
2646
2647 setModified(IsModified_MachineData);
2648 mHWData.backup();
2649 mHWData->mDragAndDropMode = aDragAndDropMode;
2650
2651 /* Save settings if online - todo why is this required?? */
2652 if (Global::IsOnline(mData->mMachineState))
2653 saveSettings(NULL);
2654
2655 return S_OK;
2656}
2657
2658STDMETHODIMP
2659Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
2660{
2661 CheckComArgOutPointerValid(aPatterns);
2662
2663 AutoCaller autoCaller(this);
2664 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2665
2666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2667
2668 try
2669 {
2670 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
2671 }
2672 catch (...)
2673 {
2674 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2675 }
2676
2677 return S_OK;
2678}
2679
2680STDMETHODIMP
2681Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
2682{
2683 AutoCaller autoCaller(this);
2684 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2685
2686 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2687
2688 HRESULT rc = checkStateDependency(MutableStateDep);
2689 if (FAILED(rc)) return rc;
2690
2691 setModified(IsModified_MachineData);
2692 mHWData.backup();
2693 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
2694 return rc;
2695}
2696
2697STDMETHODIMP
2698Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
2699{
2700 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
2701
2702 AutoCaller autoCaller(this);
2703 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2704
2705 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2706
2707 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
2708 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
2709
2710 return S_OK;
2711}
2712
2713STDMETHODIMP
2714Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
2715{
2716 CheckComArgOutPointerValid(aEnabled);
2717
2718 AutoCaller autoCaller(this);
2719 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2720
2721 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2722
2723 *aEnabled = mUserData->s.fTeleporterEnabled;
2724
2725 return S_OK;
2726}
2727
2728STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
2729{
2730 AutoCaller autoCaller(this);
2731 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2732
2733 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2734
2735 /* Only allow it to be set to true when PoweredOff or Aborted.
2736 (Clearing it is always permitted.) */
2737 if ( aEnabled
2738 && mData->mRegistered
2739 && ( !isSessionMachine()
2740 || ( mData->mMachineState != MachineState_PoweredOff
2741 && mData->mMachineState != MachineState_Teleported
2742 && mData->mMachineState != MachineState_Aborted
2743 )
2744 )
2745 )
2746 return setError(VBOX_E_INVALID_VM_STATE,
2747 tr("The machine is not powered off (state is %s)"),
2748 Global::stringifyMachineState(mData->mMachineState));
2749
2750 setModified(IsModified_MachineData);
2751 mUserData.backup();
2752 mUserData->s.fTeleporterEnabled = !!aEnabled;
2753
2754 return S_OK;
2755}
2756
2757STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
2758{
2759 CheckComArgOutPointerValid(aPort);
2760
2761 AutoCaller autoCaller(this);
2762 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2763
2764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2765
2766 *aPort = (ULONG)mUserData->s.uTeleporterPort;
2767
2768 return S_OK;
2769}
2770
2771STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
2772{
2773 if (aPort >= _64K)
2774 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
2775
2776 AutoCaller autoCaller(this);
2777 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2778
2779 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2780
2781 HRESULT rc = checkStateDependency(MutableStateDep);
2782 if (FAILED(rc)) return rc;
2783
2784 setModified(IsModified_MachineData);
2785 mUserData.backup();
2786 mUserData->s.uTeleporterPort = (uint32_t)aPort;
2787
2788 return S_OK;
2789}
2790
2791STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
2792{
2793 CheckComArgOutPointerValid(aAddress);
2794
2795 AutoCaller autoCaller(this);
2796 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2797
2798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2799
2800 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
2801
2802 return S_OK;
2803}
2804
2805STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
2806{
2807 AutoCaller autoCaller(this);
2808 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2809
2810 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2811
2812 HRESULT rc = checkStateDependency(MutableStateDep);
2813 if (FAILED(rc)) return rc;
2814
2815 setModified(IsModified_MachineData);
2816 mUserData.backup();
2817 mUserData->s.strTeleporterAddress = aAddress;
2818
2819 return S_OK;
2820}
2821
2822STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
2823{
2824 CheckComArgOutPointerValid(aPassword);
2825
2826 AutoCaller autoCaller(this);
2827 HRESULT hrc = autoCaller.rc();
2828 if (SUCCEEDED(hrc))
2829 {
2830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2831 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
2832 }
2833
2834 return hrc;
2835}
2836
2837STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
2838{
2839 /*
2840 * Hash the password first.
2841 */
2842 Utf8Str strPassword(aPassword);
2843 if (!strPassword.isEmpty())
2844 {
2845 if (VBoxIsPasswordHashed(&strPassword))
2846 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2847 VBoxHashPassword(&strPassword);
2848 }
2849
2850 /*
2851 * Do the update.
2852 */
2853 AutoCaller autoCaller(this);
2854 HRESULT hrc = autoCaller.rc();
2855 if (SUCCEEDED(hrc))
2856 {
2857 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2858 hrc = checkStateDependency(MutableStateDep);
2859 if (SUCCEEDED(hrc))
2860 {
2861 setModified(IsModified_MachineData);
2862 mUserData.backup();
2863 mUserData->s.strTeleporterPassword = strPassword;
2864 }
2865 }
2866
2867 return hrc;
2868}
2869
2870STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
2871{
2872 CheckComArgOutPointerValid(aState);
2873
2874 AutoCaller autoCaller(this);
2875 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2876
2877 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2878
2879 *aState = mUserData->s.enmFaultToleranceState;
2880 return S_OK;
2881}
2882
2883STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
2884{
2885 AutoCaller autoCaller(this);
2886 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2887
2888 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2889
2890 /* @todo deal with running state change. */
2891 HRESULT rc = checkStateDependency(MutableStateDep);
2892 if (FAILED(rc)) return rc;
2893
2894 setModified(IsModified_MachineData);
2895 mUserData.backup();
2896 mUserData->s.enmFaultToleranceState = aState;
2897 return S_OK;
2898}
2899
2900STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
2901{
2902 CheckComArgOutPointerValid(aAddress);
2903
2904 AutoCaller autoCaller(this);
2905 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2906
2907 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2908
2909 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
2910 return S_OK;
2911}
2912
2913STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
2914{
2915 AutoCaller autoCaller(this);
2916 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2917
2918 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2919
2920 /* @todo deal with running state change. */
2921 HRESULT rc = checkStateDependency(MutableStateDep);
2922 if (FAILED(rc)) return rc;
2923
2924 setModified(IsModified_MachineData);
2925 mUserData.backup();
2926 mUserData->s.strFaultToleranceAddress = aAddress;
2927 return S_OK;
2928}
2929
2930STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
2931{
2932 CheckComArgOutPointerValid(aPort);
2933
2934 AutoCaller autoCaller(this);
2935 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2936
2937 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2938
2939 *aPort = mUserData->s.uFaultTolerancePort;
2940 return S_OK;
2941}
2942
2943STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
2944{
2945 AutoCaller autoCaller(this);
2946 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2947
2948 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2949
2950 /* @todo deal with running state change. */
2951 HRESULT rc = checkStateDependency(MutableStateDep);
2952 if (FAILED(rc)) return rc;
2953
2954 setModified(IsModified_MachineData);
2955 mUserData.backup();
2956 mUserData->s.uFaultTolerancePort = aPort;
2957 return S_OK;
2958}
2959
2960STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
2961{
2962 CheckComArgOutPointerValid(aPassword);
2963
2964 AutoCaller autoCaller(this);
2965 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2966
2967 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2968
2969 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
2970
2971 return S_OK;
2972}
2973
2974STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
2975{
2976 AutoCaller autoCaller(this);
2977 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2978
2979 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2980
2981 /* @todo deal with running state change. */
2982 HRESULT rc = checkStateDependency(MutableStateDep);
2983 if (FAILED(rc)) return rc;
2984
2985 setModified(IsModified_MachineData);
2986 mUserData.backup();
2987 mUserData->s.strFaultTolerancePassword = aPassword;
2988
2989 return S_OK;
2990}
2991
2992STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
2993{
2994 CheckComArgOutPointerValid(aInterval);
2995
2996 AutoCaller autoCaller(this);
2997 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2998
2999 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3000
3001 *aInterval = mUserData->s.uFaultToleranceInterval;
3002 return S_OK;
3003}
3004
3005STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3006{
3007 AutoCaller autoCaller(this);
3008 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3009
3010 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3011
3012 /* @todo deal with running state change. */
3013 HRESULT rc = checkStateDependency(MutableStateDep);
3014 if (FAILED(rc)) return rc;
3015
3016 setModified(IsModified_MachineData);
3017 mUserData.backup();
3018 mUserData->s.uFaultToleranceInterval = aInterval;
3019 return S_OK;
3020}
3021
3022STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3023{
3024 CheckComArgOutPointerValid(aEnabled);
3025
3026 AutoCaller autoCaller(this);
3027 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3028
3029 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3030
3031 *aEnabled = mUserData->s.fRTCUseUTC;
3032
3033 return S_OK;
3034}
3035
3036STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3037{
3038 AutoCaller autoCaller(this);
3039 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3040
3041 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3042
3043 /* Only allow it to be set to true when PoweredOff or Aborted.
3044 (Clearing it is always permitted.) */
3045 if ( aEnabled
3046 && mData->mRegistered
3047 && ( !isSessionMachine()
3048 || ( mData->mMachineState != MachineState_PoweredOff
3049 && mData->mMachineState != MachineState_Teleported
3050 && mData->mMachineState != MachineState_Aborted
3051 )
3052 )
3053 )
3054 return setError(VBOX_E_INVALID_VM_STATE,
3055 tr("The machine is not powered off (state is %s)"),
3056 Global::stringifyMachineState(mData->mMachineState));
3057
3058 setModified(IsModified_MachineData);
3059 mUserData.backup();
3060 mUserData->s.fRTCUseUTC = !!aEnabled;
3061
3062 return S_OK;
3063}
3064
3065STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3066{
3067 CheckComArgOutPointerValid(aEnabled);
3068
3069 AutoCaller autoCaller(this);
3070 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3071
3072 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3073
3074 *aEnabled = mHWData->mIOCacheEnabled;
3075
3076 return S_OK;
3077}
3078
3079STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3080{
3081 AutoCaller autoCaller(this);
3082 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3083
3084 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3085
3086 HRESULT rc = checkStateDependency(MutableStateDep);
3087 if (FAILED(rc)) return rc;
3088
3089 setModified(IsModified_MachineData);
3090 mHWData.backup();
3091 mHWData->mIOCacheEnabled = aEnabled;
3092
3093 return S_OK;
3094}
3095
3096STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3097{
3098 CheckComArgOutPointerValid(aIOCacheSize);
3099
3100 AutoCaller autoCaller(this);
3101 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3102
3103 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3104
3105 *aIOCacheSize = mHWData->mIOCacheSize;
3106
3107 return S_OK;
3108}
3109
3110STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3111{
3112 AutoCaller autoCaller(this);
3113 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3114
3115 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3116
3117 HRESULT rc = checkStateDependency(MutableStateDep);
3118 if (FAILED(rc)) return rc;
3119
3120 setModified(IsModified_MachineData);
3121 mHWData.backup();
3122 mHWData->mIOCacheSize = aIOCacheSize;
3123
3124 return S_OK;
3125}
3126
3127
3128/**
3129 * @note Locks objects!
3130 */
3131STDMETHODIMP Machine::LockMachine(ISession *aSession,
3132 LockType_T lockType)
3133{
3134 CheckComArgNotNull(aSession);
3135
3136 AutoCaller autoCaller(this);
3137 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3138
3139 /* check the session state */
3140 SessionState_T state;
3141 HRESULT rc = aSession->COMGETTER(State)(&state);
3142 if (FAILED(rc)) return rc;
3143
3144 if (state != SessionState_Unlocked)
3145 return setError(VBOX_E_INVALID_OBJECT_STATE,
3146 tr("The given session is busy"));
3147
3148 // get the client's IInternalSessionControl interface
3149 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3150 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3151 E_INVALIDARG);
3152
3153 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3154
3155 if (!mData->mRegistered)
3156 return setError(E_UNEXPECTED,
3157 tr("The machine '%s' is not registered"),
3158 mUserData->s.strName.c_str());
3159
3160 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3161
3162 SessionState_T oldState = mData->mSession.mState;
3163 /* Hack: in case the session is closing and there is a progress object
3164 * which allows waiting for the session to be closed, take the opportunity
3165 * and do a limited wait (max. 1 second). This helps a lot when the system
3166 * is busy and thus session closing can take a little while. */
3167 if ( mData->mSession.mState == SessionState_Unlocking
3168 && mData->mSession.mProgress)
3169 {
3170 alock.release();
3171 mData->mSession.mProgress->WaitForCompletion(1000);
3172 alock.acquire();
3173 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3174 }
3175
3176 // try again now
3177 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3178 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3179 )
3180 {
3181 // OK, share the session... we are now dealing with three processes:
3182 // 1) VBoxSVC (where this code runs);
3183 // 2) process C: the caller's client process (who wants a shared session);
3184 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3185
3186 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3187 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3188 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3189 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3190 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3191
3192 /*
3193 * Release the lock before calling the client process. It's safe here
3194 * since the only thing to do after we get the lock again is to add
3195 * the remote control to the list (which doesn't directly influence
3196 * anything).
3197 */
3198 alock.release();
3199
3200 // get the console of the session holding the write lock (this is a remote call)
3201 ComPtr<IConsole> pConsoleW;
3202 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3203 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3204 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3205 if (FAILED(rc))
3206 // the failure may occur w/o any error info (from RPC), so provide one
3207 return setError(VBOX_E_VM_ERROR,
3208 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3209
3210 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3211
3212 // share the session machine and W's console with the caller's session
3213 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3214 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3215 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3216
3217 if (FAILED(rc))
3218 // the failure may occur w/o any error info (from RPC), so provide one
3219 return setError(VBOX_E_VM_ERROR,
3220 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3221 alock.acquire();
3222
3223 // need to revalidate the state after acquiring the lock again
3224 if (mData->mSession.mState != SessionState_Locked)
3225 {
3226 pSessionControl->Uninitialize();
3227 return setError(VBOX_E_INVALID_SESSION_STATE,
3228 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3229 mUserData->s.strName.c_str());
3230 }
3231
3232 // add the caller's session to the list
3233 mData->mSession.mRemoteControls.push_back(pSessionControl);
3234 }
3235 else if ( mData->mSession.mState == SessionState_Locked
3236 || mData->mSession.mState == SessionState_Unlocking
3237 )
3238 {
3239 // sharing not permitted, or machine still unlocking:
3240 return setError(VBOX_E_INVALID_OBJECT_STATE,
3241 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3242 mUserData->s.strName.c_str());
3243 }
3244 else
3245 {
3246 // machine is not locked: then write-lock the machine (create the session machine)
3247
3248 // must not be busy
3249 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3250
3251 // get the caller's session PID
3252 RTPROCESS pid = NIL_RTPROCESS;
3253 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3254 pSessionControl->GetPID((ULONG*)&pid);
3255 Assert(pid != NIL_RTPROCESS);
3256
3257 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3258
3259 if (fLaunchingVMProcess)
3260 {
3261 // this machine is awaiting for a spawning session to be opened:
3262 // then the calling process must be the one that got started by
3263 // LaunchVMProcess()
3264
3265 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3266 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3267
3268 if (mData->mSession.mPID != pid)
3269 return setError(E_ACCESSDENIED,
3270 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3271 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3272 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3273 }
3274
3275 // create the mutable SessionMachine from the current machine
3276 ComObjPtr<SessionMachine> sessionMachine;
3277 sessionMachine.createObject();
3278 rc = sessionMachine->init(this);
3279 AssertComRC(rc);
3280
3281 /* NOTE: doing return from this function after this point but
3282 * before the end is forbidden since it may call SessionMachine::uninit()
3283 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3284 * lock while still holding the Machine lock in alock so that a deadlock
3285 * is possible due to the wrong lock order. */
3286
3287 if (SUCCEEDED(rc))
3288 {
3289 /*
3290 * Set the session state to Spawning to protect against subsequent
3291 * attempts to open a session and to unregister the machine after
3292 * we release the lock.
3293 */
3294 SessionState_T origState = mData->mSession.mState;
3295 mData->mSession.mState = SessionState_Spawning;
3296
3297 /*
3298 * Release the lock before calling the client process -- it will call
3299 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3300 * because the state is Spawning, so that LaunchVMProcess() and
3301 * LockMachine() calls will fail. This method, called before we
3302 * acquire the lock again, will fail because of the wrong PID.
3303 *
3304 * Note that mData->mSession.mRemoteControls accessed outside
3305 * the lock may not be modified when state is Spawning, so it's safe.
3306 */
3307 alock.release();
3308
3309 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3310 rc = pSessionControl->AssignMachine(sessionMachine, lockType);
3311 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3312
3313 /* The failure may occur w/o any error info (from RPC), so provide one */
3314 if (FAILED(rc))
3315 setError(VBOX_E_VM_ERROR,
3316 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3317
3318 if ( SUCCEEDED(rc)
3319 && fLaunchingVMProcess
3320 )
3321 {
3322 /* complete the remote session initialization */
3323
3324 /* get the console from the direct session */
3325 ComPtr<IConsole> console;
3326 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3327 ComAssertComRC(rc);
3328
3329 if (SUCCEEDED(rc) && !console)
3330 {
3331 ComAssert(!!console);
3332 rc = E_FAIL;
3333 }
3334
3335 /* assign machine & console to the remote session */
3336 if (SUCCEEDED(rc))
3337 {
3338 /*
3339 * after LaunchVMProcess(), the first and the only
3340 * entry in remoteControls is that remote session
3341 */
3342 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3343 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3344 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3345
3346 /* The failure may occur w/o any error info (from RPC), so provide one */
3347 if (FAILED(rc))
3348 setError(VBOX_E_VM_ERROR,
3349 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3350 }
3351
3352 if (FAILED(rc))
3353 pSessionControl->Uninitialize();
3354 }
3355
3356 /* acquire the lock again */
3357 alock.acquire();
3358
3359 /* Restore the session state */
3360 mData->mSession.mState = origState;
3361 }
3362
3363 // finalize spawning anyway (this is why we don't return on errors above)
3364 if (fLaunchingVMProcess)
3365 {
3366 /* Note that the progress object is finalized later */
3367 /** @todo Consider checking mData->mSession.mProgress for cancellation
3368 * around here. */
3369
3370 /* We don't reset mSession.mPID here because it is necessary for
3371 * SessionMachine::uninit() to reap the child process later. */
3372
3373 if (FAILED(rc))
3374 {
3375 /* Close the remote session, remove the remote control from the list
3376 * and reset session state to Closed (@note keep the code in sync
3377 * with the relevant part in openSession()). */
3378
3379 Assert(mData->mSession.mRemoteControls.size() == 1);
3380 if (mData->mSession.mRemoteControls.size() == 1)
3381 {
3382 ErrorInfoKeeper eik;
3383 mData->mSession.mRemoteControls.front()->Uninitialize();
3384 }
3385
3386 mData->mSession.mRemoteControls.clear();
3387 mData->mSession.mState = SessionState_Unlocked;
3388 }
3389 }
3390 else
3391 {
3392 /* memorize PID of the directly opened session */
3393 if (SUCCEEDED(rc))
3394 mData->mSession.mPID = pid;
3395 }
3396
3397 if (SUCCEEDED(rc))
3398 {
3399 /* memorize the direct session control and cache IUnknown for it */
3400 mData->mSession.mDirectControl = pSessionControl;
3401 mData->mSession.mState = SessionState_Locked;
3402 /* associate the SessionMachine with this Machine */
3403 mData->mSession.mMachine = sessionMachine;
3404
3405 /* request an IUnknown pointer early from the remote party for later
3406 * identity checks (it will be internally cached within mDirectControl
3407 * at least on XPCOM) */
3408 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3409 NOREF(unk);
3410 }
3411
3412 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3413 * would break the lock order */
3414 alock.release();
3415
3416 /* uninitialize the created session machine on failure */
3417 if (FAILED(rc))
3418 sessionMachine->uninit();
3419
3420 }
3421
3422 if (SUCCEEDED(rc))
3423 {
3424 /*
3425 * tell the client watcher thread to update the set of
3426 * machines that have open sessions
3427 */
3428 mParent->updateClientWatcher();
3429
3430 if (oldState != SessionState_Locked)
3431 /* fire an event */
3432 mParent->onSessionStateChange(getId(), SessionState_Locked);
3433 }
3434
3435 return rc;
3436}
3437
3438/**
3439 * @note Locks objects!
3440 */
3441STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3442 IN_BSTR aType,
3443 IN_BSTR aEnvironment,
3444 IProgress **aProgress)
3445{
3446 CheckComArgStrNotEmptyOrNull(aType);
3447 Utf8Str strType(aType);
3448 Utf8Str strEnvironment(aEnvironment);
3449 /* "emergencystop" doesn't need the session, so skip the checks/interface
3450 * retrieval. This code doesn't quite fit in here, but introducing a
3451 * special API method would be even more effort, and would require explicit
3452 * support by every API client. It's better to hide the feature a bit. */
3453 if (strType != "emergencystop")
3454 CheckComArgNotNull(aSession);
3455 CheckComArgOutPointerValid(aProgress);
3456
3457 AutoCaller autoCaller(this);
3458 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3459
3460 ComPtr<IInternalSessionControl> control;
3461 HRESULT rc = S_OK;
3462
3463 if (strType != "emergencystop")
3464 {
3465 /* check the session state */
3466 SessionState_T state;
3467 rc = aSession->COMGETTER(State)(&state);
3468 if (FAILED(rc))
3469 return rc;
3470
3471 if (state != SessionState_Unlocked)
3472 return setError(VBOX_E_INVALID_OBJECT_STATE,
3473 tr("The given session is busy"));
3474
3475 /* get the IInternalSessionControl interface */
3476 control = aSession;
3477 ComAssertMsgRet(!control.isNull(),
3478 ("No IInternalSessionControl interface"),
3479 E_INVALIDARG);
3480 }
3481
3482 /* get the teleporter enable state for the progress object init. */
3483 BOOL fTeleporterEnabled;
3484 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3485 if (FAILED(rc))
3486 return rc;
3487
3488 /* create a progress object */
3489 if (strType != "emergencystop")
3490 {
3491 ComObjPtr<ProgressProxy> progress;
3492 progress.createObject();
3493 rc = progress->init(mParent,
3494 static_cast<IMachine*>(this),
3495 Bstr(tr("Starting VM")).raw(),
3496 TRUE /* aCancelable */,
3497 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3498 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strType.c_str()).raw(),
3499 2 /* uFirstOperationWeight */,
3500 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3501
3502 if (SUCCEEDED(rc))
3503 {
3504 rc = launchVMProcess(control, strType, strEnvironment, progress);
3505 if (SUCCEEDED(rc))
3506 {
3507 progress.queryInterfaceTo(aProgress);
3508
3509 /* signal the client watcher thread */
3510 mParent->updateClientWatcher();
3511
3512 /* fire an event */
3513 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3514 }
3515 }
3516 }
3517 else
3518 {
3519 /* no progress object - either instant success or failure */
3520 *aProgress = NULL;
3521
3522 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3523
3524 if (mData->mSession.mState != SessionState_Locked)
3525 return setError(VBOX_E_INVALID_OBJECT_STATE,
3526 tr("The machine '%s' is not locked by a session"),
3527 mUserData->s.strName.c_str());
3528
3529 /* must have a VM process associated - do not kill normal API clients
3530 * with an open session */
3531 if (!Global::IsOnline(mData->mMachineState))
3532 return setError(VBOX_E_INVALID_OBJECT_STATE,
3533 tr("The machine '%s' does not have a VM process"),
3534 mUserData->s.strName.c_str());
3535
3536 /* forcibly terminate the VM process */
3537 if (mData->mSession.mPID != NIL_RTPROCESS)
3538 RTProcTerminate(mData->mSession.mPID);
3539
3540 /* signal the client watcher thread, as most likely the client has
3541 * been terminated */
3542 mParent->updateClientWatcher();
3543 }
3544
3545 return rc;
3546}
3547
3548STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3549{
3550 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3551 return setError(E_INVALIDARG,
3552 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3553 aPosition, SchemaDefs::MaxBootPosition);
3554
3555 if (aDevice == DeviceType_USB)
3556 return setError(E_NOTIMPL,
3557 tr("Booting from USB device is currently not supported"));
3558
3559 AutoCaller autoCaller(this);
3560 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3561
3562 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3563
3564 HRESULT rc = checkStateDependency(MutableStateDep);
3565 if (FAILED(rc)) return rc;
3566
3567 setModified(IsModified_MachineData);
3568 mHWData.backup();
3569 mHWData->mBootOrder[aPosition - 1] = aDevice;
3570
3571 return S_OK;
3572}
3573
3574STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3575{
3576 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3577 return setError(E_INVALIDARG,
3578 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3579 aPosition, SchemaDefs::MaxBootPosition);
3580
3581 AutoCaller autoCaller(this);
3582 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3583
3584 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3585
3586 *aDevice = mHWData->mBootOrder[aPosition - 1];
3587
3588 return S_OK;
3589}
3590
3591STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
3592 LONG aControllerPort,
3593 LONG aDevice,
3594 DeviceType_T aType,
3595 IMedium *aMedium)
3596{
3597 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3598 aControllerName, aControllerPort, aDevice, aType, aMedium));
3599
3600 CheckComArgStrNotEmptyOrNull(aControllerName);
3601
3602 AutoCaller autoCaller(this);
3603 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3604
3605 // request the host lock first, since might be calling Host methods for getting host drives;
3606 // next, protect the media tree all the while we're in here, as well as our member variables
3607 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
3608 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3609
3610 HRESULT rc = checkStateDependency(MutableStateDep);
3611 if (FAILED(rc)) return rc;
3612
3613 /// @todo NEWMEDIA implicit machine registration
3614 if (!mData->mRegistered)
3615 return setError(VBOX_E_INVALID_OBJECT_STATE,
3616 tr("Cannot attach storage devices to an unregistered machine"));
3617
3618 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3619
3620 /* Check for an existing controller. */
3621 ComObjPtr<StorageController> ctl;
3622 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
3623 if (FAILED(rc)) return rc;
3624
3625 StorageControllerType_T ctrlType;
3626 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3627 if (FAILED(rc))
3628 return setError(E_FAIL,
3629 tr("Could not get type of controller '%ls'"),
3630 aControllerName);
3631
3632 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3633 bool fHotplug = false;
3634 if (Global::IsOnlineOrTransient(mData->mMachineState))
3635 fHotplug = true;
3636
3637 if (fHotplug && !isControllerHotplugCapable(ctrlType))
3638 return setError(VBOX_E_INVALID_VM_STATE,
3639 tr("Controller '%ls' does not support hotplugging"),
3640 aControllerName);
3641
3642 // check that the port and device are not out of range
3643 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
3644 if (FAILED(rc)) return rc;
3645
3646 /* check if the device slot is already busy */
3647 MediumAttachment *pAttachTemp;
3648 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
3649 aControllerName,
3650 aControllerPort,
3651 aDevice)))
3652 {
3653 Medium *pMedium = pAttachTemp->getMedium();
3654 if (pMedium)
3655 {
3656 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3657 return setError(VBOX_E_OBJECT_IN_USE,
3658 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3659 pMedium->getLocationFull().c_str(),
3660 aControllerPort,
3661 aDevice,
3662 aControllerName);
3663 }
3664 else
3665 return setError(VBOX_E_OBJECT_IN_USE,
3666 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3667 aControllerPort, aDevice, aControllerName);
3668 }
3669
3670 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
3671 if (aMedium && medium.isNull())
3672 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3673
3674 AutoCaller mediumCaller(medium);
3675 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3676
3677 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3678
3679 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
3680 && !medium.isNull()
3681 )
3682 return setError(VBOX_E_OBJECT_IN_USE,
3683 tr("Medium '%s' is already attached to this virtual machine"),
3684 medium->getLocationFull().c_str());
3685
3686 if (!medium.isNull())
3687 {
3688 MediumType_T mtype = medium->getType();
3689 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3690 // For DVDs it's not written to the config file, so needs no global config
3691 // version bump. For floppies it's a new attribute "type", which is ignored
3692 // by older VirtualBox version, so needs no global config version bump either.
3693 // For hard disks this type is not accepted.
3694 if (mtype == MediumType_MultiAttach)
3695 {
3696 // This type is new with VirtualBox 4.0 and therefore requires settings
3697 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3698 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3699 // two reasons: The medium type is a property of the media registry tree, which
3700 // can reside in the global config file (for pre-4.0 media); we would therefore
3701 // possibly need to bump the global config version. We don't want to do that though
3702 // because that might make downgrading to pre-4.0 impossible.
3703 // As a result, we can only use these two new types if the medium is NOT in the
3704 // global registry:
3705 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
3706 if ( medium->isInRegistry(uuidGlobalRegistry)
3707 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3708 )
3709 return setError(VBOX_E_INVALID_OBJECT_STATE,
3710 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3711 "to machines that were created with VirtualBox 4.0 or later"),
3712 medium->getLocationFull().c_str());
3713 }
3714 }
3715
3716 bool fIndirect = false;
3717 if (!medium.isNull())
3718 fIndirect = medium->isReadOnly();
3719 bool associate = true;
3720
3721 do
3722 {
3723 if ( aType == DeviceType_HardDisk
3724 && mMediaData.isBackedUp())
3725 {
3726 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3727
3728 /* check if the medium was attached to the VM before we started
3729 * changing attachments in which case the attachment just needs to
3730 * be restored */
3731 if ((pAttachTemp = findAttachment(oldAtts, medium)))
3732 {
3733 AssertReturn(!fIndirect, E_FAIL);
3734
3735 /* see if it's the same bus/channel/device */
3736 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
3737 {
3738 /* the simplest case: restore the whole attachment
3739 * and return, nothing else to do */
3740 mMediaData->mAttachments.push_back(pAttachTemp);
3741 return S_OK;
3742 }
3743
3744 /* bus/channel/device differ; we need a new attachment object,
3745 * but don't try to associate it again */
3746 associate = false;
3747 break;
3748 }
3749 }
3750
3751 /* go further only if the attachment is to be indirect */
3752 if (!fIndirect)
3753 break;
3754
3755 /* perform the so called smart attachment logic for indirect
3756 * attachments. Note that smart attachment is only applicable to base
3757 * hard disks. */
3758
3759 if (medium->getParent().isNull())
3760 {
3761 /* first, investigate the backup copy of the current hard disk
3762 * attachments to make it possible to re-attach existing diffs to
3763 * another device slot w/o losing their contents */
3764 if (mMediaData.isBackedUp())
3765 {
3766 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3767
3768 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3769 uint32_t foundLevel = 0;
3770
3771 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
3772 it != oldAtts.end();
3773 ++it)
3774 {
3775 uint32_t level = 0;
3776 MediumAttachment *pAttach = *it;
3777 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3778 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3779 if (pMedium.isNull())
3780 continue;
3781
3782 if (pMedium->getBase(&level) == medium)
3783 {
3784 /* skip the hard disk if its currently attached (we
3785 * cannot attach the same hard disk twice) */
3786 if (findAttachment(mMediaData->mAttachments,
3787 pMedium))
3788 continue;
3789
3790 /* matched device, channel and bus (i.e. attached to the
3791 * same place) will win and immediately stop the search;
3792 * otherwise the attachment that has the youngest
3793 * descendant of medium will be used
3794 */
3795 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
3796 {
3797 /* the simplest case: restore the whole attachment
3798 * and return, nothing else to do */
3799 mMediaData->mAttachments.push_back(*it);
3800 return S_OK;
3801 }
3802 else if ( foundIt == oldAtts.end()
3803 || level > foundLevel /* prefer younger */
3804 )
3805 {
3806 foundIt = it;
3807 foundLevel = level;
3808 }
3809 }
3810 }
3811
3812 if (foundIt != oldAtts.end())
3813 {
3814 /* use the previously attached hard disk */
3815 medium = (*foundIt)->getMedium();
3816 mediumCaller.attach(medium);
3817 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3818 mediumLock.attach(medium);
3819 /* not implicit, doesn't require association with this VM */
3820 fIndirect = false;
3821 associate = false;
3822 /* go right to the MediumAttachment creation */
3823 break;
3824 }
3825 }
3826
3827 /* must give up the medium lock and medium tree lock as below we
3828 * go over snapshots, which needs a lock with higher lock order. */
3829 mediumLock.release();
3830 treeLock.release();
3831
3832 /* then, search through snapshots for the best diff in the given
3833 * hard disk's chain to base the new diff on */
3834
3835 ComObjPtr<Medium> base;
3836 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3837 while (snap)
3838 {
3839 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3840
3841 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
3842
3843 MediumAttachment *pAttachFound = NULL;
3844 uint32_t foundLevel = 0;
3845
3846 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
3847 it != snapAtts.end();
3848 ++it)
3849 {
3850 MediumAttachment *pAttach = *it;
3851 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3852 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3853 if (pMedium.isNull())
3854 continue;
3855
3856 uint32_t level = 0;
3857 if (pMedium->getBase(&level) == medium)
3858 {
3859 /* matched device, channel and bus (i.e. attached to the
3860 * same place) will win and immediately stop the search;
3861 * otherwise the attachment that has the youngest
3862 * descendant of medium will be used
3863 */
3864 if ( pAttach->getDevice() == aDevice
3865 && pAttach->getPort() == aControllerPort
3866 && pAttach->getControllerName() == aControllerName
3867 )
3868 {
3869 pAttachFound = pAttach;
3870 break;
3871 }
3872 else if ( !pAttachFound
3873 || level > foundLevel /* prefer younger */
3874 )
3875 {
3876 pAttachFound = pAttach;
3877 foundLevel = level;
3878 }
3879 }
3880 }
3881
3882 if (pAttachFound)
3883 {
3884 base = pAttachFound->getMedium();
3885 break;
3886 }
3887
3888 snap = snap->getParent();
3889 }
3890
3891 /* re-lock medium tree and the medium, as we need it below */
3892 treeLock.acquire();
3893 mediumLock.acquire();
3894
3895 /* found a suitable diff, use it as a base */
3896 if (!base.isNull())
3897 {
3898 medium = base;
3899 mediumCaller.attach(medium);
3900 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3901 mediumLock.attach(medium);
3902 }
3903 }
3904
3905 Utf8Str strFullSnapshotFolder;
3906 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3907
3908 ComObjPtr<Medium> diff;
3909 diff.createObject();
3910 // store this diff in the same registry as the parent
3911 Guid uuidRegistryParent;
3912 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
3913 {
3914 // parent image has no registry: this can happen if we're attaching a new immutable
3915 // image that has not yet been attached (medium then points to the base and we're
3916 // creating the diff image for the immutable, and the parent is not yet registered);
3917 // put the parent in the machine registry then
3918 mediumLock.release();
3919 treeLock.release();
3920 alock.release();
3921 addMediumToRegistry(medium);
3922 alock.acquire();
3923 treeLock.acquire();
3924 mediumLock.acquire();
3925 medium->getFirstRegistryMachineId(uuidRegistryParent);
3926 }
3927 rc = diff->init(mParent,
3928 medium->getPreferredDiffFormat(),
3929 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3930 uuidRegistryParent);
3931 if (FAILED(rc)) return rc;
3932
3933 /* Apply the normal locking logic to the entire chain. */
3934 MediumLockList *pMediumLockList(new MediumLockList());
3935 mediumLock.release();
3936 treeLock.release();
3937 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
3938 true /* fMediumLockWrite */,
3939 medium,
3940 *pMediumLockList);
3941 treeLock.acquire();
3942 mediumLock.acquire();
3943 if (SUCCEEDED(rc))
3944 {
3945 mediumLock.release();
3946 treeLock.release();
3947 rc = pMediumLockList->Lock();
3948 treeLock.acquire();
3949 mediumLock.acquire();
3950 if (FAILED(rc))
3951 setError(rc,
3952 tr("Could not lock medium when creating diff '%s'"),
3953 diff->getLocationFull().c_str());
3954 else
3955 {
3956 /* will release the lock before the potentially lengthy
3957 * operation, so protect with the special state */
3958 MachineState_T oldState = mData->mMachineState;
3959 setMachineState(MachineState_SettingUp);
3960
3961 mediumLock.release();
3962 treeLock.release();
3963 alock.release();
3964
3965 rc = medium->createDiffStorage(diff,
3966 MediumVariant_Standard,
3967 pMediumLockList,
3968 NULL /* aProgress */,
3969 true /* aWait */);
3970
3971 alock.acquire();
3972 treeLock.acquire();
3973 mediumLock.acquire();
3974
3975 setMachineState(oldState);
3976 }
3977 }
3978
3979 /* Unlock the media and free the associated memory. */
3980 delete pMediumLockList;
3981
3982 if (FAILED(rc)) return rc;
3983
3984 /* use the created diff for the actual attachment */
3985 medium = diff;
3986 mediumCaller.attach(medium);
3987 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3988 mediumLock.attach(medium);
3989 }
3990 while (0);
3991
3992 ComObjPtr<MediumAttachment> attachment;
3993 attachment.createObject();
3994 rc = attachment->init(this,
3995 medium,
3996 aControllerName,
3997 aControllerPort,
3998 aDevice,
3999 aType,
4000 fIndirect,
4001 false /* fPassthrough */,
4002 false /* fTempEject */,
4003 false /* fNonRotational */,
4004 false /* fDiscard */,
4005 Utf8Str::Empty);
4006 if (FAILED(rc)) return rc;
4007
4008 if (associate && !medium.isNull())
4009 {
4010 // as the last step, associate the medium to the VM
4011 rc = medium->addBackReference(mData->mUuid);
4012 // here we can fail because of Deleting, or being in process of creating a Diff
4013 if (FAILED(rc)) return rc;
4014
4015 mediumLock.release();
4016 treeLock.release();
4017 alock.release();
4018 addMediumToRegistry(medium);
4019 alock.acquire();
4020 treeLock.acquire();
4021 mediumLock.acquire();
4022 }
4023
4024 /* success: finally remember the attachment */
4025 setModified(IsModified_Storage);
4026 mMediaData.backup();
4027 mMediaData->mAttachments.push_back(attachment);
4028
4029 mediumLock.release();
4030 treeLock.release();
4031 alock.release();
4032
4033 if (fHotplug)
4034 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */);
4035
4036 mParent->saveModifiedRegistries();
4037
4038 return rc;
4039}
4040
4041STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4042 LONG aDevice)
4043{
4044 CheckComArgStrNotEmptyOrNull(aControllerName);
4045
4046 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4047 aControllerName, aControllerPort, aDevice));
4048
4049 AutoCaller autoCaller(this);
4050 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4051
4052 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4053
4054 HRESULT rc = checkStateDependency(MutableStateDep);
4055 if (FAILED(rc)) return rc;
4056
4057 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4058
4059 /* Check for an existing controller. */
4060 ComObjPtr<StorageController> ctl;
4061 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4062 if (FAILED(rc)) return rc;
4063
4064 StorageControllerType_T ctrlType;
4065 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4066 if (FAILED(rc))
4067 return setError(E_FAIL,
4068 tr("Could not get type of controller '%ls'"),
4069 aControllerName);
4070
4071 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4072 bool fHotplug = false;
4073 if (Global::IsOnlineOrTransient(mData->mMachineState))
4074 fHotplug = true;
4075
4076 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4077 return setError(VBOX_E_INVALID_VM_STATE,
4078 tr("Controller '%ls' does not support hotplugging"),
4079 aControllerName);
4080
4081 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4082 aControllerName,
4083 aControllerPort,
4084 aDevice);
4085 if (!pAttach)
4086 return setError(VBOX_E_OBJECT_NOT_FOUND,
4087 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4088 aDevice, aControllerPort, aControllerName);
4089
4090 /*
4091 * The VM has to detach the device before we delete any implicit diffs.
4092 * If this fails we can roll back without loosing data.
4093 */
4094 if (fHotplug)
4095 {
4096 alock.release();
4097 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */);
4098 alock.acquire();
4099 }
4100 if (FAILED(rc)) return rc;
4101
4102 /* If we are here everything went well and we can delete the implicit now. */
4103 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4104
4105 alock.release();
4106
4107 mParent->saveModifiedRegistries();
4108
4109 return rc;
4110}
4111
4112STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4113 LONG aDevice, BOOL aPassthrough)
4114{
4115 CheckComArgStrNotEmptyOrNull(aControllerName);
4116
4117 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4118 aControllerName, aControllerPort, aDevice, aPassthrough));
4119
4120 AutoCaller autoCaller(this);
4121 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4122
4123 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4124
4125 HRESULT rc = checkStateDependency(MutableStateDep);
4126 if (FAILED(rc)) return rc;
4127
4128 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4129
4130 if (Global::IsOnlineOrTransient(mData->mMachineState))
4131 return setError(VBOX_E_INVALID_VM_STATE,
4132 tr("Invalid machine state: %s"),
4133 Global::stringifyMachineState(mData->mMachineState));
4134
4135 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4136 aControllerName,
4137 aControllerPort,
4138 aDevice);
4139 if (!pAttach)
4140 return setError(VBOX_E_OBJECT_NOT_FOUND,
4141 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4142 aDevice, aControllerPort, aControllerName);
4143
4144
4145 setModified(IsModified_Storage);
4146 mMediaData.backup();
4147
4148 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4149
4150 if (pAttach->getType() != DeviceType_DVD)
4151 return setError(E_INVALIDARG,
4152 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4153 aDevice, aControllerPort, aControllerName);
4154 pAttach->updatePassthrough(!!aPassthrough);
4155
4156 return S_OK;
4157}
4158
4159STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4160 LONG aDevice, BOOL aTemporaryEject)
4161{
4162 CheckComArgStrNotEmptyOrNull(aControllerName);
4163
4164 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4165 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4166
4167 AutoCaller autoCaller(this);
4168 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4169
4170 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4171
4172 HRESULT rc = checkStateDependency(MutableStateDep);
4173 if (FAILED(rc)) return rc;
4174
4175 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4176 aControllerName,
4177 aControllerPort,
4178 aDevice);
4179 if (!pAttach)
4180 return setError(VBOX_E_OBJECT_NOT_FOUND,
4181 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4182 aDevice, aControllerPort, aControllerName);
4183
4184
4185 setModified(IsModified_Storage);
4186 mMediaData.backup();
4187
4188 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4189
4190 if (pAttach->getType() != DeviceType_DVD)
4191 return setError(E_INVALIDARG,
4192 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4193 aDevice, aControllerPort, aControllerName);
4194 pAttach->updateTempEject(!!aTemporaryEject);
4195
4196 return S_OK;
4197}
4198
4199STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4200 LONG aDevice, BOOL aNonRotational)
4201{
4202 CheckComArgStrNotEmptyOrNull(aControllerName);
4203
4204 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4205 aControllerName, aControllerPort, aDevice, aNonRotational));
4206
4207 AutoCaller autoCaller(this);
4208 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4209
4210 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4211
4212 HRESULT rc = checkStateDependency(MutableStateDep);
4213 if (FAILED(rc)) return rc;
4214
4215 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4216
4217 if (Global::IsOnlineOrTransient(mData->mMachineState))
4218 return setError(VBOX_E_INVALID_VM_STATE,
4219 tr("Invalid machine state: %s"),
4220 Global::stringifyMachineState(mData->mMachineState));
4221
4222 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4223 aControllerName,
4224 aControllerPort,
4225 aDevice);
4226 if (!pAttach)
4227 return setError(VBOX_E_OBJECT_NOT_FOUND,
4228 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4229 aDevice, aControllerPort, aControllerName);
4230
4231
4232 setModified(IsModified_Storage);
4233 mMediaData.backup();
4234
4235 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4236
4237 if (pAttach->getType() != DeviceType_HardDisk)
4238 return setError(E_INVALIDARG,
4239 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"),
4240 aDevice, aControllerPort, aControllerName);
4241 pAttach->updateNonRotational(!!aNonRotational);
4242
4243 return S_OK;
4244}
4245
4246STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4247 LONG aDevice, BOOL aDiscard)
4248{
4249 CheckComArgStrNotEmptyOrNull(aControllerName);
4250
4251 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4252 aControllerName, aControllerPort, aDevice, aDiscard));
4253
4254 AutoCaller autoCaller(this);
4255 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4256
4257 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4258
4259 HRESULT rc = checkStateDependency(MutableStateDep);
4260 if (FAILED(rc)) return rc;
4261
4262 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4263
4264 if (Global::IsOnlineOrTransient(mData->mMachineState))
4265 return setError(VBOX_E_INVALID_VM_STATE,
4266 tr("Invalid machine state: %s"),
4267 Global::stringifyMachineState(mData->mMachineState));
4268
4269 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4270 aControllerName,
4271 aControllerPort,
4272 aDevice);
4273 if (!pAttach)
4274 return setError(VBOX_E_OBJECT_NOT_FOUND,
4275 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4276 aDevice, aControllerPort, aControllerName);
4277
4278
4279 setModified(IsModified_Storage);
4280 mMediaData.backup();
4281
4282 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4283
4284 if (pAttach->getType() != DeviceType_HardDisk)
4285 return setError(E_INVALIDARG,
4286 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"),
4287 aDevice, aControllerPort, aControllerName);
4288 pAttach->updateDiscard(!!aDiscard);
4289
4290 return S_OK;
4291}
4292
4293STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4294 LONG aDevice)
4295{
4296 int rc = S_OK;
4297 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4298 aControllerName, aControllerPort, aDevice));
4299
4300 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4301
4302 return rc;
4303}
4304
4305STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4306 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4307{
4308 CheckComArgStrNotEmptyOrNull(aControllerName);
4309
4310 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4311 aControllerName, aControllerPort, aDevice));
4312
4313 AutoCaller autoCaller(this);
4314 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4315
4316 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4317
4318 HRESULT rc = checkStateDependency(MutableStateDep);
4319 if (FAILED(rc)) return rc;
4320
4321 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4322
4323 if (Global::IsOnlineOrTransient(mData->mMachineState))
4324 return setError(VBOX_E_INVALID_VM_STATE,
4325 tr("Invalid machine state: %s"),
4326 Global::stringifyMachineState(mData->mMachineState));
4327
4328 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4329 aControllerName,
4330 aControllerPort,
4331 aDevice);
4332 if (!pAttach)
4333 return setError(VBOX_E_OBJECT_NOT_FOUND,
4334 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4335 aDevice, aControllerPort, aControllerName);
4336
4337
4338 setModified(IsModified_Storage);
4339 mMediaData.backup();
4340
4341 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4342 if (aBandwidthGroup && group.isNull())
4343 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4344
4345 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4346
4347 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4348 if (strBandwidthGroupOld.isNotEmpty())
4349 {
4350 /* Get the bandwidth group object and release it - this must not fail. */
4351 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4352 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4353 Assert(SUCCEEDED(rc));
4354
4355 pBandwidthGroupOld->release();
4356 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4357 }
4358
4359 if (!group.isNull())
4360 {
4361 group->reference();
4362 pAttach->updateBandwidthGroup(group->getName());
4363 }
4364
4365 return S_OK;
4366}
4367
4368STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4369 LONG aControllerPort,
4370 LONG aDevice,
4371 DeviceType_T aType)
4372{
4373 HRESULT rc = S_OK;
4374
4375 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4376 aControllerName, aControllerPort, aDevice, aType));
4377
4378 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4379
4380 return rc;
4381}
4382
4383
4384
4385STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4386 LONG aControllerPort,
4387 LONG aDevice,
4388 BOOL aForce)
4389{
4390 int rc = S_OK;
4391 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4392 aControllerName, aControllerPort, aForce));
4393
4394 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4395
4396 return rc;
4397}
4398
4399STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4400 LONG aControllerPort,
4401 LONG aDevice,
4402 IMedium *aMedium,
4403 BOOL aForce)
4404{
4405 int rc = S_OK;
4406 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4407 aControllerName, aControllerPort, aDevice, aForce));
4408
4409 CheckComArgStrNotEmptyOrNull(aControllerName);
4410
4411 AutoCaller autoCaller(this);
4412 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4413
4414 // request the host lock first, since might be calling Host methods for getting host drives;
4415 // next, protect the media tree all the while we're in here, as well as our member variables
4416 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4417 this->lockHandle(),
4418 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4419
4420 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4421 aControllerName,
4422 aControllerPort,
4423 aDevice);
4424 if (pAttach.isNull())
4425 return setError(VBOX_E_OBJECT_NOT_FOUND,
4426 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4427 aDevice, aControllerPort, aControllerName);
4428
4429 /* Remember previously mounted medium. The medium before taking the
4430 * backup is not necessarily the same thing. */
4431 ComObjPtr<Medium> oldmedium;
4432 oldmedium = pAttach->getMedium();
4433
4434 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4435 if (aMedium && pMedium.isNull())
4436 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4437
4438 AutoCaller mediumCaller(pMedium);
4439 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4440
4441 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4442 if (pMedium)
4443 {
4444 DeviceType_T mediumType = pAttach->getType();
4445 switch (mediumType)
4446 {
4447 case DeviceType_DVD:
4448 case DeviceType_Floppy:
4449 break;
4450
4451 default:
4452 return setError(VBOX_E_INVALID_OBJECT_STATE,
4453 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
4454 aControllerPort,
4455 aDevice,
4456 aControllerName);
4457 }
4458 }
4459
4460 setModified(IsModified_Storage);
4461 mMediaData.backup();
4462
4463 {
4464 // The backup operation makes the pAttach reference point to the
4465 // old settings. Re-get the correct reference.
4466 pAttach = findAttachment(mMediaData->mAttachments,
4467 aControllerName,
4468 aControllerPort,
4469 aDevice);
4470 if (!oldmedium.isNull())
4471 oldmedium->removeBackReference(mData->mUuid);
4472 if (!pMedium.isNull())
4473 {
4474 pMedium->addBackReference(mData->mUuid);
4475
4476 mediumLock.release();
4477 multiLock.release();
4478 addMediumToRegistry(pMedium);
4479 multiLock.acquire();
4480 mediumLock.acquire();
4481 }
4482
4483 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4484 pAttach->updateMedium(pMedium);
4485 }
4486
4487 setModified(IsModified_Storage);
4488
4489 mediumLock.release();
4490 multiLock.release();
4491 rc = onMediumChange(pAttach, aForce);
4492 multiLock.acquire();
4493 mediumLock.acquire();
4494
4495 /* On error roll back this change only. */
4496 if (FAILED(rc))
4497 {
4498 if (!pMedium.isNull())
4499 pMedium->removeBackReference(mData->mUuid);
4500 pAttach = findAttachment(mMediaData->mAttachments,
4501 aControllerName,
4502 aControllerPort,
4503 aDevice);
4504 /* If the attachment is gone in the meantime, bail out. */
4505 if (pAttach.isNull())
4506 return rc;
4507 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4508 if (!oldmedium.isNull())
4509 oldmedium->addBackReference(mData->mUuid);
4510 pAttach->updateMedium(oldmedium);
4511 }
4512
4513 mediumLock.release();
4514 multiLock.release();
4515
4516 mParent->saveModifiedRegistries();
4517
4518 return rc;
4519}
4520
4521STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
4522 LONG aControllerPort,
4523 LONG aDevice,
4524 IMedium **aMedium)
4525{
4526 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4527 aControllerName, aControllerPort, aDevice));
4528
4529 CheckComArgStrNotEmptyOrNull(aControllerName);
4530 CheckComArgOutPointerValid(aMedium);
4531
4532 AutoCaller autoCaller(this);
4533 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4534
4535 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4536
4537 *aMedium = NULL;
4538
4539 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4540 aControllerName,
4541 aControllerPort,
4542 aDevice);
4543 if (pAttach.isNull())
4544 return setError(VBOX_E_OBJECT_NOT_FOUND,
4545 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4546 aDevice, aControllerPort, aControllerName);
4547
4548 pAttach->getMedium().queryInterfaceTo(aMedium);
4549
4550 return S_OK;
4551}
4552
4553STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
4554{
4555 CheckComArgOutPointerValid(port);
4556 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
4557
4558 AutoCaller autoCaller(this);
4559 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4560
4561 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4562
4563 mSerialPorts[slot].queryInterfaceTo(port);
4564
4565 return S_OK;
4566}
4567
4568STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
4569{
4570 CheckComArgOutPointerValid(port);
4571 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
4572
4573 AutoCaller autoCaller(this);
4574 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4575
4576 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4577
4578 mParallelPorts[slot].queryInterfaceTo(port);
4579
4580 return S_OK;
4581}
4582
4583STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
4584{
4585 CheckComArgOutPointerValid(adapter);
4586 CheckComArgExpr(slot, slot < mNetworkAdapters.size());
4587
4588 AutoCaller autoCaller(this);
4589 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4590
4591 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4592
4593 mNetworkAdapters[slot].queryInterfaceTo(adapter);
4594
4595 return S_OK;
4596}
4597
4598STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
4599{
4600 CheckComArgOutSafeArrayPointerValid(aKeys);
4601
4602 AutoCaller autoCaller(this);
4603 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4604
4605 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4606
4607 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4608 int i = 0;
4609 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4610 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4611 ++it, ++i)
4612 {
4613 const Utf8Str &strKey = it->first;
4614 strKey.cloneTo(&saKeys[i]);
4615 }
4616 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
4617
4618 return S_OK;
4619 }
4620
4621 /**
4622 * @note Locks this object for reading.
4623 */
4624STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
4625 BSTR *aValue)
4626{
4627 CheckComArgStrNotEmptyOrNull(aKey);
4628 CheckComArgOutPointerValid(aValue);
4629
4630 AutoCaller autoCaller(this);
4631 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4632
4633 /* start with nothing found */
4634 Bstr bstrResult("");
4635
4636 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4637
4638 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
4639 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4640 // found:
4641 bstrResult = it->second; // source is a Utf8Str
4642
4643 /* return the result to caller (may be empty) */
4644 bstrResult.cloneTo(aValue);
4645
4646 return S_OK;
4647}
4648
4649 /**
4650 * @note Locks mParent for writing + this object for writing.
4651 */
4652STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
4653{
4654 CheckComArgStrNotEmptyOrNull(aKey);
4655
4656 AutoCaller autoCaller(this);
4657 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4658
4659 Utf8Str strKey(aKey);
4660 Utf8Str strValue(aValue);
4661 Utf8Str strOldValue; // empty
4662
4663 // locking note: we only hold the read lock briefly to look up the old value,
4664 // then release it and call the onExtraCanChange callbacks. There is a small
4665 // chance of a race insofar as the callback might be called twice if two callers
4666 // change the same key at the same time, but that's a much better solution
4667 // than the deadlock we had here before. The actual changing of the extradata
4668 // is then performed under the write lock and race-free.
4669
4670 // look up the old value first; if nothing has changed then we need not do anything
4671 {
4672 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4673 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
4674 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4675 strOldValue = it->second;
4676 }
4677
4678 bool fChanged;
4679 if ((fChanged = (strOldValue != strValue)))
4680 {
4681 // ask for permission from all listeners outside the locks;
4682 // onExtraDataCanChange() only briefly requests the VirtualBox
4683 // lock to copy the list of callbacks to invoke
4684 Bstr error;
4685 Bstr bstrValue(aValue);
4686
4687 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
4688 {
4689 const char *sep = error.isEmpty() ? "" : ": ";
4690 CBSTR err = error.raw();
4691 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4692 sep, err));
4693 return setError(E_ACCESSDENIED,
4694 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
4695 aKey,
4696 bstrValue.raw(),
4697 sep,
4698 err);
4699 }
4700
4701 // data is changing and change not vetoed: then write it out under the lock
4702 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4703
4704 if (isSnapshotMachine())
4705 {
4706 HRESULT rc = checkStateDependency(MutableStateDep);
4707 if (FAILED(rc)) return rc;
4708 }
4709
4710 if (strValue.isEmpty())
4711 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
4712 else
4713 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
4714 // creates a new key if needed
4715
4716 bool fNeedsGlobalSaveSettings = false;
4717 saveSettings(&fNeedsGlobalSaveSettings);
4718
4719 if (fNeedsGlobalSaveSettings)
4720 {
4721 // save the global settings; for that we should hold only the VirtualBox lock
4722 alock.release();
4723 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4724 mParent->saveSettings();
4725 }
4726 }
4727
4728 // fire notification outside the lock
4729 if (fChanged)
4730 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
4731
4732 return S_OK;
4733}
4734
4735STDMETHODIMP Machine::SaveSettings()
4736{
4737 AutoCaller autoCaller(this);
4738 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4739
4740 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4741
4742 /* when there was auto-conversion, we want to save the file even if
4743 * the VM is saved */
4744 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
4745 if (FAILED(rc)) return rc;
4746
4747 /* the settings file path may never be null */
4748 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4749
4750 /* save all VM data excluding snapshots */
4751 bool fNeedsGlobalSaveSettings = false;
4752 rc = saveSettings(&fNeedsGlobalSaveSettings);
4753 mlock.release();
4754
4755 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4756 {
4757 // save the global settings; for that we should hold only the VirtualBox lock
4758 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4759 rc = mParent->saveSettings();
4760 }
4761
4762 return rc;
4763}
4764
4765STDMETHODIMP Machine::DiscardSettings()
4766{
4767 AutoCaller autoCaller(this);
4768 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4769
4770 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4771
4772 HRESULT rc = checkStateDependency(MutableStateDep);
4773 if (FAILED(rc)) return rc;
4774
4775 /*
4776 * during this rollback, the session will be notified if data has
4777 * been actually changed
4778 */
4779 rollback(true /* aNotify */);
4780
4781 return S_OK;
4782}
4783
4784/** @note Locks objects! */
4785STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
4786 ComSafeArrayOut(IMedium*, aMedia))
4787{
4788 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
4789 AutoLimitedCaller autoCaller(this);
4790 AssertComRCReturnRC(autoCaller.rc());
4791
4792 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4793
4794 Guid id(getId());
4795
4796 if (mData->mSession.mState != SessionState_Unlocked)
4797 return setError(VBOX_E_INVALID_OBJECT_STATE,
4798 tr("Cannot unregister the machine '%s' while it is locked"),
4799 mUserData->s.strName.c_str());
4800
4801 // wait for state dependents to drop to zero
4802 ensureNoStateDependencies();
4803
4804 if (!mData->mAccessible)
4805 {
4806 // inaccessible maschines can only be unregistered; uninitialize ourselves
4807 // here because currently there may be no unregistered that are inaccessible
4808 // (this state combination is not supported). Note releasing the caller and
4809 // leaving the lock before calling uninit()
4810 alock.release();
4811 autoCaller.release();
4812
4813 uninit();
4814
4815 mParent->unregisterMachine(this, id);
4816 // calls VirtualBox::saveSettings()
4817
4818 return S_OK;
4819 }
4820
4821 HRESULT rc = S_OK;
4822
4823 // discard saved state
4824 if (mData->mMachineState == MachineState_Saved)
4825 {
4826 // add the saved state file to the list of files the caller should delete
4827 Assert(!mSSData->strStateFilePath.isEmpty());
4828 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4829
4830 mSSData->strStateFilePath.setNull();
4831
4832 // unconditionally set the machine state to powered off, we now
4833 // know no session has locked the machine
4834 mData->mMachineState = MachineState_PoweredOff;
4835 }
4836
4837 size_t cSnapshots = 0;
4838 if (mData->mFirstSnapshot)
4839 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
4840 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
4841 // fail now before we start detaching media
4842 return setError(VBOX_E_INVALID_OBJECT_STATE,
4843 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
4844 mUserData->s.strName.c_str(), cSnapshots);
4845
4846 // This list collects the medium objects from all medium attachments
4847 // which we will detach from the machine and its snapshots, in a specific
4848 // order which allows for closing all media without getting "media in use"
4849 // errors, simply by going through the list from the front to the back:
4850 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4851 // and must be closed before the parent media from the snapshots, or closing the parents
4852 // will fail because they still have children);
4853 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4854 // the root ("first") snapshot of the machine.
4855 MediaList llMedia;
4856
4857 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
4858 && mMediaData->mAttachments.size()
4859 )
4860 {
4861 // we have media attachments: detach them all and add the Medium objects to our list
4862 if (cleanupMode != CleanupMode_UnregisterOnly)
4863 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
4864 else
4865 return setError(VBOX_E_INVALID_OBJECT_STATE,
4866 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
4867 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
4868 }
4869
4870 if (cSnapshots)
4871 {
4872 // autoCleanup must be true here, or we would have failed above
4873
4874 // add the media from the medium attachments of the snapshots to llMedia
4875 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4876 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4877 // into the children first
4878
4879 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4880 MachineState_T oldState = mData->mMachineState;
4881 mData->mMachineState = MachineState_DeletingSnapshot;
4882
4883 // make a copy of the first snapshot so the refcount does not drop to 0
4884 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
4885 // because of the AutoCaller voodoo)
4886 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4887
4888 // GO!
4889 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
4890
4891 mData->mMachineState = oldState;
4892 }
4893
4894 if (FAILED(rc))
4895 {
4896 rollbackMedia();
4897 return rc;
4898 }
4899
4900 // commit all the media changes made above
4901 commitMedia();
4902
4903 mData->mRegistered = false;
4904
4905 // machine lock no longer needed
4906 alock.release();
4907
4908 // return media to caller
4909 SafeIfaceArray<IMedium> sfaMedia(llMedia);
4910 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
4911
4912 mParent->unregisterMachine(this, id);
4913 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
4914
4915 return S_OK;
4916}
4917
4918struct Machine::DeleteTask
4919{
4920 ComObjPtr<Machine> pMachine;
4921 RTCList<ComPtr<IMedium> > llMediums;
4922 StringsList llFilesToDelete;
4923 ComObjPtr<Progress> pProgress;
4924};
4925
4926STDMETHODIMP Machine::Delete(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
4927{
4928 LogFlowFuncEnter();
4929
4930 AutoCaller autoCaller(this);
4931 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4932
4933 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4934
4935 HRESULT rc = checkStateDependency(MutableStateDep);
4936 if (FAILED(rc)) return rc;
4937
4938 if (mData->mRegistered)
4939 return setError(VBOX_E_INVALID_VM_STATE,
4940 tr("Cannot delete settings of a registered machine"));
4941
4942 DeleteTask *pTask = new DeleteTask;
4943 pTask->pMachine = this;
4944 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
4945
4946 // collect files to delete
4947 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
4948
4949 for (size_t i = 0; i < sfaMedia.size(); ++i)
4950 {
4951 IMedium *pIMedium(sfaMedia[i]);
4952 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
4953 if (pMedium.isNull())
4954 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
4955 SafeArray<BSTR> ids;
4956 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
4957 if (FAILED(rc)) return rc;
4958 /* At this point the medium should not have any back references
4959 * anymore. If it has it is attached to another VM and *must* not
4960 * deleted. */
4961 if (ids.size() < 1)
4962 pTask->llMediums.append(pMedium);
4963 }
4964 if (mData->pMachineConfigFile->fileExists())
4965 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
4966
4967 pTask->pProgress.createObject();
4968 pTask->pProgress->init(getVirtualBox(),
4969 static_cast<IMachine*>(this) /* aInitiator */,
4970 Bstr(tr("Deleting files")).raw(),
4971 true /* fCancellable */,
4972 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
4973 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
4974
4975 int vrc = RTThreadCreate(NULL,
4976 Machine::deleteThread,
4977 (void*)pTask,
4978 0,
4979 RTTHREADTYPE_MAIN_WORKER,
4980 0,
4981 "MachineDelete");
4982
4983 pTask->pProgress.queryInterfaceTo(aProgress);
4984
4985 if (RT_FAILURE(vrc))
4986 {
4987 delete pTask;
4988 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
4989 }
4990
4991 LogFlowFuncLeave();
4992
4993 return S_OK;
4994}
4995
4996/**
4997 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
4998 * calls Machine::deleteTaskWorker() on the actual machine object.
4999 * @param Thread
5000 * @param pvUser
5001 * @return
5002 */
5003/*static*/
5004DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5005{
5006 LogFlowFuncEnter();
5007
5008 DeleteTask *pTask = (DeleteTask*)pvUser;
5009 Assert(pTask);
5010 Assert(pTask->pMachine);
5011 Assert(pTask->pProgress);
5012
5013 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5014 pTask->pProgress->notifyComplete(rc);
5015
5016 delete pTask;
5017
5018 LogFlowFuncLeave();
5019
5020 NOREF(Thread);
5021
5022 return VINF_SUCCESS;
5023}
5024
5025/**
5026 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5027 * @param task
5028 * @return
5029 */
5030HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5031{
5032 AutoCaller autoCaller(this);
5033 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5034
5035 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5036
5037 HRESULT rc = S_OK;
5038
5039 try
5040 {
5041 ULONG uLogHistoryCount = 3;
5042 ComPtr<ISystemProperties> systemProperties;
5043 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5044 if (FAILED(rc)) throw rc;
5045
5046 if (!systemProperties.isNull())
5047 {
5048 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5049 if (FAILED(rc)) throw rc;
5050 }
5051
5052 MachineState_T oldState = mData->mMachineState;
5053 setMachineState(MachineState_SettingUp);
5054 alock.release();
5055 for (size_t i = 0; i < task.llMediums.size(); ++i)
5056 {
5057 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5058 {
5059 AutoCaller mac(pMedium);
5060 if (FAILED(mac.rc())) throw mac.rc();
5061 Utf8Str strLocation = pMedium->getLocationFull();
5062 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5063 if (FAILED(rc)) throw rc;
5064 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5065 }
5066 ComPtr<IProgress> pProgress2;
5067 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5068 if (FAILED(rc)) throw rc;
5069 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5070 if (FAILED(rc)) throw rc;
5071 /* Check the result of the asynchrony process. */
5072 LONG iRc;
5073 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5074 if (FAILED(rc)) throw rc;
5075 /* If the thread of the progress object has an error, then
5076 * retrieve the error info from there, or it'll be lost. */
5077 if (FAILED(iRc))
5078 throw setError(ProgressErrorInfo(pProgress2));
5079 }
5080 setMachineState(oldState);
5081 alock.acquire();
5082
5083 // delete the files pushed on the task list by Machine::Delete()
5084 // (this includes saved states of the machine and snapshots and
5085 // medium storage files from the IMedium list passed in, and the
5086 // machine XML file)
5087 StringsList::const_iterator it = task.llFilesToDelete.begin();
5088 while (it != task.llFilesToDelete.end())
5089 {
5090 const Utf8Str &strFile = *it;
5091 LogFunc(("Deleting file %s\n", strFile.c_str()));
5092 int vrc = RTFileDelete(strFile.c_str());
5093 if (RT_FAILURE(vrc))
5094 throw setError(VBOX_E_IPRT_ERROR,
5095 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5096
5097 ++it;
5098 if (it == task.llFilesToDelete.end())
5099 {
5100 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5101 if (FAILED(rc)) throw rc;
5102 break;
5103 }
5104
5105 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5106 if (FAILED(rc)) throw rc;
5107 }
5108
5109 /* delete the settings only when the file actually exists */
5110 if (mData->pMachineConfigFile->fileExists())
5111 {
5112 /* Delete any backup or uncommitted XML files. Ignore failures.
5113 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5114 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5115 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5116 RTFileDelete(otherXml.c_str());
5117 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5118 RTFileDelete(otherXml.c_str());
5119
5120 /* delete the Logs folder, nothing important should be left
5121 * there (we don't check for errors because the user might have
5122 * some private files there that we don't want to delete) */
5123 Utf8Str logFolder;
5124 getLogFolder(logFolder);
5125 Assert(logFolder.length());
5126 if (RTDirExists(logFolder.c_str()))
5127 {
5128 /* Delete all VBox.log[.N] files from the Logs folder
5129 * (this must be in sync with the rotation logic in
5130 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5131 * files that may have been created by the GUI. */
5132 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5133 logFolder.c_str(), RTPATH_DELIMITER);
5134 RTFileDelete(log.c_str());
5135 log = Utf8StrFmt("%s%cVBox.png",
5136 logFolder.c_str(), RTPATH_DELIMITER);
5137 RTFileDelete(log.c_str());
5138 for (int i = uLogHistoryCount; i > 0; i--)
5139 {
5140 log = Utf8StrFmt("%s%cVBox.log.%d",
5141 logFolder.c_str(), RTPATH_DELIMITER, i);
5142 RTFileDelete(log.c_str());
5143 log = Utf8StrFmt("%s%cVBox.png.%d",
5144 logFolder.c_str(), RTPATH_DELIMITER, i);
5145 RTFileDelete(log.c_str());
5146 }
5147
5148 RTDirRemove(logFolder.c_str());
5149 }
5150
5151 /* delete the Snapshots folder, nothing important should be left
5152 * there (we don't check for errors because the user might have
5153 * some private files there that we don't want to delete) */
5154 Utf8Str strFullSnapshotFolder;
5155 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5156 Assert(!strFullSnapshotFolder.isEmpty());
5157 if (RTDirExists(strFullSnapshotFolder.c_str()))
5158 RTDirRemove(strFullSnapshotFolder.c_str());
5159
5160 // delete the directory that contains the settings file, but only
5161 // if it matches the VM name
5162 Utf8Str settingsDir;
5163 if (isInOwnDir(&settingsDir))
5164 RTDirRemove(settingsDir.c_str());
5165 }
5166
5167 alock.release();
5168
5169 mParent->saveModifiedRegistries();
5170 }
5171 catch (HRESULT aRC) { rc = aRC; }
5172
5173 return rc;
5174}
5175
5176STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5177{
5178 CheckComArgOutPointerValid(aSnapshot);
5179
5180 AutoCaller autoCaller(this);
5181 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5182
5183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5184
5185 ComObjPtr<Snapshot> pSnapshot;
5186 HRESULT rc;
5187
5188 if (!aNameOrId || !*aNameOrId)
5189 // null case (caller wants root snapshot): findSnapshotById() handles this
5190 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5191 else
5192 {
5193 Guid uuid(aNameOrId);
5194 if (!uuid.isEmpty())
5195 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5196 else
5197 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5198 }
5199 pSnapshot.queryInterfaceTo(aSnapshot);
5200
5201 return rc;
5202}
5203
5204STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5205{
5206 CheckComArgStrNotEmptyOrNull(aName);
5207 CheckComArgStrNotEmptyOrNull(aHostPath);
5208
5209 AutoCaller autoCaller(this);
5210 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5211
5212 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5213
5214 HRESULT rc = checkStateDependency(MutableStateDep);
5215 if (FAILED(rc)) return rc;
5216
5217 Utf8Str strName(aName);
5218
5219 ComObjPtr<SharedFolder> sharedFolder;
5220 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5221 if (SUCCEEDED(rc))
5222 return setError(VBOX_E_OBJECT_IN_USE,
5223 tr("Shared folder named '%s' already exists"),
5224 strName.c_str());
5225
5226 sharedFolder.createObject();
5227 rc = sharedFolder->init(getMachine(),
5228 strName,
5229 aHostPath,
5230 !!aWritable,
5231 !!aAutoMount,
5232 true /* fFailOnError */);
5233 if (FAILED(rc)) return rc;
5234
5235 setModified(IsModified_SharedFolders);
5236 mHWData.backup();
5237 mHWData->mSharedFolders.push_back(sharedFolder);
5238
5239 /* inform the direct session if any */
5240 alock.release();
5241 onSharedFolderChange();
5242
5243 return S_OK;
5244}
5245
5246STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5247{
5248 CheckComArgStrNotEmptyOrNull(aName);
5249
5250 AutoCaller autoCaller(this);
5251 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5252
5253 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5254
5255 HRESULT rc = checkStateDependency(MutableStateDep);
5256 if (FAILED(rc)) return rc;
5257
5258 ComObjPtr<SharedFolder> sharedFolder;
5259 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5260 if (FAILED(rc)) return rc;
5261
5262 setModified(IsModified_SharedFolders);
5263 mHWData.backup();
5264 mHWData->mSharedFolders.remove(sharedFolder);
5265
5266 /* inform the direct session if any */
5267 alock.release();
5268 onSharedFolderChange();
5269
5270 return S_OK;
5271}
5272
5273STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5274{
5275 CheckComArgOutPointerValid(aCanShow);
5276
5277 /* start with No */
5278 *aCanShow = FALSE;
5279
5280 AutoCaller autoCaller(this);
5281 AssertComRCReturnRC(autoCaller.rc());
5282
5283 ComPtr<IInternalSessionControl> directControl;
5284 {
5285 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5286
5287 if (mData->mSession.mState != SessionState_Locked)
5288 return setError(VBOX_E_INVALID_VM_STATE,
5289 tr("Machine is not locked for session (session state: %s)"),
5290 Global::stringifySessionState(mData->mSession.mState));
5291
5292 directControl = mData->mSession.mDirectControl;
5293 }
5294
5295 /* ignore calls made after #OnSessionEnd() is called */
5296 if (!directControl)
5297 return S_OK;
5298
5299 LONG64 dummy;
5300 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5301}
5302
5303STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5304{
5305 CheckComArgOutPointerValid(aWinId);
5306
5307 AutoCaller autoCaller(this);
5308 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5309
5310 ComPtr<IInternalSessionControl> directControl;
5311 {
5312 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5313
5314 if (mData->mSession.mState != SessionState_Locked)
5315 return setError(E_FAIL,
5316 tr("Machine is not locked for session (session state: %s)"),
5317 Global::stringifySessionState(mData->mSession.mState));
5318
5319 directControl = mData->mSession.mDirectControl;
5320 }
5321
5322 /* ignore calls made after #OnSessionEnd() is called */
5323 if (!directControl)
5324 return S_OK;
5325
5326 BOOL dummy;
5327 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5328}
5329
5330#ifdef VBOX_WITH_GUEST_PROPS
5331/**
5332 * Look up a guest property in VBoxSVC's internal structures.
5333 */
5334HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5335 BSTR *aValue,
5336 LONG64 *aTimestamp,
5337 BSTR *aFlags) const
5338{
5339 using namespace guestProp;
5340
5341 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5342 Utf8Str strName(aName);
5343 HWData::GuestPropertyList::const_iterator it;
5344
5345 for (it = mHWData->mGuestProperties.begin();
5346 it != mHWData->mGuestProperties.end(); ++it)
5347 {
5348 if (it->strName == strName)
5349 {
5350 char szFlags[MAX_FLAGS_LEN + 1];
5351 it->strValue.cloneTo(aValue);
5352 *aTimestamp = it->mTimestamp;
5353 writeFlags(it->mFlags, szFlags);
5354 Bstr(szFlags).cloneTo(aFlags);
5355 break;
5356 }
5357 }
5358 return S_OK;
5359}
5360
5361/**
5362 * Query the VM that a guest property belongs to for the property.
5363 * @returns E_ACCESSDENIED if the VM process is not available or not
5364 * currently handling queries and the lookup should then be done in
5365 * VBoxSVC.
5366 */
5367HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5368 BSTR *aValue,
5369 LONG64 *aTimestamp,
5370 BSTR *aFlags) const
5371{
5372 HRESULT rc;
5373 ComPtr<IInternalSessionControl> directControl;
5374 directControl = mData->mSession.mDirectControl;
5375
5376 /* fail if we were called after #OnSessionEnd() is called. This is a
5377 * silly race condition. */
5378
5379 if (!directControl)
5380 rc = E_ACCESSDENIED;
5381 else
5382 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5383 false /* isSetter */,
5384 aValue, aTimestamp, aFlags);
5385 return rc;
5386}
5387#endif // VBOX_WITH_GUEST_PROPS
5388
5389STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5390 BSTR *aValue,
5391 LONG64 *aTimestamp,
5392 BSTR *aFlags)
5393{
5394#ifndef VBOX_WITH_GUEST_PROPS
5395 ReturnComNotImplemented();
5396#else // VBOX_WITH_GUEST_PROPS
5397 CheckComArgStrNotEmptyOrNull(aName);
5398 CheckComArgOutPointerValid(aValue);
5399 CheckComArgOutPointerValid(aTimestamp);
5400 CheckComArgOutPointerValid(aFlags);
5401
5402 AutoCaller autoCaller(this);
5403 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5404
5405 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5406 if (rc == E_ACCESSDENIED)
5407 /* The VM is not running or the service is not (yet) accessible */
5408 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5409 return rc;
5410#endif // VBOX_WITH_GUEST_PROPS
5411}
5412
5413STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5414{
5415 LONG64 dummyTimestamp;
5416 Bstr dummyFlags;
5417 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5418}
5419
5420STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5421{
5422 Bstr dummyValue;
5423 Bstr dummyFlags;
5424 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5425}
5426
5427#ifdef VBOX_WITH_GUEST_PROPS
5428/**
5429 * Set a guest property in VBoxSVC's internal structures.
5430 */
5431HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5432 IN_BSTR aFlags)
5433{
5434 using namespace guestProp;
5435
5436 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5437 HRESULT rc = S_OK;
5438 HWData::GuestProperty property;
5439 property.mFlags = NILFLAG;
5440 bool found = false;
5441
5442 rc = checkStateDependency(MutableStateDep);
5443 if (FAILED(rc)) return rc;
5444
5445 try
5446 {
5447 Utf8Str utf8Name(aName);
5448 Utf8Str utf8Flags(aFlags);
5449 uint32_t fFlags = NILFLAG;
5450 if ( (aFlags != NULL)
5451 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags))
5452 )
5453 return setError(E_INVALIDARG,
5454 tr("Invalid flag values: '%ls'"),
5455 aFlags);
5456
5457 /** @todo r=bird: see efficiency rant in PushGuestProperty. (Yeah, I
5458 * know, this is simple and do an OK job atm.) */
5459 HWData::GuestPropertyList::iterator it;
5460 for (it = mHWData->mGuestProperties.begin();
5461 it != mHWData->mGuestProperties.end(); ++it)
5462 if (it->strName == utf8Name)
5463 {
5464 property = *it;
5465 if (it->mFlags & (RDONLYHOST))
5466 rc = setError(E_ACCESSDENIED,
5467 tr("The property '%ls' cannot be changed by the host"),
5468 aName);
5469 else
5470 {
5471 setModified(IsModified_MachineData);
5472 mHWData.backup(); // @todo r=dj backup in a loop?!?
5473
5474 /* The backup() operation invalidates our iterator, so
5475 * get a new one. */
5476 for (it = mHWData->mGuestProperties.begin();
5477 it->strName != utf8Name;
5478 ++it)
5479 ;
5480 mHWData->mGuestProperties.erase(it);
5481 }
5482 found = true;
5483 break;
5484 }
5485 if (found && SUCCEEDED(rc))
5486 {
5487 if (aValue)
5488 {
5489 RTTIMESPEC time;
5490 property.strValue = aValue;
5491 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5492 if (aFlags != NULL)
5493 property.mFlags = fFlags;
5494 mHWData->mGuestProperties.push_back(property);
5495 }
5496 }
5497 else if (SUCCEEDED(rc) && aValue)
5498 {
5499 RTTIMESPEC time;
5500 setModified(IsModified_MachineData);
5501 mHWData.backup();
5502 property.strName = aName;
5503 property.strValue = aValue;
5504 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5505 property.mFlags = fFlags;
5506 mHWData->mGuestProperties.push_back(property);
5507 }
5508 if ( SUCCEEDED(rc)
5509 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5510 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5511 RTSTR_MAX,
5512 utf8Name.c_str(),
5513 RTSTR_MAX,
5514 NULL)
5515 )
5516 )
5517 {
5518 /** @todo r=bird: Why aren't we leaving the lock here? The
5519 * same code in PushGuestProperty does... */
5520 mParent->onGuestPropertyChange(mData->mUuid, aName,
5521 aValue ? aValue : Bstr("").raw(),
5522 aFlags ? aFlags : Bstr("").raw());
5523 }
5524 }
5525 catch (std::bad_alloc &)
5526 {
5527 rc = E_OUTOFMEMORY;
5528 }
5529
5530 return rc;
5531}
5532
5533/**
5534 * Set a property on the VM that that property belongs to.
5535 * @returns E_ACCESSDENIED if the VM process is not available or not
5536 * currently handling queries and the setting should then be done in
5537 * VBoxSVC.
5538 */
5539HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
5540 IN_BSTR aFlags)
5541{
5542 HRESULT rc;
5543
5544 try
5545 {
5546 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5547
5548 BSTR dummy = NULL; /* will not be changed (setter) */
5549 LONG64 dummy64;
5550 if (!directControl)
5551 rc = E_ACCESSDENIED;
5552 else
5553 /** @todo Fix when adding DeleteGuestProperty(),
5554 see defect. */
5555 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
5556 true /* isSetter */,
5557 &dummy, &dummy64, &dummy);
5558 }
5559 catch (std::bad_alloc &)
5560 {
5561 rc = E_OUTOFMEMORY;
5562 }
5563
5564 return rc;
5565}
5566#endif // VBOX_WITH_GUEST_PROPS
5567
5568STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
5569 IN_BSTR aFlags)
5570{
5571#ifndef VBOX_WITH_GUEST_PROPS
5572 ReturnComNotImplemented();
5573#else // VBOX_WITH_GUEST_PROPS
5574 CheckComArgStrNotEmptyOrNull(aName);
5575 CheckComArgMaybeNull(aFlags);
5576 CheckComArgMaybeNull(aValue);
5577
5578 AutoCaller autoCaller(this);
5579 if (FAILED(autoCaller.rc()))
5580 return autoCaller.rc();
5581
5582 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
5583 if (rc == E_ACCESSDENIED)
5584 /* The VM is not running or the service is not (yet) accessible */
5585 rc = setGuestPropertyToService(aName, aValue, aFlags);
5586 return rc;
5587#endif // VBOX_WITH_GUEST_PROPS
5588}
5589
5590STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
5591{
5592 return SetGuestProperty(aName, aValue, NULL);
5593}
5594
5595STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
5596{
5597 return SetGuestProperty(aName, NULL, NULL);
5598}
5599
5600#ifdef VBOX_WITH_GUEST_PROPS
5601/**
5602 * Enumerate the guest properties in VBoxSVC's internal structures.
5603 */
5604HRESULT Machine::enumerateGuestPropertiesInService
5605 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5606 ComSafeArrayOut(BSTR, aValues),
5607 ComSafeArrayOut(LONG64, aTimestamps),
5608 ComSafeArrayOut(BSTR, aFlags))
5609{
5610 using namespace guestProp;
5611
5612 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5613 Utf8Str strPatterns(aPatterns);
5614
5615 /*
5616 * Look for matching patterns and build up a list.
5617 */
5618 HWData::GuestPropertyList propList;
5619 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
5620 it != mHWData->mGuestProperties.end();
5621 ++it)
5622 if ( strPatterns.isEmpty()
5623 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5624 RTSTR_MAX,
5625 it->strName.c_str(),
5626 RTSTR_MAX,
5627 NULL)
5628 )
5629 propList.push_back(*it);
5630
5631 /*
5632 * And build up the arrays for returning the property information.
5633 */
5634 size_t cEntries = propList.size();
5635 SafeArray<BSTR> names(cEntries);
5636 SafeArray<BSTR> values(cEntries);
5637 SafeArray<LONG64> timestamps(cEntries);
5638 SafeArray<BSTR> flags(cEntries);
5639 size_t iProp = 0;
5640 for (HWData::GuestPropertyList::iterator it = propList.begin();
5641 it != propList.end();
5642 ++it)
5643 {
5644 char szFlags[MAX_FLAGS_LEN + 1];
5645 it->strName.cloneTo(&names[iProp]);
5646 it->strValue.cloneTo(&values[iProp]);
5647 timestamps[iProp] = it->mTimestamp;
5648 writeFlags(it->mFlags, szFlags);
5649 Bstr(szFlags).cloneTo(&flags[iProp]);
5650 ++iProp;
5651 }
5652 names.detachTo(ComSafeArrayOutArg(aNames));
5653 values.detachTo(ComSafeArrayOutArg(aValues));
5654 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
5655 flags.detachTo(ComSafeArrayOutArg(aFlags));
5656 return S_OK;
5657}
5658
5659/**
5660 * Enumerate the properties managed by a VM.
5661 * @returns E_ACCESSDENIED if the VM process is not available or not
5662 * currently handling queries and the setting should then be done in
5663 * VBoxSVC.
5664 */
5665HRESULT Machine::enumerateGuestPropertiesOnVM
5666 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5667 ComSafeArrayOut(BSTR, aValues),
5668 ComSafeArrayOut(LONG64, aTimestamps),
5669 ComSafeArrayOut(BSTR, aFlags))
5670{
5671 HRESULT rc;
5672 ComPtr<IInternalSessionControl> directControl;
5673 directControl = mData->mSession.mDirectControl;
5674
5675 if (!directControl)
5676 rc = E_ACCESSDENIED;
5677 else
5678 rc = directControl->EnumerateGuestProperties
5679 (aPatterns, ComSafeArrayOutArg(aNames),
5680 ComSafeArrayOutArg(aValues),
5681 ComSafeArrayOutArg(aTimestamps),
5682 ComSafeArrayOutArg(aFlags));
5683 return rc;
5684}
5685#endif // VBOX_WITH_GUEST_PROPS
5686
5687STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
5688 ComSafeArrayOut(BSTR, aNames),
5689 ComSafeArrayOut(BSTR, aValues),
5690 ComSafeArrayOut(LONG64, aTimestamps),
5691 ComSafeArrayOut(BSTR, aFlags))
5692{
5693#ifndef VBOX_WITH_GUEST_PROPS
5694 ReturnComNotImplemented();
5695#else // VBOX_WITH_GUEST_PROPS
5696 CheckComArgMaybeNull(aPatterns);
5697 CheckComArgOutSafeArrayPointerValid(aNames);
5698 CheckComArgOutSafeArrayPointerValid(aValues);
5699 CheckComArgOutSafeArrayPointerValid(aTimestamps);
5700 CheckComArgOutSafeArrayPointerValid(aFlags);
5701
5702 AutoCaller autoCaller(this);
5703 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5704
5705 HRESULT rc = enumerateGuestPropertiesOnVM
5706 (aPatterns, ComSafeArrayOutArg(aNames),
5707 ComSafeArrayOutArg(aValues),
5708 ComSafeArrayOutArg(aTimestamps),
5709 ComSafeArrayOutArg(aFlags));
5710 if (rc == E_ACCESSDENIED)
5711 /* The VM is not running or the service is not (yet) accessible */
5712 rc = enumerateGuestPropertiesInService
5713 (aPatterns, ComSafeArrayOutArg(aNames),
5714 ComSafeArrayOutArg(aValues),
5715 ComSafeArrayOutArg(aTimestamps),
5716 ComSafeArrayOutArg(aFlags));
5717 return rc;
5718#endif // VBOX_WITH_GUEST_PROPS
5719}
5720
5721STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
5722 ComSafeArrayOut(IMediumAttachment*, aAttachments))
5723{
5724 MediaData::AttachmentList atts;
5725
5726 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
5727 if (FAILED(rc)) return rc;
5728
5729 SafeIfaceArray<IMediumAttachment> attachments(atts);
5730 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
5731
5732 return S_OK;
5733}
5734
5735STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
5736 LONG aControllerPort,
5737 LONG aDevice,
5738 IMediumAttachment **aAttachment)
5739{
5740 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5741 aControllerName, aControllerPort, aDevice));
5742
5743 CheckComArgStrNotEmptyOrNull(aControllerName);
5744 CheckComArgOutPointerValid(aAttachment);
5745
5746 AutoCaller autoCaller(this);
5747 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5748
5749 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5750
5751 *aAttachment = NULL;
5752
5753 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5754 aControllerName,
5755 aControllerPort,
5756 aDevice);
5757 if (pAttach.isNull())
5758 return setError(VBOX_E_OBJECT_NOT_FOUND,
5759 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5760 aDevice, aControllerPort, aControllerName);
5761
5762 pAttach.queryInterfaceTo(aAttachment);
5763
5764 return S_OK;
5765}
5766
5767STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
5768 StorageBus_T aConnectionType,
5769 IStorageController **controller)
5770{
5771 CheckComArgStrNotEmptyOrNull(aName);
5772
5773 if ( (aConnectionType <= StorageBus_Null)
5774 || (aConnectionType > StorageBus_SAS))
5775 return setError(E_INVALIDARG,
5776 tr("Invalid connection type: %d"),
5777 aConnectionType);
5778
5779 AutoCaller autoCaller(this);
5780 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5781
5782 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5783
5784 HRESULT rc = checkStateDependency(MutableStateDep);
5785 if (FAILED(rc)) return rc;
5786
5787 /* try to find one with the name first. */
5788 ComObjPtr<StorageController> ctrl;
5789
5790 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
5791 if (SUCCEEDED(rc))
5792 return setError(VBOX_E_OBJECT_IN_USE,
5793 tr("Storage controller named '%ls' already exists"),
5794 aName);
5795
5796 ctrl.createObject();
5797
5798 /* get a new instance number for the storage controller */
5799 ULONG ulInstance = 0;
5800 bool fBootable = true;
5801 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5802 it != mStorageControllers->end();
5803 ++it)
5804 {
5805 if ((*it)->getStorageBus() == aConnectionType)
5806 {
5807 ULONG ulCurInst = (*it)->getInstance();
5808
5809 if (ulCurInst >= ulInstance)
5810 ulInstance = ulCurInst + 1;
5811
5812 /* Only one controller of each type can be marked as bootable. */
5813 if ((*it)->getBootable())
5814 fBootable = false;
5815 }
5816 }
5817
5818 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5819 if (FAILED(rc)) return rc;
5820
5821 setModified(IsModified_Storage);
5822 mStorageControllers.backup();
5823 mStorageControllers->push_back(ctrl);
5824
5825 ctrl.queryInterfaceTo(controller);
5826
5827 /* inform the direct session if any */
5828 alock.release();
5829 onStorageControllerChange();
5830
5831 return S_OK;
5832}
5833
5834STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
5835 IStorageController **aStorageController)
5836{
5837 CheckComArgStrNotEmptyOrNull(aName);
5838
5839 AutoCaller autoCaller(this);
5840 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5841
5842 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5843
5844 ComObjPtr<StorageController> ctrl;
5845
5846 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5847 if (SUCCEEDED(rc))
5848 ctrl.queryInterfaceTo(aStorageController);
5849
5850 return rc;
5851}
5852
5853STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
5854 IStorageController **aStorageController)
5855{
5856 AutoCaller autoCaller(this);
5857 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5858
5859 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5860
5861 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5862 it != mStorageControllers->end();
5863 ++it)
5864 {
5865 if ((*it)->getInstance() == aInstance)
5866 {
5867 (*it).queryInterfaceTo(aStorageController);
5868 return S_OK;
5869 }
5870 }
5871
5872 return setError(VBOX_E_OBJECT_NOT_FOUND,
5873 tr("Could not find a storage controller with instance number '%lu'"),
5874 aInstance);
5875}
5876
5877STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
5878{
5879 AutoCaller autoCaller(this);
5880 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5881
5882 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5883
5884 HRESULT rc = checkStateDependency(MutableStateDep);
5885 if (FAILED(rc)) return rc;
5886
5887 ComObjPtr<StorageController> ctrl;
5888
5889 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5890 if (SUCCEEDED(rc))
5891 {
5892 /* Ensure that only one controller of each type is marked as bootable. */
5893 if (fBootable == TRUE)
5894 {
5895 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5896 it != mStorageControllers->end();
5897 ++it)
5898 {
5899 ComObjPtr<StorageController> aCtrl = (*it);
5900
5901 if ( (aCtrl->getName() != Utf8Str(aName))
5902 && aCtrl->getBootable() == TRUE
5903 && aCtrl->getStorageBus() == ctrl->getStorageBus()
5904 && aCtrl->getControllerType() == ctrl->getControllerType())
5905 {
5906 aCtrl->setBootable(FALSE);
5907 break;
5908 }
5909 }
5910 }
5911
5912 if (SUCCEEDED(rc))
5913 {
5914 ctrl->setBootable(fBootable);
5915 setModified(IsModified_Storage);
5916 }
5917 }
5918
5919 if (SUCCEEDED(rc))
5920 {
5921 /* inform the direct session if any */
5922 alock.release();
5923 onStorageControllerChange();
5924 }
5925
5926 return rc;
5927}
5928
5929STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
5930{
5931 CheckComArgStrNotEmptyOrNull(aName);
5932
5933 AutoCaller autoCaller(this);
5934 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5935
5936 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5937
5938 HRESULT rc = checkStateDependency(MutableStateDep);
5939 if (FAILED(rc)) return rc;
5940
5941 ComObjPtr<StorageController> ctrl;
5942 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5943 if (FAILED(rc)) return rc;
5944
5945 {
5946 /* find all attached devices to the appropriate storage controller and detach them all*/
5947 MediaData::AttachmentList::const_iterator endList = mMediaData->mAttachments.end();
5948 MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
5949 for (;it != endList; it++)
5950 {
5951 MediumAttachment *pAttachTemp = *it;
5952 AutoCaller localAutoCaller(pAttachTemp);
5953 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
5954
5955 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
5956
5957 if (pAttachTemp->getControllerName() == aName)
5958 {
5959 LONG port = pAttachTemp->getPort();
5960 LONG device = pAttachTemp->getDevice();
5961 rc = DetachDevice(aName, port, device);
5962 if (FAILED(rc)) return rc;
5963 }
5964 }
5965 }
5966
5967 /* We can remove it now. */
5968 setModified(IsModified_Storage);
5969 mStorageControllers.backup();
5970
5971 ctrl->unshare();
5972
5973 mStorageControllers->remove(ctrl);
5974
5975 /* inform the direct session if any */
5976 alock.release();
5977 onStorageControllerChange();
5978
5979 return S_OK;
5980}
5981
5982STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
5983 ULONG *puOriginX,
5984 ULONG *puOriginY,
5985 ULONG *puWidth,
5986 ULONG *puHeight,
5987 BOOL *pfEnabled)
5988{
5989 LogFlowThisFunc(("\n"));
5990
5991 CheckComArgNotNull(puOriginX);
5992 CheckComArgNotNull(puOriginY);
5993 CheckComArgNotNull(puWidth);
5994 CheckComArgNotNull(puHeight);
5995 CheckComArgNotNull(pfEnabled);
5996
5997 uint32_t u32OriginX= 0;
5998 uint32_t u32OriginY= 0;
5999 uint32_t u32Width = 0;
6000 uint32_t u32Height = 0;
6001 uint16_t u16Flags = 0;
6002
6003 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6004 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6005 if (RT_FAILURE(vrc))
6006 {
6007#ifdef RT_OS_WINDOWS
6008 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6009 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6010 * So just assign fEnable to TRUE again.
6011 * The right fix would be to change GUI API wrappers to make sure that parameters
6012 * are changed only if API succeeds.
6013 */
6014 *pfEnabled = TRUE;
6015#endif
6016 return setError(VBOX_E_IPRT_ERROR,
6017 tr("Saved guest size is not available (%Rrc)"),
6018 vrc);
6019 }
6020
6021 *puOriginX = u32OriginX;
6022 *puOriginY = u32OriginY;
6023 *puWidth = u32Width;
6024 *puHeight = u32Height;
6025 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6026
6027 return S_OK;
6028}
6029
6030STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6031{
6032 LogFlowThisFunc(("\n"));
6033
6034 CheckComArgNotNull(aSize);
6035 CheckComArgNotNull(aWidth);
6036 CheckComArgNotNull(aHeight);
6037
6038 if (aScreenId != 0)
6039 return E_NOTIMPL;
6040
6041 AutoCaller autoCaller(this);
6042 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6043
6044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6045
6046 uint8_t *pu8Data = NULL;
6047 uint32_t cbData = 0;
6048 uint32_t u32Width = 0;
6049 uint32_t u32Height = 0;
6050
6051 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6052
6053 if (RT_FAILURE(vrc))
6054 return setError(VBOX_E_IPRT_ERROR,
6055 tr("Saved screenshot data is not available (%Rrc)"),
6056 vrc);
6057
6058 *aSize = cbData;
6059 *aWidth = u32Width;
6060 *aHeight = u32Height;
6061
6062 freeSavedDisplayScreenshot(pu8Data);
6063
6064 return S_OK;
6065}
6066
6067STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6068{
6069 LogFlowThisFunc(("\n"));
6070
6071 CheckComArgNotNull(aWidth);
6072 CheckComArgNotNull(aHeight);
6073 CheckComArgOutSafeArrayPointerValid(aData);
6074
6075 if (aScreenId != 0)
6076 return E_NOTIMPL;
6077
6078 AutoCaller autoCaller(this);
6079 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6080
6081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6082
6083 uint8_t *pu8Data = NULL;
6084 uint32_t cbData = 0;
6085 uint32_t u32Width = 0;
6086 uint32_t u32Height = 0;
6087
6088 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6089
6090 if (RT_FAILURE(vrc))
6091 return setError(VBOX_E_IPRT_ERROR,
6092 tr("Saved screenshot data is not available (%Rrc)"),
6093 vrc);
6094
6095 *aWidth = u32Width;
6096 *aHeight = u32Height;
6097
6098 com::SafeArray<BYTE> bitmap(cbData);
6099 /* Convert pixels to format expected by the API caller. */
6100 if (aBGR)
6101 {
6102 /* [0] B, [1] G, [2] R, [3] A. */
6103 for (unsigned i = 0; i < cbData; i += 4)
6104 {
6105 bitmap[i] = pu8Data[i];
6106 bitmap[i + 1] = pu8Data[i + 1];
6107 bitmap[i + 2] = pu8Data[i + 2];
6108 bitmap[i + 3] = 0xff;
6109 }
6110 }
6111 else
6112 {
6113 /* [0] R, [1] G, [2] B, [3] A. */
6114 for (unsigned i = 0; i < cbData; i += 4)
6115 {
6116 bitmap[i] = pu8Data[i + 2];
6117 bitmap[i + 1] = pu8Data[i + 1];
6118 bitmap[i + 2] = pu8Data[i];
6119 bitmap[i + 3] = 0xff;
6120 }
6121 }
6122 bitmap.detachTo(ComSafeArrayOutArg(aData));
6123
6124 freeSavedDisplayScreenshot(pu8Data);
6125
6126 return S_OK;
6127}
6128
6129
6130STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6131{
6132 LogFlowThisFunc(("\n"));
6133
6134 CheckComArgNotNull(aWidth);
6135 CheckComArgNotNull(aHeight);
6136 CheckComArgOutSafeArrayPointerValid(aData);
6137
6138 if (aScreenId != 0)
6139 return E_NOTIMPL;
6140
6141 AutoCaller autoCaller(this);
6142 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6143
6144 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6145
6146 uint8_t *pu8Data = NULL;
6147 uint32_t cbData = 0;
6148 uint32_t u32Width = 0;
6149 uint32_t u32Height = 0;
6150
6151 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6152
6153 if (RT_FAILURE(vrc))
6154 return setError(VBOX_E_IPRT_ERROR,
6155 tr("Saved screenshot data is not available (%Rrc)"),
6156 vrc);
6157
6158 *aWidth = u32Width;
6159 *aHeight = u32Height;
6160
6161 HRESULT rc = S_OK;
6162 uint8_t *pu8PNG = NULL;
6163 uint32_t cbPNG = 0;
6164 uint32_t cxPNG = 0;
6165 uint32_t cyPNG = 0;
6166
6167 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6168
6169 if (RT_SUCCESS(vrc))
6170 {
6171 com::SafeArray<BYTE> screenData(cbPNG);
6172 screenData.initFrom(pu8PNG, cbPNG);
6173 if (pu8PNG)
6174 RTMemFree(pu8PNG);
6175 screenData.detachTo(ComSafeArrayOutArg(aData));
6176 }
6177 else
6178 {
6179 if (pu8PNG)
6180 RTMemFree(pu8PNG);
6181 return setError(VBOX_E_IPRT_ERROR,
6182 tr("Could not convert screenshot to PNG (%Rrc)"),
6183 vrc);
6184 }
6185
6186 freeSavedDisplayScreenshot(pu8Data);
6187
6188 return rc;
6189}
6190
6191STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6192{
6193 LogFlowThisFunc(("\n"));
6194
6195 CheckComArgNotNull(aSize);
6196 CheckComArgNotNull(aWidth);
6197 CheckComArgNotNull(aHeight);
6198
6199 if (aScreenId != 0)
6200 return E_NOTIMPL;
6201
6202 AutoCaller autoCaller(this);
6203 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6204
6205 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6206
6207 uint8_t *pu8Data = NULL;
6208 uint32_t cbData = 0;
6209 uint32_t u32Width = 0;
6210 uint32_t u32Height = 0;
6211
6212 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6213
6214 if (RT_FAILURE(vrc))
6215 return setError(VBOX_E_IPRT_ERROR,
6216 tr("Saved screenshot data is not available (%Rrc)"),
6217 vrc);
6218
6219 *aSize = cbData;
6220 *aWidth = u32Width;
6221 *aHeight = u32Height;
6222
6223 freeSavedDisplayScreenshot(pu8Data);
6224
6225 return S_OK;
6226}
6227
6228STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6229{
6230 LogFlowThisFunc(("\n"));
6231
6232 CheckComArgNotNull(aWidth);
6233 CheckComArgNotNull(aHeight);
6234 CheckComArgOutSafeArrayPointerValid(aData);
6235
6236 if (aScreenId != 0)
6237 return E_NOTIMPL;
6238
6239 AutoCaller autoCaller(this);
6240 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6241
6242 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6243
6244 uint8_t *pu8Data = NULL;
6245 uint32_t cbData = 0;
6246 uint32_t u32Width = 0;
6247 uint32_t u32Height = 0;
6248
6249 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6250
6251 if (RT_FAILURE(vrc))
6252 return setError(VBOX_E_IPRT_ERROR,
6253 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6254 vrc);
6255
6256 *aWidth = u32Width;
6257 *aHeight = u32Height;
6258
6259 com::SafeArray<BYTE> png(cbData);
6260 png.initFrom(pu8Data, cbData);
6261 png.detachTo(ComSafeArrayOutArg(aData));
6262
6263 freeSavedDisplayScreenshot(pu8Data);
6264
6265 return S_OK;
6266}
6267
6268STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6269{
6270 HRESULT rc = S_OK;
6271 LogFlowThisFunc(("\n"));
6272
6273 AutoCaller autoCaller(this);
6274 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6275
6276 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6277
6278 if (!mHWData->mCPUHotPlugEnabled)
6279 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6280
6281 if (aCpu >= mHWData->mCPUCount)
6282 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6283
6284 if (mHWData->mCPUAttached[aCpu])
6285 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6286
6287 alock.release();
6288 rc = onCPUChange(aCpu, false);
6289 alock.acquire();
6290 if (FAILED(rc)) return rc;
6291
6292 setModified(IsModified_MachineData);
6293 mHWData.backup();
6294 mHWData->mCPUAttached[aCpu] = true;
6295
6296 /* Save settings if online */
6297 if (Global::IsOnline(mData->mMachineState))
6298 saveSettings(NULL);
6299
6300 return S_OK;
6301}
6302
6303STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6304{
6305 HRESULT rc = S_OK;
6306 LogFlowThisFunc(("\n"));
6307
6308 AutoCaller autoCaller(this);
6309 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6310
6311 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6312
6313 if (!mHWData->mCPUHotPlugEnabled)
6314 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6315
6316 if (aCpu >= SchemaDefs::MaxCPUCount)
6317 return setError(E_INVALIDARG,
6318 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6319 SchemaDefs::MaxCPUCount);
6320
6321 if (!mHWData->mCPUAttached[aCpu])
6322 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6323
6324 /* CPU 0 can't be detached */
6325 if (aCpu == 0)
6326 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6327
6328 alock.release();
6329 rc = onCPUChange(aCpu, true);
6330 alock.acquire();
6331 if (FAILED(rc)) return rc;
6332
6333 setModified(IsModified_MachineData);
6334 mHWData.backup();
6335 mHWData->mCPUAttached[aCpu] = false;
6336
6337 /* Save settings if online */
6338 if (Global::IsOnline(mData->mMachineState))
6339 saveSettings(NULL);
6340
6341 return S_OK;
6342}
6343
6344STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6345{
6346 LogFlowThisFunc(("\n"));
6347
6348 CheckComArgNotNull(aCpuAttached);
6349
6350 *aCpuAttached = false;
6351
6352 AutoCaller autoCaller(this);
6353 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6354
6355 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6356
6357 /* If hotplug is enabled the CPU is always enabled. */
6358 if (!mHWData->mCPUHotPlugEnabled)
6359 {
6360 if (aCpu < mHWData->mCPUCount)
6361 *aCpuAttached = true;
6362 }
6363 else
6364 {
6365 if (aCpu < SchemaDefs::MaxCPUCount)
6366 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6367 }
6368
6369 return S_OK;
6370}
6371
6372STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6373{
6374 CheckComArgOutPointerValid(aName);
6375
6376 AutoCaller autoCaller(this);
6377 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6378
6379 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6380
6381 Utf8Str log = queryLogFilename(aIdx);
6382 if (!RTFileExists(log.c_str()))
6383 log.setNull();
6384 log.cloneTo(aName);
6385
6386 return S_OK;
6387}
6388
6389STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6390{
6391 LogFlowThisFunc(("\n"));
6392 CheckComArgOutSafeArrayPointerValid(aData);
6393 if (aSize < 0)
6394 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6395
6396 AutoCaller autoCaller(this);
6397 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6398
6399 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6400
6401 HRESULT rc = S_OK;
6402 Utf8Str log = queryLogFilename(aIdx);
6403
6404 /* do not unnecessarily hold the lock while doing something which does
6405 * not need the lock and potentially takes a long time. */
6406 alock.release();
6407
6408 /* Limit the chunk size to 32K for now, as that gives better performance
6409 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6410 * One byte expands to approx. 25 bytes of breathtaking XML. */
6411 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6412 com::SafeArray<BYTE> logData(cbData);
6413
6414 RTFILE LogFile;
6415 int vrc = RTFileOpen(&LogFile, log.c_str(),
6416 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6417 if (RT_SUCCESS(vrc))
6418 {
6419 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6420 if (RT_SUCCESS(vrc))
6421 logData.resize(cbData);
6422 else
6423 rc = setError(VBOX_E_IPRT_ERROR,
6424 tr("Could not read log file '%s' (%Rrc)"),
6425 log.c_str(), vrc);
6426 RTFileClose(LogFile);
6427 }
6428 else
6429 rc = setError(VBOX_E_IPRT_ERROR,
6430 tr("Could not open log file '%s' (%Rrc)"),
6431 log.c_str(), vrc);
6432
6433 if (FAILED(rc))
6434 logData.resize(0);
6435 logData.detachTo(ComSafeArrayOutArg(aData));
6436
6437 return rc;
6438}
6439
6440
6441/**
6442 * Currently this method doesn't attach device to the running VM,
6443 * just makes sure it's plugged on next VM start.
6444 */
6445STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6446{
6447 AutoCaller autoCaller(this);
6448 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6449
6450 // lock scope
6451 {
6452 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6453
6454 HRESULT rc = checkStateDependency(MutableStateDep);
6455 if (FAILED(rc)) return rc;
6456
6457 ChipsetType_T aChipset = ChipsetType_PIIX3;
6458 COMGETTER(ChipsetType)(&aChipset);
6459
6460 if (aChipset != ChipsetType_ICH9)
6461 {
6462 return setError(E_INVALIDARG,
6463 tr("Host PCI attachment only supported with ICH9 chipset"));
6464 }
6465
6466 // check if device with this host PCI address already attached
6467 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6468 it != mHWData->mPCIDeviceAssignments.end();
6469 ++it)
6470 {
6471 LONG iHostAddress = -1;
6472 ComPtr<PCIDeviceAttachment> pAttach;
6473 pAttach = *it;
6474 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6475 if (iHostAddress == hostAddress)
6476 return setError(E_INVALIDARG,
6477 tr("Device with host PCI address already attached to this VM"));
6478 }
6479
6480 ComObjPtr<PCIDeviceAttachment> pda;
6481 char name[32];
6482
6483 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6484 Bstr bname(name);
6485 pda.createObject();
6486 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
6487 setModified(IsModified_MachineData);
6488 mHWData.backup();
6489 mHWData->mPCIDeviceAssignments.push_back(pda);
6490 }
6491
6492 return S_OK;
6493}
6494
6495/**
6496 * Currently this method doesn't detach device from the running VM,
6497 * just makes sure it's not plugged on next VM start.
6498 */
6499STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
6500{
6501 AutoCaller autoCaller(this);
6502 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6503
6504 ComObjPtr<PCIDeviceAttachment> pAttach;
6505 bool fRemoved = false;
6506 HRESULT rc;
6507
6508 // lock scope
6509 {
6510 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6511
6512 rc = checkStateDependency(MutableStateDep);
6513 if (FAILED(rc)) return rc;
6514
6515 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6516 it != mHWData->mPCIDeviceAssignments.end();
6517 ++it)
6518 {
6519 LONG iHostAddress = -1;
6520 pAttach = *it;
6521 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6522 if (iHostAddress != -1 && iHostAddress == hostAddress)
6523 {
6524 setModified(IsModified_MachineData);
6525 mHWData.backup();
6526 mHWData->mPCIDeviceAssignments.remove(pAttach);
6527 fRemoved = true;
6528 break;
6529 }
6530 }
6531 }
6532
6533
6534 /* Fire event outside of the lock */
6535 if (fRemoved)
6536 {
6537 Assert(!pAttach.isNull());
6538 ComPtr<IEventSource> es;
6539 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6540 Assert(SUCCEEDED(rc));
6541 Bstr mid;
6542 rc = this->COMGETTER(Id)(mid.asOutParam());
6543 Assert(SUCCEEDED(rc));
6544 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6545 }
6546
6547 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6548 tr("No host PCI device %08x attached"),
6549 hostAddress
6550 );
6551}
6552
6553STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
6554{
6555 CheckComArgOutSafeArrayPointerValid(aAssignments);
6556
6557 AutoCaller autoCaller(this);
6558 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6559
6560 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6561
6562 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
6563 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
6564
6565 return S_OK;
6566}
6567
6568STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
6569{
6570 CheckComArgOutPointerValid(aBandwidthControl);
6571
6572 AutoCaller autoCaller(this);
6573 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6574
6575 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
6576
6577 return S_OK;
6578}
6579
6580STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
6581{
6582 CheckComArgOutPointerValid(pfEnabled);
6583 AutoCaller autoCaller(this);
6584 HRESULT hrc = autoCaller.rc();
6585 if (SUCCEEDED(hrc))
6586 {
6587 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6588 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
6589 }
6590 return hrc;
6591}
6592
6593STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
6594{
6595 AutoCaller autoCaller(this);
6596 HRESULT hrc = autoCaller.rc();
6597 if (SUCCEEDED(hrc))
6598 {
6599 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6600 hrc = checkStateDependency(MutableStateDep);
6601 if (SUCCEEDED(hrc))
6602 {
6603 hrc = mHWData.backupEx();
6604 if (SUCCEEDED(hrc))
6605 {
6606 setModified(IsModified_MachineData);
6607 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
6608 }
6609 }
6610 }
6611 return hrc;
6612}
6613
6614STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
6615{
6616 CheckComArgOutPointerValid(pbstrConfig);
6617 AutoCaller autoCaller(this);
6618 HRESULT hrc = autoCaller.rc();
6619 if (SUCCEEDED(hrc))
6620 {
6621 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6622 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
6623 }
6624 return hrc;
6625}
6626
6627STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
6628{
6629 CheckComArgStr(bstrConfig);
6630 AutoCaller autoCaller(this);
6631 HRESULT hrc = autoCaller.rc();
6632 if (SUCCEEDED(hrc))
6633 {
6634 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6635 hrc = checkStateDependency(MutableStateDep);
6636 if (SUCCEEDED(hrc))
6637 {
6638 hrc = mHWData.backupEx();
6639 if (SUCCEEDED(hrc))
6640 {
6641 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
6642 if (SUCCEEDED(hrc))
6643 setModified(IsModified_MachineData);
6644 }
6645 }
6646 }
6647 return hrc;
6648
6649}
6650
6651STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
6652{
6653 CheckComArgOutPointerValid(pfAllow);
6654 AutoCaller autoCaller(this);
6655 HRESULT hrc = autoCaller.rc();
6656 if (SUCCEEDED(hrc))
6657 {
6658 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6659 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
6660 }
6661 return hrc;
6662}
6663
6664STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
6665{
6666 AutoCaller autoCaller(this);
6667 HRESULT hrc = autoCaller.rc();
6668 if (SUCCEEDED(hrc))
6669 {
6670 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6671 hrc = checkStateDependency(MutableStateDep);
6672 if (SUCCEEDED(hrc))
6673 {
6674 hrc = mHWData.backupEx();
6675 if (SUCCEEDED(hrc))
6676 {
6677 setModified(IsModified_MachineData);
6678 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
6679 }
6680 }
6681 }
6682 return hrc;
6683}
6684
6685STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
6686{
6687 CheckComArgOutPointerValid(pfEnabled);
6688 AutoCaller autoCaller(this);
6689 HRESULT hrc = autoCaller.rc();
6690 if (SUCCEEDED(hrc))
6691 {
6692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6693 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
6694 }
6695 return hrc;
6696}
6697
6698STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
6699{
6700 AutoCaller autoCaller(this);
6701 HRESULT hrc = autoCaller.rc();
6702 if (SUCCEEDED(hrc))
6703 {
6704 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6705 hrc = checkStateDependency(MutableStateDep);
6706 if ( SUCCEEDED(hrc)
6707 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
6708 {
6709 AutostartDb *autostartDb = mParent->getAutostartDb();
6710 int vrc;
6711
6712 if (fEnabled)
6713 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6714 else
6715 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6716
6717 if (RT_SUCCESS(vrc))
6718 {
6719 hrc = mHWData.backupEx();
6720 if (SUCCEEDED(hrc))
6721 {
6722 setModified(IsModified_MachineData);
6723 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
6724 }
6725 }
6726 else if (vrc == VERR_NOT_SUPPORTED)
6727 hrc = setError(VBOX_E_NOT_SUPPORTED,
6728 tr("The VM autostart feature is not supported on this platform"));
6729 else if (vrc == VERR_PATH_NOT_FOUND)
6730 hrc = setError(E_FAIL,
6731 tr("The path to the autostart database is not set"));
6732 else
6733 hrc = setError(E_UNEXPECTED,
6734 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6735 fEnabled ? "Adding" : "Removing",
6736 mUserData->s.strName.c_str(), vrc);
6737 }
6738 }
6739 return hrc;
6740}
6741
6742STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
6743{
6744 CheckComArgOutPointerValid(puDelay);
6745 AutoCaller autoCaller(this);
6746 HRESULT hrc = autoCaller.rc();
6747 if (SUCCEEDED(hrc))
6748 {
6749 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6750 *puDelay = mHWData->mAutostart.uAutostartDelay;
6751 }
6752 return hrc;
6753}
6754
6755STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
6756{
6757 AutoCaller autoCaller(this);
6758 HRESULT hrc = autoCaller.rc();
6759 if (SUCCEEDED(hrc))
6760 {
6761 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6762 hrc = checkStateDependency(MutableStateDep);
6763 if (SUCCEEDED(hrc))
6764 {
6765 hrc = mHWData.backupEx();
6766 if (SUCCEEDED(hrc))
6767 {
6768 setModified(IsModified_MachineData);
6769 mHWData->mAutostart.uAutostartDelay = uDelay;
6770 }
6771 }
6772 }
6773 return hrc;
6774}
6775
6776STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
6777{
6778 CheckComArgOutPointerValid(penmAutostopType);
6779 AutoCaller autoCaller(this);
6780 HRESULT hrc = autoCaller.rc();
6781 if (SUCCEEDED(hrc))
6782 {
6783 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6784 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
6785 }
6786 return hrc;
6787}
6788
6789STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
6790{
6791 AutoCaller autoCaller(this);
6792 HRESULT hrc = autoCaller.rc();
6793 if (SUCCEEDED(hrc))
6794 {
6795 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6796 hrc = checkStateDependency(MutableStateDep);
6797 if ( SUCCEEDED(hrc)
6798 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
6799 {
6800 AutostartDb *autostartDb = mParent->getAutostartDb();
6801 int vrc;
6802
6803 if (enmAutostopType != AutostopType_Disabled)
6804 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6805 else
6806 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6807
6808 if (RT_SUCCESS(vrc))
6809 {
6810 hrc = mHWData.backupEx();
6811 if (SUCCEEDED(hrc))
6812 {
6813 setModified(IsModified_MachineData);
6814 mHWData->mAutostart.enmAutostopType = enmAutostopType;
6815 }
6816 }
6817 else if (vrc == VERR_NOT_SUPPORTED)
6818 hrc = setError(VBOX_E_NOT_SUPPORTED,
6819 tr("The VM autostop feature is not supported on this platform"));
6820 else if (vrc == VERR_PATH_NOT_FOUND)
6821 hrc = setError(E_FAIL,
6822 tr("The path to the autostart database is not set"));
6823 else
6824 hrc = setError(E_UNEXPECTED,
6825 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6826 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6827 mUserData->s.strName.c_str(), vrc);
6828 }
6829 }
6830 return hrc;
6831}
6832
6833
6834STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
6835{
6836 LogFlowFuncEnter();
6837
6838 CheckComArgNotNull(pTarget);
6839 CheckComArgOutPointerValid(pProgress);
6840
6841 /* Convert the options. */
6842 RTCList<CloneOptions_T> optList;
6843 if (options != NULL)
6844 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
6845
6846 if (optList.contains(CloneOptions_Link))
6847 {
6848 if (!isSnapshotMachine())
6849 return setError(E_INVALIDARG,
6850 tr("Linked clone can only be created from a snapshot"));
6851 if (mode != CloneMode_MachineState)
6852 return setError(E_INVALIDARG,
6853 tr("Linked clone can only be created for a single machine state"));
6854 }
6855 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6856
6857 AutoCaller autoCaller(this);
6858 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6859
6860
6861 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
6862
6863 HRESULT rc = pWorker->start(pProgress);
6864
6865 LogFlowFuncLeave();
6866
6867 return rc;
6868}
6869
6870// public methods for internal purposes
6871/////////////////////////////////////////////////////////////////////////////
6872
6873/**
6874 * Adds the given IsModified_* flag to the dirty flags of the machine.
6875 * This must be called either during loadSettings or under the machine write lock.
6876 * @param fl
6877 */
6878void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
6879{
6880 mData->flModifications |= fl;
6881 if (fAllowStateModification && isStateModificationAllowed())
6882 mData->mCurrentStateModified = true;
6883}
6884
6885/**
6886 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
6887 * care of the write locking.
6888 *
6889 * @param fModifications The flag to add.
6890 */
6891void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
6892{
6893 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6894 setModified(fModification, fAllowStateModification);
6895}
6896
6897/**
6898 * Saves the registry entry of this machine to the given configuration node.
6899 *
6900 * @param aEntryNode Node to save the registry entry to.
6901 *
6902 * @note locks this object for reading.
6903 */
6904HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
6905{
6906 AutoLimitedCaller autoCaller(this);
6907 AssertComRCReturnRC(autoCaller.rc());
6908
6909 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6910
6911 data.uuid = mData->mUuid;
6912 data.strSettingsFile = mData->m_strConfigFile;
6913
6914 return S_OK;
6915}
6916
6917/**
6918 * Calculates the absolute path of the given path taking the directory of the
6919 * machine settings file as the current directory.
6920 *
6921 * @param aPath Path to calculate the absolute path for.
6922 * @param aResult Where to put the result (used only on success, can be the
6923 * same Utf8Str instance as passed in @a aPath).
6924 * @return IPRT result.
6925 *
6926 * @note Locks this object for reading.
6927 */
6928int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
6929{
6930 AutoCaller autoCaller(this);
6931 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
6932
6933 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6934
6935 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
6936
6937 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
6938
6939 strSettingsDir.stripFilename();
6940 char folder[RTPATH_MAX];
6941 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
6942 if (RT_SUCCESS(vrc))
6943 aResult = folder;
6944
6945 return vrc;
6946}
6947
6948/**
6949 * Copies strSource to strTarget, making it relative to the machine folder
6950 * if it is a subdirectory thereof, or simply copying it otherwise.
6951 *
6952 * @param strSource Path to evaluate and copy.
6953 * @param strTarget Buffer to receive target path.
6954 *
6955 * @note Locks this object for reading.
6956 */
6957void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
6958 Utf8Str &strTarget)
6959{
6960 AutoCaller autoCaller(this);
6961 AssertComRCReturn(autoCaller.rc(), (void)0);
6962
6963 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6964
6965 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
6966 // use strTarget as a temporary buffer to hold the machine settings dir
6967 strTarget = mData->m_strConfigFileFull;
6968 strTarget.stripFilename();
6969 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
6970 {
6971 // is relative: then append what's left
6972 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
6973 // for empty paths (only possible for subdirs) use "." to avoid
6974 // triggering default settings for not present config attributes.
6975 if (strTarget.isEmpty())
6976 strTarget = ".";
6977 }
6978 else
6979 // is not relative: then overwrite
6980 strTarget = strSource;
6981}
6982
6983/**
6984 * Returns the full path to the machine's log folder in the
6985 * \a aLogFolder argument.
6986 */
6987void Machine::getLogFolder(Utf8Str &aLogFolder)
6988{
6989 AutoCaller autoCaller(this);
6990 AssertComRCReturnVoid(autoCaller.rc());
6991
6992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6993
6994 char szTmp[RTPATH_MAX];
6995 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
6996 if (RT_SUCCESS(vrc))
6997 {
6998 if (szTmp[0] && !mUserData.isNull())
6999 {
7000 char szTmp2[RTPATH_MAX];
7001 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7002 if (RT_SUCCESS(vrc))
7003 aLogFolder = BstrFmt("%s%c%s",
7004 szTmp2,
7005 RTPATH_DELIMITER,
7006 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7007 }
7008 else
7009 vrc = VERR_PATH_IS_RELATIVE;
7010 }
7011
7012 if (RT_FAILURE(vrc))
7013 {
7014 // fallback if VBOX_USER_LOGHOME is not set or invalid
7015 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7016 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7017 aLogFolder.append(RTPATH_DELIMITER);
7018 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7019 }
7020}
7021
7022/**
7023 * Returns the full path to the machine's log file for an given index.
7024 */
7025Utf8Str Machine::queryLogFilename(ULONG idx)
7026{
7027 Utf8Str logFolder;
7028 getLogFolder(logFolder);
7029 Assert(logFolder.length());
7030 Utf8Str log;
7031 if (idx == 0)
7032 log = Utf8StrFmt("%s%cVBox.log",
7033 logFolder.c_str(), RTPATH_DELIMITER);
7034 else
7035 log = Utf8StrFmt("%s%cVBox.log.%d",
7036 logFolder.c_str(), RTPATH_DELIMITER, idx);
7037 return log;
7038}
7039
7040/**
7041 * Composes a unique saved state filename based on the current system time. The filename is
7042 * granular to the second so this will work so long as no more than one snapshot is taken on
7043 * a machine per second.
7044 *
7045 * Before version 4.1, we used this formula for saved state files:
7046 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7047 * which no longer works because saved state files can now be shared between the saved state of the
7048 * "saved" machine and an online snapshot, and the following would cause problems:
7049 * 1) save machine
7050 * 2) create online snapshot from that machine state --> reusing saved state file
7051 * 3) save machine again --> filename would be reused, breaking the online snapshot
7052 *
7053 * So instead we now use a timestamp.
7054 *
7055 * @param str
7056 */
7057void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7058{
7059 AutoCaller autoCaller(this);
7060 AssertComRCReturnVoid(autoCaller.rc());
7061
7062 {
7063 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7064 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7065 }
7066
7067 RTTIMESPEC ts;
7068 RTTimeNow(&ts);
7069 RTTIME time;
7070 RTTimeExplode(&time, &ts);
7071
7072 strStateFilePath += RTPATH_DELIMITER;
7073 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7074 time.i32Year, time.u8Month, time.u8MonthDay,
7075 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7076}
7077
7078/**
7079 * @note Locks this object for writing, calls the client process
7080 * (inside the lock).
7081 */
7082HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7083 const Utf8Str &strType,
7084 const Utf8Str &strEnvironment,
7085 ProgressProxy *aProgress)
7086{
7087 LogFlowThisFuncEnter();
7088
7089 AssertReturn(aControl, E_FAIL);
7090 AssertReturn(aProgress, E_FAIL);
7091
7092 AutoCaller autoCaller(this);
7093 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7094
7095 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7096
7097 if (!mData->mRegistered)
7098 return setError(E_UNEXPECTED,
7099 tr("The machine '%s' is not registered"),
7100 mUserData->s.strName.c_str());
7101
7102 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7103
7104 if ( mData->mSession.mState == SessionState_Locked
7105 || mData->mSession.mState == SessionState_Spawning
7106 || mData->mSession.mState == SessionState_Unlocking)
7107 return setError(VBOX_E_INVALID_OBJECT_STATE,
7108 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7109 mUserData->s.strName.c_str());
7110
7111 /* may not be busy */
7112 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7113
7114 /* get the path to the executable */
7115 char szPath[RTPATH_MAX];
7116 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7117 size_t sz = strlen(szPath);
7118 szPath[sz++] = RTPATH_DELIMITER;
7119 szPath[sz] = 0;
7120 char *cmd = szPath + sz;
7121 sz = RTPATH_MAX - sz;
7122
7123 int vrc = VINF_SUCCESS;
7124 RTPROCESS pid = NIL_RTPROCESS;
7125
7126 RTENV env = RTENV_DEFAULT;
7127
7128 if (!strEnvironment.isEmpty())
7129 {
7130 char *newEnvStr = NULL;
7131
7132 do
7133 {
7134 /* clone the current environment */
7135 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7136 AssertRCBreakStmt(vrc2, vrc = vrc2);
7137
7138 newEnvStr = RTStrDup(strEnvironment.c_str());
7139 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7140
7141 /* put new variables to the environment
7142 * (ignore empty variable names here since RTEnv API
7143 * intentionally doesn't do that) */
7144 char *var = newEnvStr;
7145 for (char *p = newEnvStr; *p; ++p)
7146 {
7147 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7148 {
7149 *p = '\0';
7150 if (*var)
7151 {
7152 char *val = strchr(var, '=');
7153 if (val)
7154 {
7155 *val++ = '\0';
7156 vrc2 = RTEnvSetEx(env, var, val);
7157 }
7158 else
7159 vrc2 = RTEnvUnsetEx(env, var);
7160 if (RT_FAILURE(vrc2))
7161 break;
7162 }
7163 var = p + 1;
7164 }
7165 }
7166 if (RT_SUCCESS(vrc2) && *var)
7167 vrc2 = RTEnvPutEx(env, var);
7168
7169 AssertRCBreakStmt(vrc2, vrc = vrc2);
7170 }
7171 while (0);
7172
7173 if (newEnvStr != NULL)
7174 RTStrFree(newEnvStr);
7175 }
7176
7177 /* Qt is default */
7178#ifdef VBOX_WITH_QTGUI
7179 if (strType == "gui" || strType == "GUI/Qt")
7180 {
7181# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7182 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7183# else
7184 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7185# endif
7186 Assert(sz >= sizeof(VirtualBox_exe));
7187 strcpy(cmd, VirtualBox_exe);
7188
7189 Utf8Str idStr = mData->mUuid.toString();
7190 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7191 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7192 }
7193#else /* !VBOX_WITH_QTGUI */
7194 if (0)
7195 ;
7196#endif /* VBOX_WITH_QTGUI */
7197
7198 else
7199
7200#ifdef VBOX_WITH_VBOXSDL
7201 if (strType == "sdl" || strType == "GUI/SDL")
7202 {
7203 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7204 Assert(sz >= sizeof(VBoxSDL_exe));
7205 strcpy(cmd, VBoxSDL_exe);
7206
7207 Utf8Str idStr = mData->mUuid.toString();
7208 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7209 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7210 }
7211#else /* !VBOX_WITH_VBOXSDL */
7212 if (0)
7213 ;
7214#endif /* !VBOX_WITH_VBOXSDL */
7215
7216 else
7217
7218#ifdef VBOX_WITH_HEADLESS
7219 if ( strType == "headless"
7220 || strType == "capture"
7221 || strType == "vrdp" /* Deprecated. Same as headless. */
7222 )
7223 {
7224 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7225 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7226 * and a VM works even if the server has not been installed.
7227 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7228 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7229 * differently in 4.0 and 3.x.
7230 */
7231 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7232 Assert(sz >= sizeof(VBoxHeadless_exe));
7233 strcpy(cmd, VBoxHeadless_exe);
7234
7235 Utf8Str idStr = mData->mUuid.toString();
7236 /* Leave space for "--capture" arg. */
7237 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7238 "--startvm", idStr.c_str(),
7239 "--vrde", "config",
7240 0, /* For "--capture". */
7241 0 };
7242 if (strType == "capture")
7243 {
7244 unsigned pos = RT_ELEMENTS(args) - 2;
7245 args[pos] = "--capture";
7246 }
7247 vrc = RTProcCreate(szPath, args, env,
7248#ifdef RT_OS_WINDOWS
7249 RTPROC_FLAGS_NO_WINDOW
7250#else
7251 0
7252#endif
7253 , &pid);
7254 }
7255#else /* !VBOX_WITH_HEADLESS */
7256 if (0)
7257 ;
7258#endif /* !VBOX_WITH_HEADLESS */
7259 else
7260 {
7261 RTEnvDestroy(env);
7262 return setError(E_INVALIDARG,
7263 tr("Invalid session type: '%s'"),
7264 strType.c_str());
7265 }
7266
7267 RTEnvDestroy(env);
7268
7269 if (RT_FAILURE(vrc))
7270 return setError(VBOX_E_IPRT_ERROR,
7271 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7272 mUserData->s.strName.c_str(), vrc);
7273
7274 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7275
7276 /*
7277 * Note that we don't release the lock here before calling the client,
7278 * because it doesn't need to call us back if called with a NULL argument.
7279 * Releasing the lock here is dangerous because we didn't prepare the
7280 * launch data yet, but the client we've just started may happen to be
7281 * too fast and call openSession() that will fail (because of PID, etc.),
7282 * so that the Machine will never get out of the Spawning session state.
7283 */
7284
7285 /* inform the session that it will be a remote one */
7286 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7287 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
7288 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7289
7290 if (FAILED(rc))
7291 {
7292 /* restore the session state */
7293 mData->mSession.mState = SessionState_Unlocked;
7294 /* The failure may occur w/o any error info (from RPC), so provide one */
7295 return setError(VBOX_E_VM_ERROR,
7296 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7297 }
7298
7299 /* attach launch data to the machine */
7300 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7301 mData->mSession.mRemoteControls.push_back(aControl);
7302 mData->mSession.mProgress = aProgress;
7303 mData->mSession.mPID = pid;
7304 mData->mSession.mState = SessionState_Spawning;
7305 mData->mSession.mType = strType;
7306
7307 LogFlowThisFuncLeave();
7308 return S_OK;
7309}
7310
7311/**
7312 * Returns @c true if the given machine has an open direct session and returns
7313 * the session machine instance and additional session data (on some platforms)
7314 * if so.
7315 *
7316 * Note that when the method returns @c false, the arguments remain unchanged.
7317 *
7318 * @param aMachine Session machine object.
7319 * @param aControl Direct session control object (optional).
7320 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7321 *
7322 * @note locks this object for reading.
7323 */
7324#if defined(RT_OS_WINDOWS)
7325bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7326 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7327 HANDLE *aIPCSem /*= NULL*/,
7328 bool aAllowClosing /*= false*/)
7329#elif defined(RT_OS_OS2)
7330bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7331 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7332 HMTX *aIPCSem /*= NULL*/,
7333 bool aAllowClosing /*= false*/)
7334#else
7335bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7336 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7337 bool aAllowClosing /*= false*/)
7338#endif
7339{
7340 AutoLimitedCaller autoCaller(this);
7341 AssertComRCReturn(autoCaller.rc(), false);
7342
7343 /* just return false for inaccessible machines */
7344 if (autoCaller.state() != Ready)
7345 return false;
7346
7347 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7348
7349 if ( mData->mSession.mState == SessionState_Locked
7350 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7351 )
7352 {
7353 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7354
7355 aMachine = mData->mSession.mMachine;
7356
7357 if (aControl != NULL)
7358 *aControl = mData->mSession.mDirectControl;
7359
7360#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7361 /* Additional session data */
7362 if (aIPCSem != NULL)
7363 *aIPCSem = aMachine->mIPCSem;
7364#endif
7365 return true;
7366 }
7367
7368 return false;
7369}
7370
7371/**
7372 * Returns @c true if the given machine has an spawning direct session and
7373 * returns and additional session data (on some platforms) if so.
7374 *
7375 * Note that when the method returns @c false, the arguments remain unchanged.
7376 *
7377 * @param aPID PID of the spawned direct session process.
7378 *
7379 * @note locks this object for reading.
7380 */
7381#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7382bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7383#else
7384bool Machine::isSessionSpawning()
7385#endif
7386{
7387 AutoLimitedCaller autoCaller(this);
7388 AssertComRCReturn(autoCaller.rc(), false);
7389
7390 /* just return false for inaccessible machines */
7391 if (autoCaller.state() != Ready)
7392 return false;
7393
7394 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7395
7396 if (mData->mSession.mState == SessionState_Spawning)
7397 {
7398#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7399 /* Additional session data */
7400 if (aPID != NULL)
7401 {
7402 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
7403 *aPID = mData->mSession.mPID;
7404 }
7405#endif
7406 return true;
7407 }
7408
7409 return false;
7410}
7411
7412/**
7413 * Called from the client watcher thread to check for unexpected client process
7414 * death during Session_Spawning state (e.g. before it successfully opened a
7415 * direct session).
7416 *
7417 * On Win32 and on OS/2, this method is called only when we've got the
7418 * direct client's process termination notification, so it always returns @c
7419 * true.
7420 *
7421 * On other platforms, this method returns @c true if the client process is
7422 * terminated and @c false if it's still alive.
7423 *
7424 * @note Locks this object for writing.
7425 */
7426bool Machine::checkForSpawnFailure()
7427{
7428 AutoCaller autoCaller(this);
7429 if (!autoCaller.isOk())
7430 {
7431 /* nothing to do */
7432 LogFlowThisFunc(("Already uninitialized!\n"));
7433 return true;
7434 }
7435
7436 /* VirtualBox::addProcessToReap() needs a write lock */
7437 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
7438
7439 if (mData->mSession.mState != SessionState_Spawning)
7440 {
7441 /* nothing to do */
7442 LogFlowThisFunc(("Not spawning any more!\n"));
7443 return true;
7444 }
7445
7446 HRESULT rc = S_OK;
7447
7448#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7449
7450 /* the process was already unexpectedly terminated, we just need to set an
7451 * error and finalize session spawning */
7452 rc = setError(E_FAIL,
7453 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
7454 getName().c_str());
7455#else
7456
7457 /* PID not yet initialized, skip check. */
7458 if (mData->mSession.mPID == NIL_RTPROCESS)
7459 return false;
7460
7461 RTPROCSTATUS status;
7462 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
7463 &status);
7464
7465 if (vrc != VERR_PROCESS_RUNNING)
7466 {
7467 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7468 rc = setError(E_FAIL,
7469 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7470 getName().c_str(), status.iStatus);
7471 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7472 rc = setError(E_FAIL,
7473 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7474 getName().c_str(), status.iStatus);
7475 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7476 rc = setError(E_FAIL,
7477 tr("The virtual machine '%s' has terminated abnormally"),
7478 getName().c_str(), status.iStatus);
7479 else
7480 rc = setError(E_FAIL,
7481 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7482 getName().c_str(), rc);
7483 }
7484
7485#endif
7486
7487 if (FAILED(rc))
7488 {
7489 /* Close the remote session, remove the remote control from the list
7490 * and reset session state to Closed (@note keep the code in sync with
7491 * the relevant part in checkForSpawnFailure()). */
7492
7493 Assert(mData->mSession.mRemoteControls.size() == 1);
7494 if (mData->mSession.mRemoteControls.size() == 1)
7495 {
7496 ErrorInfoKeeper eik;
7497 mData->mSession.mRemoteControls.front()->Uninitialize();
7498 }
7499
7500 mData->mSession.mRemoteControls.clear();
7501 mData->mSession.mState = SessionState_Unlocked;
7502
7503 /* finalize the progress after setting the state */
7504 if (!mData->mSession.mProgress.isNull())
7505 {
7506 mData->mSession.mProgress->notifyComplete(rc);
7507 mData->mSession.mProgress.setNull();
7508 }
7509
7510 mParent->addProcessToReap(mData->mSession.mPID);
7511 mData->mSession.mPID = NIL_RTPROCESS;
7512
7513 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7514 return true;
7515 }
7516
7517 return false;
7518}
7519
7520/**
7521 * Checks whether the machine can be registered. If so, commits and saves
7522 * all settings.
7523 *
7524 * @note Must be called from mParent's write lock. Locks this object and
7525 * children for writing.
7526 */
7527HRESULT Machine::prepareRegister()
7528{
7529 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7530
7531 AutoLimitedCaller autoCaller(this);
7532 AssertComRCReturnRC(autoCaller.rc());
7533
7534 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7535
7536 /* wait for state dependents to drop to zero */
7537 ensureNoStateDependencies();
7538
7539 if (!mData->mAccessible)
7540 return setError(VBOX_E_INVALID_OBJECT_STATE,
7541 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7542 mUserData->s.strName.c_str(),
7543 mData->mUuid.toString().c_str());
7544
7545 AssertReturn(autoCaller.state() == Ready, E_FAIL);
7546
7547 if (mData->mRegistered)
7548 return setError(VBOX_E_INVALID_OBJECT_STATE,
7549 tr("The machine '%s' with UUID {%s} is already registered"),
7550 mUserData->s.strName.c_str(),
7551 mData->mUuid.toString().c_str());
7552
7553 HRESULT rc = S_OK;
7554
7555 // Ensure the settings are saved. If we are going to be registered and
7556 // no config file exists yet, create it by calling saveSettings() too.
7557 if ( (mData->flModifications)
7558 || (!mData->pMachineConfigFile->fileExists())
7559 )
7560 {
7561 rc = saveSettings(NULL);
7562 // no need to check whether VirtualBox.xml needs saving too since
7563 // we can't have a machine XML file rename pending
7564 if (FAILED(rc)) return rc;
7565 }
7566
7567 /* more config checking goes here */
7568
7569 if (SUCCEEDED(rc))
7570 {
7571 /* we may have had implicit modifications we want to fix on success */
7572 commit();
7573
7574 mData->mRegistered = true;
7575 }
7576 else
7577 {
7578 /* we may have had implicit modifications we want to cancel on failure*/
7579 rollback(false /* aNotify */);
7580 }
7581
7582 return rc;
7583}
7584
7585/**
7586 * Increases the number of objects dependent on the machine state or on the
7587 * registered state. Guarantees that these two states will not change at least
7588 * until #releaseStateDependency() is called.
7589 *
7590 * Depending on the @a aDepType value, additional state checks may be made.
7591 * These checks will set extended error info on failure. See
7592 * #checkStateDependency() for more info.
7593 *
7594 * If this method returns a failure, the dependency is not added and the caller
7595 * is not allowed to rely on any particular machine state or registration state
7596 * value and may return the failed result code to the upper level.
7597 *
7598 * @param aDepType Dependency type to add.
7599 * @param aState Current machine state (NULL if not interested).
7600 * @param aRegistered Current registered state (NULL if not interested).
7601 *
7602 * @note Locks this object for writing.
7603 */
7604HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7605 MachineState_T *aState /* = NULL */,
7606 BOOL *aRegistered /* = NULL */)
7607{
7608 AutoCaller autoCaller(this);
7609 AssertComRCReturnRC(autoCaller.rc());
7610
7611 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7612
7613 HRESULT rc = checkStateDependency(aDepType);
7614 if (FAILED(rc)) return rc;
7615
7616 {
7617 if (mData->mMachineStateChangePending != 0)
7618 {
7619 /* ensureNoStateDependencies() is waiting for state dependencies to
7620 * drop to zero so don't add more. It may make sense to wait a bit
7621 * and retry before reporting an error (since the pending state
7622 * transition should be really quick) but let's just assert for
7623 * now to see if it ever happens on practice. */
7624
7625 AssertFailed();
7626
7627 return setError(E_ACCESSDENIED,
7628 tr("Machine state change is in progress. Please retry the operation later."));
7629 }
7630
7631 ++mData->mMachineStateDeps;
7632 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7633 }
7634
7635 if (aState)
7636 *aState = mData->mMachineState;
7637 if (aRegistered)
7638 *aRegistered = mData->mRegistered;
7639
7640 return S_OK;
7641}
7642
7643/**
7644 * Decreases the number of objects dependent on the machine state.
7645 * Must always complete the #addStateDependency() call after the state
7646 * dependency is no more necessary.
7647 */
7648void Machine::releaseStateDependency()
7649{
7650 AutoCaller autoCaller(this);
7651 AssertComRCReturnVoid(autoCaller.rc());
7652
7653 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7654
7655 /* releaseStateDependency() w/o addStateDependency()? */
7656 AssertReturnVoid(mData->mMachineStateDeps != 0);
7657 -- mData->mMachineStateDeps;
7658
7659 if (mData->mMachineStateDeps == 0)
7660 {
7661 /* inform ensureNoStateDependencies() that there are no more deps */
7662 if (mData->mMachineStateChangePending != 0)
7663 {
7664 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7665 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7666 }
7667 }
7668}
7669
7670// protected methods
7671/////////////////////////////////////////////////////////////////////////////
7672
7673/**
7674 * Performs machine state checks based on the @a aDepType value. If a check
7675 * fails, this method will set extended error info, otherwise it will return
7676 * S_OK. It is supposed, that on failure, the caller will immediately return
7677 * the return value of this method to the upper level.
7678 *
7679 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7680 *
7681 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7682 * current state of this machine object allows to change settings of the
7683 * machine (i.e. the machine is not registered, or registered but not running
7684 * and not saved). It is useful to call this method from Machine setters
7685 * before performing any change.
7686 *
7687 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7688 * as for MutableStateDep except that if the machine is saved, S_OK is also
7689 * returned. This is useful in setters which allow changing machine
7690 * properties when it is in the saved state.
7691 *
7692 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7693 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7694 * Aborted).
7695 *
7696 * @param aDepType Dependency type to check.
7697 *
7698 * @note Non Machine based classes should use #addStateDependency() and
7699 * #releaseStateDependency() methods or the smart AutoStateDependency
7700 * template.
7701 *
7702 * @note This method must be called from under this object's read or write
7703 * lock.
7704 */
7705HRESULT Machine::checkStateDependency(StateDependency aDepType)
7706{
7707 switch (aDepType)
7708 {
7709 case AnyStateDep:
7710 {
7711 break;
7712 }
7713 case MutableStateDep:
7714 {
7715 if ( mData->mRegistered
7716 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7717 || ( mData->mMachineState != MachineState_Paused
7718 && mData->mMachineState != MachineState_Running
7719 && mData->mMachineState != MachineState_Aborted
7720 && mData->mMachineState != MachineState_Teleported
7721 && mData->mMachineState != MachineState_PoweredOff
7722 )
7723 )
7724 )
7725 return setError(VBOX_E_INVALID_VM_STATE,
7726 tr("The machine is not mutable (state is %s)"),
7727 Global::stringifyMachineState(mData->mMachineState));
7728 break;
7729 }
7730 case MutableOrSavedStateDep:
7731 {
7732 if ( mData->mRegistered
7733 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7734 || ( mData->mMachineState != MachineState_Paused
7735 && mData->mMachineState != MachineState_Running
7736 && mData->mMachineState != MachineState_Aborted
7737 && mData->mMachineState != MachineState_Teleported
7738 && mData->mMachineState != MachineState_Saved
7739 && mData->mMachineState != MachineState_PoweredOff
7740 )
7741 )
7742 )
7743 return setError(VBOX_E_INVALID_VM_STATE,
7744 tr("The machine is not mutable (state is %s)"),
7745 Global::stringifyMachineState(mData->mMachineState));
7746 break;
7747 }
7748 case OfflineStateDep:
7749 {
7750 if ( mData->mRegistered
7751 && ( !isSessionMachine()
7752 || ( mData->mMachineState != MachineState_PoweredOff
7753 && mData->mMachineState != MachineState_Saved
7754 && mData->mMachineState != MachineState_Aborted
7755 && mData->mMachineState != MachineState_Teleported
7756 )
7757 )
7758 )
7759 return setError(VBOX_E_INVALID_VM_STATE,
7760 tr("The machine is not offline (state is %s)"),
7761 Global::stringifyMachineState(mData->mMachineState));
7762 break;
7763 }
7764 }
7765
7766 return S_OK;
7767}
7768
7769/**
7770 * Helper to initialize all associated child objects and allocate data
7771 * structures.
7772 *
7773 * This method must be called as a part of the object's initialization procedure
7774 * (usually done in the #init() method).
7775 *
7776 * @note Must be called only from #init() or from #registeredInit().
7777 */
7778HRESULT Machine::initDataAndChildObjects()
7779{
7780 AutoCaller autoCaller(this);
7781 AssertComRCReturnRC(autoCaller.rc());
7782 AssertComRCReturn(autoCaller.state() == InInit ||
7783 autoCaller.state() == Limited, E_FAIL);
7784
7785 AssertReturn(!mData->mAccessible, E_FAIL);
7786
7787 /* allocate data structures */
7788 mSSData.allocate();
7789 mUserData.allocate();
7790 mHWData.allocate();
7791 mMediaData.allocate();
7792 mStorageControllers.allocate();
7793
7794 /* initialize mOSTypeId */
7795 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
7796
7797 /* create associated BIOS settings object */
7798 unconst(mBIOSSettings).createObject();
7799 mBIOSSettings->init(this);
7800
7801 /* create an associated VRDE object (default is disabled) */
7802 unconst(mVRDEServer).createObject();
7803 mVRDEServer->init(this);
7804
7805 /* create associated serial port objects */
7806 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
7807 {
7808 unconst(mSerialPorts[slot]).createObject();
7809 mSerialPorts[slot]->init(this, slot);
7810 }
7811
7812 /* create associated parallel port objects */
7813 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7814 {
7815 unconst(mParallelPorts[slot]).createObject();
7816 mParallelPorts[slot]->init(this, slot);
7817 }
7818
7819 /* create the audio adapter object (always present, default is disabled) */
7820 unconst(mAudioAdapter).createObject();
7821 mAudioAdapter->init(this);
7822
7823 /* create the USB controller object (always present, default is disabled) */
7824 unconst(mUSBController).createObject();
7825 mUSBController->init(this);
7826
7827 /* create associated network adapter objects */
7828 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
7829 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7830 {
7831 unconst(mNetworkAdapters[slot]).createObject();
7832 mNetworkAdapters[slot]->init(this, slot);
7833 }
7834
7835 /* create the bandwidth control */
7836 unconst(mBandwidthControl).createObject();
7837 mBandwidthControl->init(this);
7838
7839 return S_OK;
7840}
7841
7842/**
7843 * Helper to uninitialize all associated child objects and to free all data
7844 * structures.
7845 *
7846 * This method must be called as a part of the object's uninitialization
7847 * procedure (usually done in the #uninit() method).
7848 *
7849 * @note Must be called only from #uninit() or from #registeredInit().
7850 */
7851void Machine::uninitDataAndChildObjects()
7852{
7853 AutoCaller autoCaller(this);
7854 AssertComRCReturnVoid(autoCaller.rc());
7855 AssertComRCReturnVoid( autoCaller.state() == InUninit
7856 || autoCaller.state() == Limited);
7857
7858 /* tell all our other child objects we've been uninitialized */
7859 if (mBandwidthControl)
7860 {
7861 mBandwidthControl->uninit();
7862 unconst(mBandwidthControl).setNull();
7863 }
7864
7865 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7866 {
7867 if (mNetworkAdapters[slot])
7868 {
7869 mNetworkAdapters[slot]->uninit();
7870 unconst(mNetworkAdapters[slot]).setNull();
7871 }
7872 }
7873
7874 if (mUSBController)
7875 {
7876 mUSBController->uninit();
7877 unconst(mUSBController).setNull();
7878 }
7879
7880 if (mAudioAdapter)
7881 {
7882 mAudioAdapter->uninit();
7883 unconst(mAudioAdapter).setNull();
7884 }
7885
7886 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7887 {
7888 if (mParallelPorts[slot])
7889 {
7890 mParallelPorts[slot]->uninit();
7891 unconst(mParallelPorts[slot]).setNull();
7892 }
7893 }
7894
7895 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
7896 {
7897 if (mSerialPorts[slot])
7898 {
7899 mSerialPorts[slot]->uninit();
7900 unconst(mSerialPorts[slot]).setNull();
7901 }
7902 }
7903
7904 if (mVRDEServer)
7905 {
7906 mVRDEServer->uninit();
7907 unconst(mVRDEServer).setNull();
7908 }
7909
7910 if (mBIOSSettings)
7911 {
7912 mBIOSSettings->uninit();
7913 unconst(mBIOSSettings).setNull();
7914 }
7915
7916 /* Deassociate hard disks (only when a real Machine or a SnapshotMachine
7917 * instance is uninitialized; SessionMachine instances refer to real
7918 * Machine hard disks). This is necessary for a clean re-initialization of
7919 * the VM after successfully re-checking the accessibility state. Note
7920 * that in case of normal Machine or SnapshotMachine uninitialization (as
7921 * a result of unregistering or deleting the snapshot), outdated hard
7922 * disk attachments will already be uninitialized and deleted, so this
7923 * code will not affect them. */
7924 if ( !!mMediaData
7925 && (!isSessionMachine())
7926 )
7927 {
7928 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
7929 it != mMediaData->mAttachments.end();
7930 ++it)
7931 {
7932 ComObjPtr<Medium> hd = (*it)->getMedium();
7933 if (hd.isNull())
7934 continue;
7935 HRESULT rc = hd->removeBackReference(mData->mUuid, getSnapshotId());
7936 AssertComRC(rc);
7937 }
7938 }
7939
7940 if (!isSessionMachine() && !isSnapshotMachine())
7941 {
7942 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
7943 if (mData->mFirstSnapshot)
7944 {
7945 // snapshots tree is protected by media write lock; strictly
7946 // this isn't necessary here since we're deleting the entire
7947 // machine, but otherwise we assert in Snapshot::uninit()
7948 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7949 mData->mFirstSnapshot->uninit();
7950 mData->mFirstSnapshot.setNull();
7951 }
7952
7953 mData->mCurrentSnapshot.setNull();
7954 }
7955
7956 /* free data structures (the essential mData structure is not freed here
7957 * since it may be still in use) */
7958 mMediaData.free();
7959 mStorageControllers.free();
7960 mHWData.free();
7961 mUserData.free();
7962 mSSData.free();
7963}
7964
7965/**
7966 * Returns a pointer to the Machine object for this machine that acts like a
7967 * parent for complex machine data objects such as shared folders, etc.
7968 *
7969 * For primary Machine objects and for SnapshotMachine objects, returns this
7970 * object's pointer itself. For SessionMachine objects, returns the peer
7971 * (primary) machine pointer.
7972 */
7973Machine* Machine::getMachine()
7974{
7975 if (isSessionMachine())
7976 return (Machine*)mPeer;
7977 return this;
7978}
7979
7980/**
7981 * Makes sure that there are no machine state dependents. If necessary, waits
7982 * for the number of dependents to drop to zero.
7983 *
7984 * Make sure this method is called from under this object's write lock to
7985 * guarantee that no new dependents may be added when this method returns
7986 * control to the caller.
7987 *
7988 * @note Locks this object for writing. The lock will be released while waiting
7989 * (if necessary).
7990 *
7991 * @warning To be used only in methods that change the machine state!
7992 */
7993void Machine::ensureNoStateDependencies()
7994{
7995 AssertReturnVoid(isWriteLockOnCurrentThread());
7996
7997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7998
7999 /* Wait for all state dependents if necessary */
8000 if (mData->mMachineStateDeps != 0)
8001 {
8002 /* lazy semaphore creation */
8003 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8004 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8005
8006 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8007 mData->mMachineStateDeps));
8008
8009 ++mData->mMachineStateChangePending;
8010
8011 /* reset the semaphore before waiting, the last dependent will signal
8012 * it */
8013 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8014
8015 alock.release();
8016
8017 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8018
8019 alock.acquire();
8020
8021 -- mData->mMachineStateChangePending;
8022 }
8023}
8024
8025/**
8026 * Changes the machine state and informs callbacks.
8027 *
8028 * This method is not intended to fail so it either returns S_OK or asserts (and
8029 * returns a failure).
8030 *
8031 * @note Locks this object for writing.
8032 */
8033HRESULT Machine::setMachineState(MachineState_T aMachineState)
8034{
8035 LogFlowThisFuncEnter();
8036 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8037
8038 AutoCaller autoCaller(this);
8039 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8040
8041 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8042
8043 /* wait for state dependents to drop to zero */
8044 ensureNoStateDependencies();
8045
8046 if (mData->mMachineState != aMachineState)
8047 {
8048 mData->mMachineState = aMachineState;
8049
8050 RTTimeNow(&mData->mLastStateChange);
8051
8052 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8053 }
8054
8055 LogFlowThisFuncLeave();
8056 return S_OK;
8057}
8058
8059/**
8060 * Searches for a shared folder with the given logical name
8061 * in the collection of shared folders.
8062 *
8063 * @param aName logical name of the shared folder
8064 * @param aSharedFolder where to return the found object
8065 * @param aSetError whether to set the error info if the folder is
8066 * not found
8067 * @return
8068 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8069 *
8070 * @note
8071 * must be called from under the object's lock!
8072 */
8073HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8074 ComObjPtr<SharedFolder> &aSharedFolder,
8075 bool aSetError /* = false */)
8076{
8077 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8078 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8079 it != mHWData->mSharedFolders.end();
8080 ++it)
8081 {
8082 SharedFolder *pSF = *it;
8083 AutoCaller autoCaller(pSF);
8084 if (pSF->getName() == aName)
8085 {
8086 aSharedFolder = pSF;
8087 rc = S_OK;
8088 break;
8089 }
8090 }
8091
8092 if (aSetError && FAILED(rc))
8093 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8094
8095 return rc;
8096}
8097
8098/**
8099 * Initializes all machine instance data from the given settings structures
8100 * from XML. The exception is the machine UUID which needs special handling
8101 * depending on the caller's use case, so the caller needs to set that herself.
8102 *
8103 * This gets called in several contexts during machine initialization:
8104 *
8105 * -- When machine XML exists on disk already and needs to be loaded into memory,
8106 * for example, from registeredInit() to load all registered machines on
8107 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8108 * attached to the machine should be part of some media registry already.
8109 *
8110 * -- During OVF import, when a machine config has been constructed from an
8111 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8112 * ensure that the media listed as attachments in the config (which have
8113 * been imported from the OVF) receive the correct registry ID.
8114 *
8115 * -- During VM cloning.
8116 *
8117 * @param config Machine settings from XML.
8118 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8119 * @return
8120 */
8121HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8122 const Guid *puuidRegistry)
8123{
8124 // copy name, description, OS type, teleporter, UTC etc.
8125 mUserData->s = config.machineUserData;
8126
8127 // look up the object by Id to check it is valid
8128 ComPtr<IGuestOSType> guestOSType;
8129 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8130 guestOSType.asOutParam());
8131 if (FAILED(rc)) return rc;
8132
8133 // stateFile (optional)
8134 if (config.strStateFile.isEmpty())
8135 mSSData->strStateFilePath.setNull();
8136 else
8137 {
8138 Utf8Str stateFilePathFull(config.strStateFile);
8139 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8140 if (RT_FAILURE(vrc))
8141 return setError(E_FAIL,
8142 tr("Invalid saved state file path '%s' (%Rrc)"),
8143 config.strStateFile.c_str(),
8144 vrc);
8145 mSSData->strStateFilePath = stateFilePathFull;
8146 }
8147
8148 // snapshot folder needs special processing so set it again
8149 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8150 if (FAILED(rc)) return rc;
8151
8152 /* Copy the extra data items (Not in any case config is already the same as
8153 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8154 * make sure the extra data map is copied). */
8155 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8156
8157 /* currentStateModified (optional, default is true) */
8158 mData->mCurrentStateModified = config.fCurrentStateModified;
8159
8160 mData->mLastStateChange = config.timeLastStateChange;
8161
8162 /*
8163 * note: all mUserData members must be assigned prior this point because
8164 * we need to commit changes in order to let mUserData be shared by all
8165 * snapshot machine instances.
8166 */
8167 mUserData.commitCopy();
8168
8169 // machine registry, if present (must be loaded before snapshots)
8170 if (config.canHaveOwnMediaRegistry())
8171 {
8172 // determine machine folder
8173 Utf8Str strMachineFolder = getSettingsFileFull();
8174 strMachineFolder.stripFilename();
8175 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8176 config.mediaRegistry,
8177 strMachineFolder);
8178 if (FAILED(rc)) return rc;
8179 }
8180
8181 /* Snapshot node (optional) */
8182 size_t cRootSnapshots;
8183 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8184 {
8185 // there must be only one root snapshot
8186 Assert(cRootSnapshots == 1);
8187
8188 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8189
8190 rc = loadSnapshot(snap,
8191 config.uuidCurrentSnapshot,
8192 NULL); // no parent == first snapshot
8193 if (FAILED(rc)) return rc;
8194 }
8195
8196 // hardware data
8197 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8198 if (FAILED(rc)) return rc;
8199
8200 // load storage controllers
8201 rc = loadStorageControllers(config.storageMachine,
8202 puuidRegistry,
8203 NULL /* puuidSnapshot */);
8204 if (FAILED(rc)) return rc;
8205
8206 /*
8207 * NOTE: the assignment below must be the last thing to do,
8208 * otherwise it will be not possible to change the settings
8209 * somewhere in the code above because all setters will be
8210 * blocked by checkStateDependency(MutableStateDep).
8211 */
8212
8213 /* set the machine state to Aborted or Saved when appropriate */
8214 if (config.fAborted)
8215 {
8216 mSSData->strStateFilePath.setNull();
8217
8218 /* no need to use setMachineState() during init() */
8219 mData->mMachineState = MachineState_Aborted;
8220 }
8221 else if (!mSSData->strStateFilePath.isEmpty())
8222 {
8223 /* no need to use setMachineState() during init() */
8224 mData->mMachineState = MachineState_Saved;
8225 }
8226
8227 // after loading settings, we are no longer different from the XML on disk
8228 mData->flModifications = 0;
8229
8230 return S_OK;
8231}
8232
8233/**
8234 * Recursively loads all snapshots starting from the given.
8235 *
8236 * @param aNode <Snapshot> node.
8237 * @param aCurSnapshotId Current snapshot ID from the settings file.
8238 * @param aParentSnapshot Parent snapshot.
8239 */
8240HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8241 const Guid &aCurSnapshotId,
8242 Snapshot *aParentSnapshot)
8243{
8244 AssertReturn(!isSnapshotMachine(), E_FAIL);
8245 AssertReturn(!isSessionMachine(), E_FAIL);
8246
8247 HRESULT rc = S_OK;
8248
8249 Utf8Str strStateFile;
8250 if (!data.strStateFile.isEmpty())
8251 {
8252 /* optional */
8253 strStateFile = data.strStateFile;
8254 int vrc = calculateFullPath(strStateFile, strStateFile);
8255 if (RT_FAILURE(vrc))
8256 return setError(E_FAIL,
8257 tr("Invalid saved state file path '%s' (%Rrc)"),
8258 strStateFile.c_str(),
8259 vrc);
8260 }
8261
8262 /* create a snapshot machine object */
8263 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8264 pSnapshotMachine.createObject();
8265 rc = pSnapshotMachine->initFromSettings(this,
8266 data.hardware,
8267 &data.debugging,
8268 &data.autostart,
8269 data.storage,
8270 data.uuid.ref(),
8271 strStateFile);
8272 if (FAILED(rc)) return rc;
8273
8274 /* create a snapshot object */
8275 ComObjPtr<Snapshot> pSnapshot;
8276 pSnapshot.createObject();
8277 /* initialize the snapshot */
8278 rc = pSnapshot->init(mParent, // VirtualBox object
8279 data.uuid,
8280 data.strName,
8281 data.strDescription,
8282 data.timestamp,
8283 pSnapshotMachine,
8284 aParentSnapshot);
8285 if (FAILED(rc)) return rc;
8286
8287 /* memorize the first snapshot if necessary */
8288 if (!mData->mFirstSnapshot)
8289 mData->mFirstSnapshot = pSnapshot;
8290
8291 /* memorize the current snapshot when appropriate */
8292 if ( !mData->mCurrentSnapshot
8293 && pSnapshot->getId() == aCurSnapshotId
8294 )
8295 mData->mCurrentSnapshot = pSnapshot;
8296
8297 // now create the children
8298 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8299 it != data.llChildSnapshots.end();
8300 ++it)
8301 {
8302 const settings::Snapshot &childData = *it;
8303 // recurse
8304 rc = loadSnapshot(childData,
8305 aCurSnapshotId,
8306 pSnapshot); // parent = the one we created above
8307 if (FAILED(rc)) return rc;
8308 }
8309
8310 return rc;
8311}
8312
8313/**
8314 * Loads settings into mHWData.
8315 *
8316 * @param data Reference to the hardware settings.
8317 * @param pDbg Pointer to the debugging settings.
8318 * @param pAutostart Pointer to the autostart settings.
8319 */
8320HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8321 const settings::Autostart *pAutostart)
8322{
8323 AssertReturn(!isSessionMachine(), E_FAIL);
8324
8325 HRESULT rc = S_OK;
8326
8327 try
8328 {
8329 /* The hardware version attribute (optional). */
8330 mHWData->mHWVersion = data.strVersion;
8331 mHWData->mHardwareUUID = data.uuid;
8332
8333 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8334 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8335 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8336 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8337 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8338 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8339 mHWData->mPAEEnabled = data.fPAE;
8340 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8341
8342 mHWData->mCPUCount = data.cCPUs;
8343 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8344 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8345
8346 // cpu
8347 if (mHWData->mCPUHotPlugEnabled)
8348 {
8349 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8350 it != data.llCpus.end();
8351 ++it)
8352 {
8353 const settings::Cpu &cpu = *it;
8354
8355 mHWData->mCPUAttached[cpu.ulId] = true;
8356 }
8357 }
8358
8359 // cpuid leafs
8360 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8361 it != data.llCpuIdLeafs.end();
8362 ++it)
8363 {
8364 const settings::CpuIdLeaf &leaf = *it;
8365
8366 switch (leaf.ulId)
8367 {
8368 case 0x0:
8369 case 0x1:
8370 case 0x2:
8371 case 0x3:
8372 case 0x4:
8373 case 0x5:
8374 case 0x6:
8375 case 0x7:
8376 case 0x8:
8377 case 0x9:
8378 case 0xA:
8379 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8380 break;
8381
8382 case 0x80000000:
8383 case 0x80000001:
8384 case 0x80000002:
8385 case 0x80000003:
8386 case 0x80000004:
8387 case 0x80000005:
8388 case 0x80000006:
8389 case 0x80000007:
8390 case 0x80000008:
8391 case 0x80000009:
8392 case 0x8000000A:
8393 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8394 break;
8395
8396 default:
8397 /* just ignore */
8398 break;
8399 }
8400 }
8401
8402 mHWData->mMemorySize = data.ulMemorySizeMB;
8403 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8404
8405 // boot order
8406 for (size_t i = 0;
8407 i < RT_ELEMENTS(mHWData->mBootOrder);
8408 i++)
8409 {
8410 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8411 if (it == data.mapBootOrder.end())
8412 mHWData->mBootOrder[i] = DeviceType_Null;
8413 else
8414 mHWData->mBootOrder[i] = it->second;
8415 }
8416
8417 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8418 mHWData->mMonitorCount = data.cMonitors;
8419 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8420 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8421 mHWData->mFirmwareType = data.firmwareType;
8422 mHWData->mPointingHIDType = data.pointingHIDType;
8423 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8424 mHWData->mChipsetType = data.chipsetType;
8425 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8426 mHWData->mHPETEnabled = data.fHPETEnabled;
8427
8428 /* VRDEServer */
8429 rc = mVRDEServer->loadSettings(data.vrdeSettings);
8430 if (FAILED(rc)) return rc;
8431
8432 /* BIOS */
8433 rc = mBIOSSettings->loadSettings(data.biosSettings);
8434 if (FAILED(rc)) return rc;
8435
8436 // Bandwidth control (must come before network adapters)
8437 rc = mBandwidthControl->loadSettings(data.ioSettings);
8438 if (FAILED(rc)) return rc;
8439
8440 /* USB Controller */
8441 rc = mUSBController->loadSettings(data.usbController);
8442 if (FAILED(rc)) return rc;
8443
8444 // network adapters
8445 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8446 uint32_t oldCount = mNetworkAdapters.size();
8447 if (newCount > oldCount)
8448 {
8449 mNetworkAdapters.resize(newCount);
8450 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
8451 {
8452 unconst(mNetworkAdapters[slot]).createObject();
8453 mNetworkAdapters[slot]->init(this, slot);
8454 }
8455 }
8456 else if (newCount < oldCount)
8457 mNetworkAdapters.resize(newCount);
8458 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8459 it != data.llNetworkAdapters.end();
8460 ++it)
8461 {
8462 const settings::NetworkAdapter &nic = *it;
8463
8464 /* slot unicity is guaranteed by XML Schema */
8465 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8466 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
8467 if (FAILED(rc)) return rc;
8468 }
8469
8470 // serial ports
8471 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8472 it != data.llSerialPorts.end();
8473 ++it)
8474 {
8475 const settings::SerialPort &s = *it;
8476
8477 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8478 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
8479 if (FAILED(rc)) return rc;
8480 }
8481
8482 // parallel ports (optional)
8483 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8484 it != data.llParallelPorts.end();
8485 ++it)
8486 {
8487 const settings::ParallelPort &p = *it;
8488
8489 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8490 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
8491 if (FAILED(rc)) return rc;
8492 }
8493
8494 /* AudioAdapter */
8495 rc = mAudioAdapter->loadSettings(data.audioAdapter);
8496 if (FAILED(rc)) return rc;
8497
8498 /* Shared folders */
8499 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8500 it != data.llSharedFolders.end();
8501 ++it)
8502 {
8503 const settings::SharedFolder &sf = *it;
8504
8505 ComObjPtr<SharedFolder> sharedFolder;
8506 /* Check for double entries. Not allowed! */
8507 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8508 if (SUCCEEDED(rc))
8509 return setError(VBOX_E_OBJECT_IN_USE,
8510 tr("Shared folder named '%s' already exists"),
8511 sf.strName.c_str());
8512
8513 /* Create the new shared folder. Don't break on error. This will be
8514 * reported when the machine starts. */
8515 sharedFolder.createObject();
8516 rc = sharedFolder->init(getMachine(),
8517 sf.strName,
8518 sf.strHostPath,
8519 RT_BOOL(sf.fWritable),
8520 RT_BOOL(sf.fAutoMount),
8521 false /* fFailOnError */);
8522 if (FAILED(rc)) return rc;
8523 mHWData->mSharedFolders.push_back(sharedFolder);
8524 }
8525
8526 // Clipboard
8527 mHWData->mClipboardMode = data.clipboardMode;
8528
8529 // drag'n'drop
8530 mHWData->mDragAndDropMode = data.dragAndDropMode;
8531
8532 // guest settings
8533 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8534
8535 // IO settings
8536 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8537 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8538
8539 // Host PCI devices
8540 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8541 it != data.pciAttachments.end();
8542 ++it)
8543 {
8544 const settings::HostPCIDeviceAttachment &hpda = *it;
8545 ComObjPtr<PCIDeviceAttachment> pda;
8546
8547 pda.createObject();
8548 pda->loadSettings(this, hpda);
8549 mHWData->mPCIDeviceAssignments.push_back(pda);
8550 }
8551
8552 /*
8553 * (The following isn't really real hardware, but it lives in HWData
8554 * for reasons of convenience.)
8555 */
8556
8557#ifdef VBOX_WITH_GUEST_PROPS
8558 /* Guest properties (optional) */
8559 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8560 it != data.llGuestProperties.end();
8561 ++it)
8562 {
8563 const settings::GuestProperty &prop = *it;
8564 uint32_t fFlags = guestProp::NILFLAG;
8565 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8566 HWData::GuestProperty property = { prop.strName, prop.strValue, (LONG64) prop.timestamp, fFlags };
8567 mHWData->mGuestProperties.push_back(property);
8568 }
8569
8570 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8571#endif /* VBOX_WITH_GUEST_PROPS defined */
8572
8573 rc = loadDebugging(pDbg);
8574 if (FAILED(rc))
8575 return rc;
8576
8577 mHWData->mAutostart = *pAutostart;
8578 }
8579 catch(std::bad_alloc &)
8580 {
8581 return E_OUTOFMEMORY;
8582 }
8583
8584 AssertComRC(rc);
8585 return rc;
8586}
8587
8588/**
8589 * Called from Machine::loadHardware() to load the debugging settings of the
8590 * machine.
8591 *
8592 * @param pDbg Pointer to the settings.
8593 */
8594HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
8595{
8596 mHWData->mDebugging = *pDbg;
8597 /* no more processing currently required, this will probably change. */
8598 return S_OK;
8599}
8600
8601/**
8602 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
8603 *
8604 * @param data
8605 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8606 * @param puuidSnapshot
8607 * @return
8608 */
8609HRESULT Machine::loadStorageControllers(const settings::Storage &data,
8610 const Guid *puuidRegistry,
8611 const Guid *puuidSnapshot)
8612{
8613 AssertReturn(!isSessionMachine(), E_FAIL);
8614
8615 HRESULT rc = S_OK;
8616
8617 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8618 it != data.llStorageControllers.end();
8619 ++it)
8620 {
8621 const settings::StorageController &ctlData = *it;
8622
8623 ComObjPtr<StorageController> pCtl;
8624 /* Try to find one with the name first. */
8625 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8626 if (SUCCEEDED(rc))
8627 return setError(VBOX_E_OBJECT_IN_USE,
8628 tr("Storage controller named '%s' already exists"),
8629 ctlData.strName.c_str());
8630
8631 pCtl.createObject();
8632 rc = pCtl->init(this,
8633 ctlData.strName,
8634 ctlData.storageBus,
8635 ctlData.ulInstance,
8636 ctlData.fBootable);
8637 if (FAILED(rc)) return rc;
8638
8639 mStorageControllers->push_back(pCtl);
8640
8641 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8642 if (FAILED(rc)) return rc;
8643
8644 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8645 if (FAILED(rc)) return rc;
8646
8647 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8648 if (FAILED(rc)) return rc;
8649
8650 /* Set IDE emulation settings (only for AHCI controller). */
8651 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8652 {
8653 if ( (FAILED(rc = pCtl->SetIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8654 || (FAILED(rc = pCtl->SetIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8655 || (FAILED(rc = pCtl->SetIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8656 || (FAILED(rc = pCtl->SetIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8657 )
8658 return rc;
8659 }
8660
8661 /* Load the attached devices now. */
8662 rc = loadStorageDevices(pCtl,
8663 ctlData,
8664 puuidRegistry,
8665 puuidSnapshot);
8666 if (FAILED(rc)) return rc;
8667 }
8668
8669 return S_OK;
8670}
8671
8672/**
8673 * Called from loadStorageControllers for a controller's devices.
8674 *
8675 * @param aStorageController
8676 * @param data
8677 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8678 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
8679 * @return
8680 */
8681HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
8682 const settings::StorageController &data,
8683 const Guid *puuidRegistry,
8684 const Guid *puuidSnapshot)
8685{
8686 HRESULT rc = S_OK;
8687
8688 /* paranoia: detect duplicate attachments */
8689 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8690 it != data.llAttachedDevices.end();
8691 ++it)
8692 {
8693 const settings::AttachedDevice &ad = *it;
8694
8695 for (settings::AttachedDevicesList::const_iterator it2 = it;
8696 it2 != data.llAttachedDevices.end();
8697 ++it2)
8698 {
8699 if (it == it2)
8700 continue;
8701
8702 const settings::AttachedDevice &ad2 = *it2;
8703
8704 if ( ad.lPort == ad2.lPort
8705 && ad.lDevice == ad2.lDevice)
8706 {
8707 return setError(E_FAIL,
8708 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
8709 aStorageController->getName().c_str(),
8710 ad.lPort,
8711 ad.lDevice,
8712 mUserData->s.strName.c_str());
8713 }
8714 }
8715 }
8716
8717 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8718 it != data.llAttachedDevices.end();
8719 ++it)
8720 {
8721 const settings::AttachedDevice &dev = *it;
8722 ComObjPtr<Medium> medium;
8723
8724 switch (dev.deviceType)
8725 {
8726 case DeviceType_Floppy:
8727 case DeviceType_DVD:
8728 if (dev.strHostDriveSrc.isNotEmpty())
8729 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
8730 else
8731 rc = mParent->findRemoveableMedium(dev.deviceType,
8732 dev.uuid,
8733 false /* fRefresh */,
8734 false /* aSetError */,
8735 medium);
8736 if (rc == VBOX_E_OBJECT_NOT_FOUND)
8737 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
8738 rc = S_OK;
8739 break;
8740
8741 case DeviceType_HardDisk:
8742 {
8743 /* find a hard disk by UUID */
8744 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
8745 if (FAILED(rc))
8746 {
8747 if (isSnapshotMachine())
8748 {
8749 // wrap another error message around the "cannot find hard disk" set by findHardDisk
8750 // so the user knows that the bad disk is in a snapshot somewhere
8751 com::ErrorInfo info;
8752 return setError(E_FAIL,
8753 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
8754 puuidSnapshot->raw(),
8755 info.getText().raw());
8756 }
8757 else
8758 return rc;
8759 }
8760
8761 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
8762
8763 if (medium->getType() == MediumType_Immutable)
8764 {
8765 if (isSnapshotMachine())
8766 return setError(E_FAIL,
8767 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8768 "of the virtual machine '%s' ('%s')"),
8769 medium->getLocationFull().c_str(),
8770 dev.uuid.raw(),
8771 puuidSnapshot->raw(),
8772 mUserData->s.strName.c_str(),
8773 mData->m_strConfigFileFull.c_str());
8774
8775 return setError(E_FAIL,
8776 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8777 medium->getLocationFull().c_str(),
8778 dev.uuid.raw(),
8779 mUserData->s.strName.c_str(),
8780 mData->m_strConfigFileFull.c_str());
8781 }
8782
8783 if (medium->getType() == MediumType_MultiAttach)
8784 {
8785 if (isSnapshotMachine())
8786 return setError(E_FAIL,
8787 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8788 "of the virtual machine '%s' ('%s')"),
8789 medium->getLocationFull().c_str(),
8790 dev.uuid.raw(),
8791 puuidSnapshot->raw(),
8792 mUserData->s.strName.c_str(),
8793 mData->m_strConfigFileFull.c_str());
8794
8795 return setError(E_FAIL,
8796 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8797 medium->getLocationFull().c_str(),
8798 dev.uuid.raw(),
8799 mUserData->s.strName.c_str(),
8800 mData->m_strConfigFileFull.c_str());
8801 }
8802
8803 if ( !isSnapshotMachine()
8804 && medium->getChildren().size() != 0
8805 )
8806 return setError(E_FAIL,
8807 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
8808 "because it has %d differencing child hard disks"),
8809 medium->getLocationFull().c_str(),
8810 dev.uuid.raw(),
8811 mUserData->s.strName.c_str(),
8812 mData->m_strConfigFileFull.c_str(),
8813 medium->getChildren().size());
8814
8815 if (findAttachment(mMediaData->mAttachments,
8816 medium))
8817 return setError(E_FAIL,
8818 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
8819 medium->getLocationFull().c_str(),
8820 dev.uuid.raw(),
8821 mUserData->s.strName.c_str(),
8822 mData->m_strConfigFileFull.c_str());
8823
8824 break;
8825 }
8826
8827 default:
8828 return setError(E_FAIL,
8829 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
8830 medium->getLocationFull().c_str(),
8831 mUserData->s.strName.c_str(),
8832 mData->m_strConfigFileFull.c_str());
8833 }
8834
8835 if (FAILED(rc))
8836 break;
8837
8838 /* Bandwidth groups are loaded at this point. */
8839 ComObjPtr<BandwidthGroup> pBwGroup;
8840
8841 if (!dev.strBwGroup.isEmpty())
8842 {
8843 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
8844 if (FAILED(rc))
8845 return setError(E_FAIL,
8846 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
8847 medium->getLocationFull().c_str(),
8848 dev.strBwGroup.c_str(),
8849 mUserData->s.strName.c_str(),
8850 mData->m_strConfigFileFull.c_str());
8851 pBwGroup->reference();
8852 }
8853
8854 const Bstr controllerName = aStorageController->getName();
8855 ComObjPtr<MediumAttachment> pAttachment;
8856 pAttachment.createObject();
8857 rc = pAttachment->init(this,
8858 medium,
8859 controllerName,
8860 dev.lPort,
8861 dev.lDevice,
8862 dev.deviceType,
8863 false,
8864 dev.fPassThrough,
8865 dev.fTempEject,
8866 dev.fNonRotational,
8867 dev.fDiscard,
8868 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
8869 if (FAILED(rc)) break;
8870
8871 /* associate the medium with this machine and snapshot */
8872 if (!medium.isNull())
8873 {
8874 AutoCaller medCaller(medium);
8875 if (FAILED(medCaller.rc())) return medCaller.rc();
8876 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
8877
8878 if (isSnapshotMachine())
8879 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
8880 else
8881 rc = medium->addBackReference(mData->mUuid);
8882 /* If the medium->addBackReference fails it sets an appropriate
8883 * error message, so no need to do any guesswork here. */
8884
8885 if (puuidRegistry)
8886 // caller wants registry ID to be set on all attached media (OVF import case)
8887 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
8888 }
8889
8890 if (FAILED(rc))
8891 break;
8892
8893 /* back up mMediaData to let registeredInit() properly rollback on failure
8894 * (= limited accessibility) */
8895 setModified(IsModified_Storage);
8896 mMediaData.backup();
8897 mMediaData->mAttachments.push_back(pAttachment);
8898 }
8899
8900 return rc;
8901}
8902
8903/**
8904 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
8905 *
8906 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
8907 * @param aSnapshot where to return the found snapshot
8908 * @param aSetError true to set extended error info on failure
8909 */
8910HRESULT Machine::findSnapshotById(const Guid &aId,
8911 ComObjPtr<Snapshot> &aSnapshot,
8912 bool aSetError /* = false */)
8913{
8914 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
8915
8916 if (!mData->mFirstSnapshot)
8917 {
8918 if (aSetError)
8919 return setError(E_FAIL, tr("This machine does not have any snapshots"));
8920 return E_FAIL;
8921 }
8922
8923 if (aId.isEmpty())
8924 aSnapshot = mData->mFirstSnapshot;
8925 else
8926 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
8927
8928 if (!aSnapshot)
8929 {
8930 if (aSetError)
8931 return setError(E_FAIL,
8932 tr("Could not find a snapshot with UUID {%s}"),
8933 aId.toString().c_str());
8934 return E_FAIL;
8935 }
8936
8937 return S_OK;
8938}
8939
8940/**
8941 * Returns the snapshot with the given name or fails of no such snapshot.
8942 *
8943 * @param aName snapshot name to find
8944 * @param aSnapshot where to return the found snapshot
8945 * @param aSetError true to set extended error info on failure
8946 */
8947HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
8948 ComObjPtr<Snapshot> &aSnapshot,
8949 bool aSetError /* = false */)
8950{
8951 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
8952
8953 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
8954
8955 if (!mData->mFirstSnapshot)
8956 {
8957 if (aSetError)
8958 return setError(VBOX_E_OBJECT_NOT_FOUND,
8959 tr("This machine does not have any snapshots"));
8960 return VBOX_E_OBJECT_NOT_FOUND;
8961 }
8962
8963 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
8964
8965 if (!aSnapshot)
8966 {
8967 if (aSetError)
8968 return setError(VBOX_E_OBJECT_NOT_FOUND,
8969 tr("Could not find a snapshot named '%s'"), strName.c_str());
8970 return VBOX_E_OBJECT_NOT_FOUND;
8971 }
8972
8973 return S_OK;
8974}
8975
8976/**
8977 * Returns a storage controller object with the given name.
8978 *
8979 * @param aName storage controller name to find
8980 * @param aStorageController where to return the found storage controller
8981 * @param aSetError true to set extended error info on failure
8982 */
8983HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
8984 ComObjPtr<StorageController> &aStorageController,
8985 bool aSetError /* = false */)
8986{
8987 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
8988
8989 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
8990 it != mStorageControllers->end();
8991 ++it)
8992 {
8993 if ((*it)->getName() == aName)
8994 {
8995 aStorageController = (*it);
8996 return S_OK;
8997 }
8998 }
8999
9000 if (aSetError)
9001 return setError(VBOX_E_OBJECT_NOT_FOUND,
9002 tr("Could not find a storage controller named '%s'"),
9003 aName.c_str());
9004 return VBOX_E_OBJECT_NOT_FOUND;
9005}
9006
9007HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9008 MediaData::AttachmentList &atts)
9009{
9010 AutoCaller autoCaller(this);
9011 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9012
9013 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9014
9015 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9016 it != mMediaData->mAttachments.end();
9017 ++it)
9018 {
9019 const ComObjPtr<MediumAttachment> &pAtt = *it;
9020
9021 // should never happen, but deal with NULL pointers in the list.
9022 AssertStmt(!pAtt.isNull(), continue);
9023
9024 // getControllerName() needs caller+read lock
9025 AutoCaller autoAttCaller(pAtt);
9026 if (FAILED(autoAttCaller.rc()))
9027 {
9028 atts.clear();
9029 return autoAttCaller.rc();
9030 }
9031 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9032
9033 if (pAtt->getControllerName() == aName)
9034 atts.push_back(pAtt);
9035 }
9036
9037 return S_OK;
9038}
9039
9040/**
9041 * Helper for #saveSettings. Cares about renaming the settings directory and
9042 * file if the machine name was changed and about creating a new settings file
9043 * if this is a new machine.
9044 *
9045 * @note Must be never called directly but only from #saveSettings().
9046 */
9047HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9048{
9049 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9050
9051 HRESULT rc = S_OK;
9052
9053 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9054
9055 /// @todo need to handle primary group change, too
9056
9057 /* attempt to rename the settings file if machine name is changed */
9058 if ( mUserData->s.fNameSync
9059 && mUserData.isBackedUp()
9060 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9061 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9062 )
9063 {
9064 bool dirRenamed = false;
9065 bool fileRenamed = false;
9066
9067 Utf8Str configFile, newConfigFile;
9068 Utf8Str configFilePrev, newConfigFilePrev;
9069 Utf8Str configDir, newConfigDir;
9070
9071 do
9072 {
9073 int vrc = VINF_SUCCESS;
9074
9075 Utf8Str name = mUserData.backedUpData()->s.strName;
9076 Utf8Str newName = mUserData->s.strName;
9077 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9078 if (group == "/")
9079 group.setNull();
9080 Utf8Str newGroup = mUserData->s.llGroups.front();
9081 if (newGroup == "/")
9082 newGroup.setNull();
9083
9084 configFile = mData->m_strConfigFileFull;
9085
9086 /* first, rename the directory if it matches the group and machine name */
9087 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9088 group.c_str(), RTPATH_DELIMITER, name.c_str());
9089 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9090 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9091 configDir = configFile;
9092 configDir.stripFilename();
9093 newConfigDir = configDir;
9094 if ( configDir.length() >= groupPlusName.length()
9095 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9096 {
9097 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9098 Utf8Str newConfigBaseDir(newConfigDir);
9099 newConfigDir.append(newGroupPlusName);
9100 /* new dir and old dir cannot be equal here because of 'if'
9101 * above and because name != newName */
9102 Assert(configDir != newConfigDir);
9103 if (!fSettingsFileIsNew)
9104 {
9105 /* perform real rename only if the machine is not new */
9106 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9107 if ( vrc == VERR_FILE_NOT_FOUND
9108 || vrc == VERR_PATH_NOT_FOUND)
9109 {
9110 /* create the parent directory, then retry renaming */
9111 Utf8Str parent(newConfigDir);
9112 parent.stripFilename();
9113 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9114 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9115 }
9116 if (RT_FAILURE(vrc))
9117 {
9118 rc = setError(E_FAIL,
9119 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9120 configDir.c_str(),
9121 newConfigDir.c_str(),
9122 vrc);
9123 break;
9124 }
9125 /* delete subdirectories which are no longer needed */
9126 Utf8Str dir(configDir);
9127 dir.stripFilename();
9128 while (dir != newConfigBaseDir && dir != ".")
9129 {
9130 vrc = RTDirRemove(dir.c_str());
9131 if (RT_FAILURE(vrc))
9132 break;
9133 dir.stripFilename();
9134 }
9135 dirRenamed = true;
9136 }
9137 }
9138
9139 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9140 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9141
9142 /* then try to rename the settings file itself */
9143 if (newConfigFile != configFile)
9144 {
9145 /* get the path to old settings file in renamed directory */
9146 configFile = Utf8StrFmt("%s%c%s",
9147 newConfigDir.c_str(),
9148 RTPATH_DELIMITER,
9149 RTPathFilename(configFile.c_str()));
9150 if (!fSettingsFileIsNew)
9151 {
9152 /* perform real rename only if the machine is not new */
9153 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9154 if (RT_FAILURE(vrc))
9155 {
9156 rc = setError(E_FAIL,
9157 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9158 configFile.c_str(),
9159 newConfigFile.c_str(),
9160 vrc);
9161 break;
9162 }
9163 fileRenamed = true;
9164 configFilePrev = configFile;
9165 configFilePrev += "-prev";
9166 newConfigFilePrev = newConfigFile;
9167 newConfigFilePrev += "-prev";
9168 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9169 }
9170 }
9171
9172 // update m_strConfigFileFull amd mConfigFile
9173 mData->m_strConfigFileFull = newConfigFile;
9174 // compute the relative path too
9175 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9176
9177 // store the old and new so that VirtualBox::saveSettings() can update
9178 // the media registry
9179 if ( mData->mRegistered
9180 && configDir != newConfigDir)
9181 {
9182 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9183
9184 if (pfNeedsGlobalSaveSettings)
9185 *pfNeedsGlobalSaveSettings = true;
9186 }
9187
9188 // in the saved state file path, replace the old directory with the new directory
9189 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9190 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9191
9192 // and do the same thing for the saved state file paths of all the online snapshots
9193 if (mData->mFirstSnapshot)
9194 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9195 newConfigDir.c_str());
9196 }
9197 while (0);
9198
9199 if (FAILED(rc))
9200 {
9201 /* silently try to rename everything back */
9202 if (fileRenamed)
9203 {
9204 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9205 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9206 }
9207 if (dirRenamed)
9208 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9209 }
9210
9211 if (FAILED(rc)) return rc;
9212 }
9213
9214 if (fSettingsFileIsNew)
9215 {
9216 /* create a virgin config file */
9217 int vrc = VINF_SUCCESS;
9218
9219 /* ensure the settings directory exists */
9220 Utf8Str path(mData->m_strConfigFileFull);
9221 path.stripFilename();
9222 if (!RTDirExists(path.c_str()))
9223 {
9224 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9225 if (RT_FAILURE(vrc))
9226 {
9227 return setError(E_FAIL,
9228 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9229 path.c_str(),
9230 vrc);
9231 }
9232 }
9233
9234 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9235 path = Utf8Str(mData->m_strConfigFileFull);
9236 RTFILE f = NIL_RTFILE;
9237 vrc = RTFileOpen(&f, path.c_str(),
9238 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9239 if (RT_FAILURE(vrc))
9240 return setError(E_FAIL,
9241 tr("Could not create the settings file '%s' (%Rrc)"),
9242 path.c_str(),
9243 vrc);
9244 RTFileClose(f);
9245 }
9246
9247 return rc;
9248}
9249
9250/**
9251 * Saves and commits machine data, user data and hardware data.
9252 *
9253 * Note that on failure, the data remains uncommitted.
9254 *
9255 * @a aFlags may combine the following flags:
9256 *
9257 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9258 * Used when saving settings after an operation that makes them 100%
9259 * correspond to the settings from the current snapshot.
9260 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9261 * #isReallyModified() returns false. This is necessary for cases when we
9262 * change machine data directly, not through the backup()/commit() mechanism.
9263 * - SaveS_Force: settings will be saved without doing a deep compare of the
9264 * settings structures. This is used when this is called because snapshots
9265 * have changed to avoid the overhead of the deep compare.
9266 *
9267 * @note Must be called from under this object's write lock. Locks children for
9268 * writing.
9269 *
9270 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9271 * initialized to false and that will be set to true by this function if
9272 * the caller must invoke VirtualBox::saveSettings() because the global
9273 * settings have changed. This will happen if a machine rename has been
9274 * saved and the global machine and media registries will therefore need
9275 * updating.
9276 */
9277HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9278 int aFlags /*= 0*/)
9279{
9280 LogFlowThisFuncEnter();
9281
9282 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9283
9284 /* make sure child objects are unable to modify the settings while we are
9285 * saving them */
9286 ensureNoStateDependencies();
9287
9288 AssertReturn(!isSnapshotMachine(),
9289 E_FAIL);
9290
9291 HRESULT rc = S_OK;
9292 bool fNeedsWrite = false;
9293
9294 /* First, prepare to save settings. It will care about renaming the
9295 * settings directory and file if the machine name was changed and about
9296 * creating a new settings file if this is a new machine. */
9297 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9298 if (FAILED(rc)) return rc;
9299
9300 // keep a pointer to the current settings structures
9301 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9302 settings::MachineConfigFile *pNewConfig = NULL;
9303
9304 try
9305 {
9306 // make a fresh one to have everyone write stuff into
9307 pNewConfig = new settings::MachineConfigFile(NULL);
9308 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9309
9310 // now go and copy all the settings data from COM to the settings structures
9311 // (this calles saveSettings() on all the COM objects in the machine)
9312 copyMachineDataToSettings(*pNewConfig);
9313
9314 if (aFlags & SaveS_ResetCurStateModified)
9315 {
9316 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9317 mData->mCurrentStateModified = FALSE;
9318 fNeedsWrite = true; // always, no need to compare
9319 }
9320 else if (aFlags & SaveS_Force)
9321 {
9322 fNeedsWrite = true; // always, no need to compare
9323 }
9324 else
9325 {
9326 if (!mData->mCurrentStateModified)
9327 {
9328 // do a deep compare of the settings that we just saved with the settings
9329 // previously stored in the config file; this invokes MachineConfigFile::operator==
9330 // which does a deep compare of all the settings, which is expensive but less expensive
9331 // than writing out XML in vain
9332 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9333
9334 // could still be modified if any settings changed
9335 mData->mCurrentStateModified = fAnySettingsChanged;
9336
9337 fNeedsWrite = fAnySettingsChanged;
9338 }
9339 else
9340 fNeedsWrite = true;
9341 }
9342
9343 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9344
9345 if (fNeedsWrite)
9346 // now spit it all out!
9347 pNewConfig->write(mData->m_strConfigFileFull);
9348
9349 mData->pMachineConfigFile = pNewConfig;
9350 delete pOldConfig;
9351 commit();
9352
9353 // after saving settings, we are no longer different from the XML on disk
9354 mData->flModifications = 0;
9355 }
9356 catch (HRESULT err)
9357 {
9358 // we assume that error info is set by the thrower
9359 rc = err;
9360
9361 // restore old config
9362 delete pNewConfig;
9363 mData->pMachineConfigFile = pOldConfig;
9364 }
9365 catch (...)
9366 {
9367 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9368 }
9369
9370 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9371 {
9372 /* Fire the data change event, even on failure (since we've already
9373 * committed all data). This is done only for SessionMachines because
9374 * mutable Machine instances are always not registered (i.e. private
9375 * to the client process that creates them) and thus don't need to
9376 * inform callbacks. */
9377 if (isSessionMachine())
9378 mParent->onMachineDataChange(mData->mUuid);
9379 }
9380
9381 LogFlowThisFunc(("rc=%08X\n", rc));
9382 LogFlowThisFuncLeave();
9383 return rc;
9384}
9385
9386/**
9387 * Implementation for saving the machine settings into the given
9388 * settings::MachineConfigFile instance. This copies machine extradata
9389 * from the previous machine config file in the instance data, if any.
9390 *
9391 * This gets called from two locations:
9392 *
9393 * -- Machine::saveSettings(), during the regular XML writing;
9394 *
9395 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9396 * exported to OVF and we write the VirtualBox proprietary XML
9397 * into a <vbox:Machine> tag.
9398 *
9399 * This routine fills all the fields in there, including snapshots, *except*
9400 * for the following:
9401 *
9402 * -- fCurrentStateModified. There is some special logic associated with that.
9403 *
9404 * The caller can then call MachineConfigFile::write() or do something else
9405 * with it.
9406 *
9407 * Caller must hold the machine lock!
9408 *
9409 * This throws XML errors and HRESULT, so the caller must have a catch block!
9410 */
9411void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
9412{
9413 // deep copy extradata
9414 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9415
9416 config.uuid = mData->mUuid;
9417
9418 // copy name, description, OS type, teleport, UTC etc.
9419 config.machineUserData = mUserData->s;
9420
9421 if ( mData->mMachineState == MachineState_Saved
9422 || mData->mMachineState == MachineState_Restoring
9423 // when deleting a snapshot we may or may not have a saved state in the current state,
9424 // so let's not assert here please
9425 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9426 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9427 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9428 && (!mSSData->strStateFilePath.isEmpty())
9429 )
9430 )
9431 {
9432 Assert(!mSSData->strStateFilePath.isEmpty());
9433 /* try to make the file name relative to the settings file dir */
9434 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9435 }
9436 else
9437 {
9438 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9439 config.strStateFile.setNull();
9440 }
9441
9442 if (mData->mCurrentSnapshot)
9443 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
9444 else
9445 config.uuidCurrentSnapshot.clear();
9446
9447 config.timeLastStateChange = mData->mLastStateChange;
9448 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9449 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9450
9451 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9452 if (FAILED(rc)) throw rc;
9453
9454 rc = saveStorageControllers(config.storageMachine);
9455 if (FAILED(rc)) throw rc;
9456
9457 // save machine's media registry if this is VirtualBox 4.0 or later
9458 if (config.canHaveOwnMediaRegistry())
9459 {
9460 // determine machine folder
9461 Utf8Str strMachineFolder = getSettingsFileFull();
9462 strMachineFolder.stripFilename();
9463 mParent->saveMediaRegistry(config.mediaRegistry,
9464 getId(), // only media with registry ID == machine UUID
9465 strMachineFolder);
9466 // this throws HRESULT
9467 }
9468
9469 // save snapshots
9470 rc = saveAllSnapshots(config);
9471 if (FAILED(rc)) throw rc;
9472}
9473
9474/**
9475 * Saves all snapshots of the machine into the given machine config file. Called
9476 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9477 * @param config
9478 * @return
9479 */
9480HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
9481{
9482 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9483
9484 HRESULT rc = S_OK;
9485
9486 try
9487 {
9488 config.llFirstSnapshot.clear();
9489
9490 if (mData->mFirstSnapshot)
9491 {
9492 settings::Snapshot snapNew;
9493 config.llFirstSnapshot.push_back(snapNew);
9494
9495 // get reference to the fresh copy of the snapshot on the list and
9496 // work on that copy directly to avoid excessive copying later
9497 settings::Snapshot &snap = config.llFirstSnapshot.front();
9498
9499 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
9500 if (FAILED(rc)) throw rc;
9501 }
9502
9503// if (mType == IsSessionMachine)
9504// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9505
9506 }
9507 catch (HRESULT err)
9508 {
9509 /* we assume that error info is set by the thrower */
9510 rc = err;
9511 }
9512 catch (...)
9513 {
9514 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9515 }
9516
9517 return rc;
9518}
9519
9520/**
9521 * Saves the VM hardware configuration. It is assumed that the
9522 * given node is empty.
9523 *
9524 * @param data Reference to the settings object for the hardware config.
9525 * @param pDbg Pointer to the settings object for the debugging config
9526 * which happens to live in mHWData.
9527 * @param pAutostart Pointer to the settings object for the autostart config
9528 * which happens to live in mHWData.
9529 */
9530HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9531 settings::Autostart *pAutostart)
9532{
9533 HRESULT rc = S_OK;
9534
9535 try
9536 {
9537 /* The hardware version attribute (optional).
9538 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9539 if ( mHWData->mHWVersion == "1"
9540 && mSSData->strStateFilePath.isEmpty()
9541 )
9542 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. */
9543
9544 data.strVersion = mHWData->mHWVersion;
9545 data.uuid = mHWData->mHardwareUUID;
9546
9547 // CPU
9548 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9549 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
9550 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9551 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9552 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9553 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9554 data.fPAE = !!mHWData->mPAEEnabled;
9555 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9556
9557 /* Standard and Extended CPUID leafs. */
9558 data.llCpuIdLeafs.clear();
9559 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
9560 {
9561 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9562 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9563 }
9564 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
9565 {
9566 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9567 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9568 }
9569
9570 data.cCPUs = mHWData->mCPUCount;
9571 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9572 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9573
9574 data.llCpus.clear();
9575 if (data.fCpuHotPlug)
9576 {
9577 for (unsigned idx = 0; idx < data.cCPUs; idx++)
9578 {
9579 if (mHWData->mCPUAttached[idx])
9580 {
9581 settings::Cpu cpu;
9582 cpu.ulId = idx;
9583 data.llCpus.push_back(cpu);
9584 }
9585 }
9586 }
9587
9588 // memory
9589 data.ulMemorySizeMB = mHWData->mMemorySize;
9590 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9591
9592 // firmware
9593 data.firmwareType = mHWData->mFirmwareType;
9594
9595 // HID
9596 data.pointingHIDType = mHWData->mPointingHIDType;
9597 data.keyboardHIDType = mHWData->mKeyboardHIDType;
9598
9599 // chipset
9600 data.chipsetType = mHWData->mChipsetType;
9601
9602 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9603
9604 // HPET
9605 data.fHPETEnabled = !!mHWData->mHPETEnabled;
9606
9607 // boot order
9608 data.mapBootOrder.clear();
9609 for (size_t i = 0;
9610 i < RT_ELEMENTS(mHWData->mBootOrder);
9611 ++i)
9612 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9613
9614 // display
9615 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9616 data.cMonitors = mHWData->mMonitorCount;
9617 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9618 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9619
9620 /* VRDEServer settings (optional) */
9621 rc = mVRDEServer->saveSettings(data.vrdeSettings);
9622 if (FAILED(rc)) throw rc;
9623
9624 /* BIOS (required) */
9625 rc = mBIOSSettings->saveSettings(data.biosSettings);
9626 if (FAILED(rc)) throw rc;
9627
9628 /* USB Controller (required) */
9629 rc = mUSBController->saveSettings(data.usbController);
9630 if (FAILED(rc)) throw rc;
9631
9632 /* Network adapters (required) */
9633 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
9634 data.llNetworkAdapters.clear();
9635 /* Write out only the nominal number of network adapters for this
9636 * chipset type. Since Machine::commit() hasn't been called there
9637 * may be extra NIC settings in the vector. */
9638 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
9639 {
9640 settings::NetworkAdapter nic;
9641 nic.ulSlot = slot;
9642 /* paranoia check... must not be NULL, but must not crash either. */
9643 if (mNetworkAdapters[slot])
9644 {
9645 rc = mNetworkAdapters[slot]->saveSettings(nic);
9646 if (FAILED(rc)) throw rc;
9647
9648 data.llNetworkAdapters.push_back(nic);
9649 }
9650 }
9651
9652 /* Serial ports */
9653 data.llSerialPorts.clear();
9654 for (ULONG slot = 0;
9655 slot < RT_ELEMENTS(mSerialPorts);
9656 ++slot)
9657 {
9658 settings::SerialPort s;
9659 s.ulSlot = slot;
9660 rc = mSerialPorts[slot]->saveSettings(s);
9661 if (FAILED(rc)) return rc;
9662
9663 data.llSerialPorts.push_back(s);
9664 }
9665
9666 /* Parallel ports */
9667 data.llParallelPorts.clear();
9668 for (ULONG slot = 0;
9669 slot < RT_ELEMENTS(mParallelPorts);
9670 ++slot)
9671 {
9672 settings::ParallelPort p;
9673 p.ulSlot = slot;
9674 rc = mParallelPorts[slot]->saveSettings(p);
9675 if (FAILED(rc)) return rc;
9676
9677 data.llParallelPorts.push_back(p);
9678 }
9679
9680 /* Audio adapter */
9681 rc = mAudioAdapter->saveSettings(data.audioAdapter);
9682 if (FAILED(rc)) return rc;
9683
9684 /* Shared folders */
9685 data.llSharedFolders.clear();
9686 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
9687 it != mHWData->mSharedFolders.end();
9688 ++it)
9689 {
9690 SharedFolder *pSF = *it;
9691 AutoCaller sfCaller(pSF);
9692 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
9693 settings::SharedFolder sf;
9694 sf.strName = pSF->getName();
9695 sf.strHostPath = pSF->getHostPath();
9696 sf.fWritable = !!pSF->isWritable();
9697 sf.fAutoMount = !!pSF->isAutoMounted();
9698
9699 data.llSharedFolders.push_back(sf);
9700 }
9701
9702 // clipboard
9703 data.clipboardMode = mHWData->mClipboardMode;
9704
9705 // drag'n'drop
9706 data.dragAndDropMode = mHWData->mDragAndDropMode;
9707
9708 /* Guest */
9709 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
9710
9711 // IO settings
9712 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
9713 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
9714
9715 /* BandwidthControl (required) */
9716 rc = mBandwidthControl->saveSettings(data.ioSettings);
9717 if (FAILED(rc)) throw rc;
9718
9719 /* Host PCI devices */
9720 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
9721 it != mHWData->mPCIDeviceAssignments.end();
9722 ++it)
9723 {
9724 ComObjPtr<PCIDeviceAttachment> pda = *it;
9725 settings::HostPCIDeviceAttachment hpda;
9726
9727 rc = pda->saveSettings(hpda);
9728 if (FAILED(rc)) throw rc;
9729
9730 data.pciAttachments.push_back(hpda);
9731 }
9732
9733
9734 // guest properties
9735 data.llGuestProperties.clear();
9736#ifdef VBOX_WITH_GUEST_PROPS
9737 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
9738 it != mHWData->mGuestProperties.end();
9739 ++it)
9740 {
9741 HWData::GuestProperty property = *it;
9742
9743 /* Remove transient guest properties at shutdown unless we
9744 * are saving state */
9745 if ( ( mData->mMachineState == MachineState_PoweredOff
9746 || mData->mMachineState == MachineState_Aborted
9747 || mData->mMachineState == MachineState_Teleported)
9748 && ( property.mFlags & guestProp::TRANSIENT
9749 || property.mFlags & guestProp::TRANSRESET))
9750 continue;
9751 settings::GuestProperty prop;
9752 prop.strName = property.strName;
9753 prop.strValue = property.strValue;
9754 prop.timestamp = property.mTimestamp;
9755 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
9756 guestProp::writeFlags(property.mFlags, szFlags);
9757 prop.strFlags = szFlags;
9758
9759 data.llGuestProperties.push_back(prop);
9760 }
9761
9762 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
9763 /* I presume this doesn't require a backup(). */
9764 mData->mGuestPropertiesModified = FALSE;
9765#endif /* VBOX_WITH_GUEST_PROPS defined */
9766
9767 *pDbg = mHWData->mDebugging;
9768 *pAutostart = mHWData->mAutostart;
9769 }
9770 catch(std::bad_alloc &)
9771 {
9772 return E_OUTOFMEMORY;
9773 }
9774
9775 AssertComRC(rc);
9776 return rc;
9777}
9778
9779/**
9780 * Saves the storage controller configuration.
9781 *
9782 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
9783 */
9784HRESULT Machine::saveStorageControllers(settings::Storage &data)
9785{
9786 data.llStorageControllers.clear();
9787
9788 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9789 it != mStorageControllers->end();
9790 ++it)
9791 {
9792 HRESULT rc;
9793 ComObjPtr<StorageController> pCtl = *it;
9794
9795 settings::StorageController ctl;
9796 ctl.strName = pCtl->getName();
9797 ctl.controllerType = pCtl->getControllerType();
9798 ctl.storageBus = pCtl->getStorageBus();
9799 ctl.ulInstance = pCtl->getInstance();
9800 ctl.fBootable = pCtl->getBootable();
9801
9802 /* Save the port count. */
9803 ULONG portCount;
9804 rc = pCtl->COMGETTER(PortCount)(&portCount);
9805 ComAssertComRCRet(rc, rc);
9806 ctl.ulPortCount = portCount;
9807
9808 /* Save fUseHostIOCache */
9809 BOOL fUseHostIOCache;
9810 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
9811 ComAssertComRCRet(rc, rc);
9812 ctl.fUseHostIOCache = !!fUseHostIOCache;
9813
9814 /* Save IDE emulation settings. */
9815 if (ctl.controllerType == StorageControllerType_IntelAhci)
9816 {
9817 if ( (FAILED(rc = pCtl->GetIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
9818 || (FAILED(rc = pCtl->GetIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
9819 || (FAILED(rc = pCtl->GetIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
9820 || (FAILED(rc = pCtl->GetIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
9821 )
9822 ComAssertComRCRet(rc, rc);
9823 }
9824
9825 /* save the devices now. */
9826 rc = saveStorageDevices(pCtl, ctl);
9827 ComAssertComRCRet(rc, rc);
9828
9829 data.llStorageControllers.push_back(ctl);
9830 }
9831
9832 return S_OK;
9833}
9834
9835/**
9836 * Saves the hard disk configuration.
9837 */
9838HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
9839 settings::StorageController &data)
9840{
9841 MediaData::AttachmentList atts;
9842
9843 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
9844 if (FAILED(rc)) return rc;
9845
9846 data.llAttachedDevices.clear();
9847 for (MediaData::AttachmentList::const_iterator it = atts.begin();
9848 it != atts.end();
9849 ++it)
9850 {
9851 settings::AttachedDevice dev;
9852
9853 MediumAttachment *pAttach = *it;
9854 Medium *pMedium = pAttach->getMedium();
9855
9856 dev.deviceType = pAttach->getType();
9857 dev.lPort = pAttach->getPort();
9858 dev.lDevice = pAttach->getDevice();
9859 if (pMedium)
9860 {
9861 if (pMedium->isHostDrive())
9862 dev.strHostDriveSrc = pMedium->getLocationFull();
9863 else
9864 dev.uuid = pMedium->getId();
9865 dev.fPassThrough = pAttach->getPassthrough();
9866 dev.fTempEject = pAttach->getTempEject();
9867 dev.fDiscard = pAttach->getDiscard();
9868 }
9869
9870 dev.strBwGroup = pAttach->getBandwidthGroup();
9871
9872 data.llAttachedDevices.push_back(dev);
9873 }
9874
9875 return S_OK;
9876}
9877
9878/**
9879 * Saves machine state settings as defined by aFlags
9880 * (SaveSTS_* values).
9881 *
9882 * @param aFlags Combination of SaveSTS_* flags.
9883 *
9884 * @note Locks objects for writing.
9885 */
9886HRESULT Machine::saveStateSettings(int aFlags)
9887{
9888 if (aFlags == 0)
9889 return S_OK;
9890
9891 AutoCaller autoCaller(this);
9892 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9893
9894 /* This object's write lock is also necessary to serialize file access
9895 * (prevent concurrent reads and writes) */
9896 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9897
9898 HRESULT rc = S_OK;
9899
9900 Assert(mData->pMachineConfigFile);
9901
9902 try
9903 {
9904 if (aFlags & SaveSTS_CurStateModified)
9905 mData->pMachineConfigFile->fCurrentStateModified = true;
9906
9907 if (aFlags & SaveSTS_StateFilePath)
9908 {
9909 if (!mSSData->strStateFilePath.isEmpty())
9910 /* try to make the file name relative to the settings file dir */
9911 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
9912 else
9913 mData->pMachineConfigFile->strStateFile.setNull();
9914 }
9915
9916 if (aFlags & SaveSTS_StateTimeStamp)
9917 {
9918 Assert( mData->mMachineState != MachineState_Aborted
9919 || mSSData->strStateFilePath.isEmpty());
9920
9921 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
9922
9923 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
9924//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
9925 }
9926
9927 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
9928 }
9929 catch (...)
9930 {
9931 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9932 }
9933
9934 return rc;
9935}
9936
9937/**
9938 * Ensures that the given medium is added to a media registry. If this machine
9939 * was created with 4.0 or later, then the machine registry is used. Otherwise
9940 * the global VirtualBox media registry is used.
9941 *
9942 * Caller must NOT hold machine lock, media tree or any medium locks!
9943 *
9944 * @param pMedium
9945 */
9946void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
9947{
9948 /* Paranoia checks: do not hold machine or media tree locks. */
9949 AssertReturnVoid(!isWriteLockOnCurrentThread());
9950 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
9951
9952 ComObjPtr<Medium> pBase;
9953 {
9954 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9955 pBase = pMedium->getBase();
9956 }
9957
9958 /* Paranoia checks: do not hold medium locks. */
9959 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
9960 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
9961
9962 // decide which medium registry to use now that the medium is attached:
9963 Guid uuid;
9964 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
9965 // machine XML is VirtualBox 4.0 or higher:
9966 uuid = getId(); // machine UUID
9967 else
9968 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
9969
9970 if (pMedium->addRegistry(uuid, false /* fRecurse */))
9971 mParent->markRegistryModified(uuid);
9972
9973 /* For more complex hard disk structures it can happen that the base
9974 * medium isn't yet associated with any medium registry. Do that now. */
9975 if (pMedium != pBase)
9976 {
9977 if (pBase->addRegistry(uuid, true /* fRecurse */))
9978 mParent->markRegistryModified(uuid);
9979 }
9980}
9981
9982/**
9983 * Creates differencing hard disks for all normal hard disks attached to this
9984 * machine and a new set of attachments to refer to created disks.
9985 *
9986 * Used when taking a snapshot or when deleting the current state. Gets called
9987 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
9988 *
9989 * This method assumes that mMediaData contains the original hard disk attachments
9990 * it needs to create diffs for. On success, these attachments will be replaced
9991 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
9992 * called to delete created diffs which will also rollback mMediaData and restore
9993 * whatever was backed up before calling this method.
9994 *
9995 * Attachments with non-normal hard disks are left as is.
9996 *
9997 * If @a aOnline is @c false then the original hard disks that require implicit
9998 * diffs will be locked for reading. Otherwise it is assumed that they are
9999 * already locked for writing (when the VM was started). Note that in the latter
10000 * case it is responsibility of the caller to lock the newly created diffs for
10001 * writing if this method succeeds.
10002 *
10003 * @param aProgress Progress object to run (must contain at least as
10004 * many operations left as the number of hard disks
10005 * attached).
10006 * @param aOnline Whether the VM was online prior to this operation.
10007 *
10008 * @note The progress object is not marked as completed, neither on success nor
10009 * on failure. This is a responsibility of the caller.
10010 *
10011 * @note Locks this object for writing.
10012 */
10013HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10014 ULONG aWeight,
10015 bool aOnline)
10016{
10017 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10018
10019 AutoCaller autoCaller(this);
10020 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10021
10022 AutoMultiWriteLock2 alock(this->lockHandle(),
10023 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10024
10025 /* must be in a protective state because we release the lock below */
10026 AssertReturn( mData->mMachineState == MachineState_Saving
10027 || mData->mMachineState == MachineState_LiveSnapshotting
10028 || mData->mMachineState == MachineState_RestoringSnapshot
10029 || mData->mMachineState == MachineState_DeletingSnapshot
10030 , E_FAIL);
10031
10032 HRESULT rc = S_OK;
10033
10034 MediumLockListMap lockedMediaOffline;
10035 MediumLockListMap *lockedMediaMap;
10036 if (aOnline)
10037 lockedMediaMap = &mData->mSession.mLockedMedia;
10038 else
10039 lockedMediaMap = &lockedMediaOffline;
10040
10041 try
10042 {
10043 if (!aOnline)
10044 {
10045 /* lock all attached hard disks early to detect "in use"
10046 * situations before creating actual diffs */
10047 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10048 it != mMediaData->mAttachments.end();
10049 ++it)
10050 {
10051 MediumAttachment* pAtt = *it;
10052 if (pAtt->getType() == DeviceType_HardDisk)
10053 {
10054 Medium* pMedium = pAtt->getMedium();
10055 Assert(pMedium);
10056
10057 MediumLockList *pMediumLockList(new MediumLockList());
10058 alock.release();
10059 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10060 false /* fMediumLockWrite */,
10061 NULL,
10062 *pMediumLockList);
10063 alock.acquire();
10064 if (FAILED(rc))
10065 {
10066 delete pMediumLockList;
10067 throw rc;
10068 }
10069 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10070 if (FAILED(rc))
10071 {
10072 throw setError(rc,
10073 tr("Collecting locking information for all attached media failed"));
10074 }
10075 }
10076 }
10077
10078 /* Now lock all media. If this fails, nothing is locked. */
10079 alock.release();
10080 rc = lockedMediaMap->Lock();
10081 alock.acquire();
10082 if (FAILED(rc))
10083 {
10084 throw setError(rc,
10085 tr("Locking of attached media failed"));
10086 }
10087 }
10088
10089 /* remember the current list (note that we don't use backup() since
10090 * mMediaData may be already backed up) */
10091 MediaData::AttachmentList atts = mMediaData->mAttachments;
10092
10093 /* start from scratch */
10094 mMediaData->mAttachments.clear();
10095
10096 /* go through remembered attachments and create diffs for normal hard
10097 * disks and attach them */
10098 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10099 it != atts.end();
10100 ++it)
10101 {
10102 MediumAttachment* pAtt = *it;
10103
10104 DeviceType_T devType = pAtt->getType();
10105 Medium* pMedium = pAtt->getMedium();
10106
10107 if ( devType != DeviceType_HardDisk
10108 || pMedium == NULL
10109 || pMedium->getType() != MediumType_Normal)
10110 {
10111 /* copy the attachment as is */
10112
10113 /** @todo the progress object created in Console::TakeSnaphot
10114 * only expects operations for hard disks. Later other
10115 * device types need to show up in the progress as well. */
10116 if (devType == DeviceType_HardDisk)
10117 {
10118 if (pMedium == NULL)
10119 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10120 aWeight); // weight
10121 else
10122 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10123 pMedium->getBase()->getName().c_str()).raw(),
10124 aWeight); // weight
10125 }
10126
10127 mMediaData->mAttachments.push_back(pAtt);
10128 continue;
10129 }
10130
10131 /* need a diff */
10132 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10133 pMedium->getBase()->getName().c_str()).raw(),
10134 aWeight); // weight
10135
10136 Utf8Str strFullSnapshotFolder;
10137 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10138
10139 ComObjPtr<Medium> diff;
10140 diff.createObject();
10141 // store the diff in the same registry as the parent
10142 // (this cannot fail here because we can't create implicit diffs for
10143 // unregistered images)
10144 Guid uuidRegistryParent;
10145 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10146 Assert(fInRegistry); NOREF(fInRegistry);
10147 rc = diff->init(mParent,
10148 pMedium->getPreferredDiffFormat(),
10149 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10150 uuidRegistryParent);
10151 if (FAILED(rc)) throw rc;
10152
10153 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10154 * the push_back? Looks like we're going to release medium with the
10155 * wrong kind of lock (general issue with if we fail anywhere at all)
10156 * and an orphaned VDI in the snapshots folder. */
10157
10158 /* update the appropriate lock list */
10159 MediumLockList *pMediumLockList;
10160 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10161 AssertComRCThrowRC(rc);
10162 if (aOnline)
10163 {
10164 alock.release();
10165 rc = pMediumLockList->Update(pMedium, false);
10166 alock.acquire();
10167 AssertComRCThrowRC(rc);
10168 }
10169
10170 /* release the locks before the potentially lengthy operation */
10171 alock.release();
10172 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10173 pMediumLockList,
10174 NULL /* aProgress */,
10175 true /* aWait */);
10176 alock.acquire();
10177 if (FAILED(rc)) throw rc;
10178
10179 rc = lockedMediaMap->Unlock();
10180 AssertComRCThrowRC(rc);
10181 alock.release();
10182 rc = pMediumLockList->Append(diff, true);
10183 alock.acquire();
10184 AssertComRCThrowRC(rc);
10185 alock.release();
10186 rc = lockedMediaMap->Lock();
10187 alock.acquire();
10188 AssertComRCThrowRC(rc);
10189
10190 rc = diff->addBackReference(mData->mUuid);
10191 AssertComRCThrowRC(rc);
10192
10193 /* add a new attachment */
10194 ComObjPtr<MediumAttachment> attachment;
10195 attachment.createObject();
10196 rc = attachment->init(this,
10197 diff,
10198 pAtt->getControllerName(),
10199 pAtt->getPort(),
10200 pAtt->getDevice(),
10201 DeviceType_HardDisk,
10202 true /* aImplicit */,
10203 false /* aPassthrough */,
10204 false /* aTempEject */,
10205 pAtt->getNonRotational(),
10206 pAtt->getDiscard(),
10207 pAtt->getBandwidthGroup());
10208 if (FAILED(rc)) throw rc;
10209
10210 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10211 AssertComRCThrowRC(rc);
10212 mMediaData->mAttachments.push_back(attachment);
10213 }
10214 }
10215 catch (HRESULT aRC) { rc = aRC; }
10216
10217 /* unlock all hard disks we locked */
10218 if (!aOnline)
10219 {
10220 ErrorInfoKeeper eik;
10221
10222 HRESULT rc1 = lockedMediaMap->Clear();
10223 AssertComRC(rc1);
10224 }
10225
10226 if (FAILED(rc))
10227 {
10228 MultiResult mrc = rc;
10229
10230 alock.release();
10231 mrc = deleteImplicitDiffs();
10232 }
10233
10234 return rc;
10235}
10236
10237/**
10238 * Deletes implicit differencing hard disks created either by
10239 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10240 *
10241 * Note that to delete hard disks created by #AttachDevice() this method is
10242 * called from #fixupMedia() when the changes are rolled back.
10243 *
10244 * @note Locks this object for writing.
10245 */
10246HRESULT Machine::deleteImplicitDiffs()
10247{
10248 AutoCaller autoCaller(this);
10249 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10250
10251 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10252 LogFlowThisFuncEnter();
10253
10254 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10255
10256 HRESULT rc = S_OK;
10257
10258 MediaData::AttachmentList implicitAtts;
10259
10260 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10261
10262 /* enumerate new attachments */
10263 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10264 it != mMediaData->mAttachments.end();
10265 ++it)
10266 {
10267 ComObjPtr<Medium> hd = (*it)->getMedium();
10268 if (hd.isNull())
10269 continue;
10270
10271 if ((*it)->isImplicit())
10272 {
10273 /* deassociate and mark for deletion */
10274 LogFlowThisFunc(("Detaching '%s', pending deletion\n", (*it)->getLogName()));
10275 rc = hd->removeBackReference(mData->mUuid);
10276 AssertComRC(rc);
10277 implicitAtts.push_back(*it);
10278 continue;
10279 }
10280
10281 /* was this hard disk attached before? */
10282 if (!findAttachment(oldAtts, hd))
10283 {
10284 /* no: de-associate */
10285 LogFlowThisFunc(("Detaching '%s', no deletion\n", (*it)->getLogName()));
10286 rc = hd->removeBackReference(mData->mUuid);
10287 AssertComRC(rc);
10288 continue;
10289 }
10290 LogFlowThisFunc(("Not detaching '%s'\n", (*it)->getLogName()));
10291 }
10292
10293 /* rollback hard disk changes */
10294 mMediaData.rollback();
10295
10296 MultiResult mrc(S_OK);
10297
10298 /* delete unused implicit diffs */
10299 if (implicitAtts.size() != 0)
10300 {
10301 /* will release the lock before the potentially lengthy
10302 * operation, so protect with the special state (unless already
10303 * protected) */
10304 MachineState_T oldState = mData->mMachineState;
10305 if ( oldState != MachineState_Saving
10306 && oldState != MachineState_LiveSnapshotting
10307 && oldState != MachineState_RestoringSnapshot
10308 && oldState != MachineState_DeletingSnapshot
10309 && oldState != MachineState_DeletingSnapshotOnline
10310 && oldState != MachineState_DeletingSnapshotPaused
10311 )
10312 setMachineState(MachineState_SettingUp);
10313
10314 alock.release();
10315
10316 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
10317 it != implicitAtts.end();
10318 ++it)
10319 {
10320 LogFlowThisFunc(("Deleting '%s'\n", (*it)->getLogName()));
10321 ComObjPtr<Medium> hd = (*it)->getMedium();
10322
10323 rc = hd->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10324 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, (*it)->getLogName(), hd->getLocationFull().c_str() ));
10325 mrc = rc;
10326 }
10327
10328 alock.acquire();
10329
10330 if (mData->mMachineState == MachineState_SettingUp)
10331 setMachineState(oldState);
10332 }
10333
10334 return mrc;
10335}
10336
10337/**
10338 * Looks through the given list of media attachments for one with the given parameters
10339 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10340 * can be searched as well if needed.
10341 *
10342 * @param list
10343 * @param aControllerName
10344 * @param aControllerPort
10345 * @param aDevice
10346 * @return
10347 */
10348MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10349 IN_BSTR aControllerName,
10350 LONG aControllerPort,
10351 LONG aDevice)
10352{
10353 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10354 it != ll.end();
10355 ++it)
10356 {
10357 MediumAttachment *pAttach = *it;
10358 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
10359 return pAttach;
10360 }
10361
10362 return NULL;
10363}
10364
10365/**
10366 * Looks through the given list of media attachments for one with the given parameters
10367 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10368 * can be searched as well if needed.
10369 *
10370 * @param list
10371 * @param aControllerName
10372 * @param aControllerPort
10373 * @param aDevice
10374 * @return
10375 */
10376MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10377 ComObjPtr<Medium> pMedium)
10378{
10379 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10380 it != ll.end();
10381 ++it)
10382 {
10383 MediumAttachment *pAttach = *it;
10384 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10385 if (pMediumThis == pMedium)
10386 return pAttach;
10387 }
10388
10389 return NULL;
10390}
10391
10392/**
10393 * Looks through the given list of media attachments for one with the given parameters
10394 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10395 * can be searched as well if needed.
10396 *
10397 * @param list
10398 * @param aControllerName
10399 * @param aControllerPort
10400 * @param aDevice
10401 * @return
10402 */
10403MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10404 Guid &id)
10405{
10406 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10407 it != ll.end();
10408 ++it)
10409 {
10410 MediumAttachment *pAttach = *it;
10411 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10412 if (pMediumThis->getId() == id)
10413 return pAttach;
10414 }
10415
10416 return NULL;
10417}
10418
10419/**
10420 * Main implementation for Machine::DetachDevice. This also gets called
10421 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10422 *
10423 * @param pAttach Medium attachment to detach.
10424 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10425 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
10426 * @return
10427 */
10428HRESULT Machine::detachDevice(MediumAttachment *pAttach,
10429 AutoWriteLock &writeLock,
10430 Snapshot *pSnapshot)
10431{
10432 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
10433 DeviceType_T mediumType = pAttach->getType();
10434
10435 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
10436
10437 if (pAttach->isImplicit())
10438 {
10439 /* attempt to implicitly delete the implicitly created diff */
10440
10441 /// @todo move the implicit flag from MediumAttachment to Medium
10442 /// and forbid any hard disk operation when it is implicit. Or maybe
10443 /// a special media state for it to make it even more simple.
10444
10445 Assert(mMediaData.isBackedUp());
10446
10447 /* will release the lock before the potentially lengthy operation, so
10448 * protect with the special state */
10449 MachineState_T oldState = mData->mMachineState;
10450 setMachineState(MachineState_SettingUp);
10451
10452 writeLock.release();
10453
10454 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
10455 true /*aWait*/);
10456
10457 writeLock.acquire();
10458
10459 setMachineState(oldState);
10460
10461 if (FAILED(rc)) return rc;
10462 }
10463
10464 setModified(IsModified_Storage);
10465 mMediaData.backup();
10466 mMediaData->mAttachments.remove(pAttach);
10467
10468 if (!oldmedium.isNull())
10469 {
10470 // if this is from a snapshot, do not defer detachment to commitMedia()
10471 if (pSnapshot)
10472 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
10473 // else if non-hard disk media, do not defer detachment to commitMedia() either
10474 else if (mediumType != DeviceType_HardDisk)
10475 oldmedium->removeBackReference(mData->mUuid);
10476 }
10477
10478 return S_OK;
10479}
10480
10481/**
10482 * Goes thru all media of the given list and
10483 *
10484 * 1) calls detachDevice() on each of them for this machine and
10485 * 2) adds all Medium objects found in the process to the given list,
10486 * depending on cleanupMode.
10487 *
10488 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10489 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10490 * media to the list.
10491 *
10492 * This gets called from Machine::Unregister, both for the actual Machine and
10493 * the SnapshotMachine objects that might be found in the snapshots.
10494 *
10495 * Requires caller and locking. The machine lock must be passed in because it
10496 * will be passed on to detachDevice which needs it for temporary unlocking.
10497 *
10498 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
10499 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
10500 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
10501 * otherwise no media get added.
10502 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
10503 * @return
10504 */
10505HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
10506 Snapshot *pSnapshot,
10507 CleanupMode_T cleanupMode,
10508 MediaList &llMedia)
10509{
10510 Assert(isWriteLockOnCurrentThread());
10511
10512 HRESULT rc;
10513
10514 // make a temporary list because detachDevice invalidates iterators into
10515 // mMediaData->mAttachments
10516 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
10517
10518 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
10519 it != llAttachments2.end();
10520 ++it)
10521 {
10522 ComObjPtr<MediumAttachment> &pAttach = *it;
10523 ComObjPtr<Medium> pMedium = pAttach->getMedium();
10524
10525 if (!pMedium.isNull())
10526 {
10527 AutoCaller mac(pMedium);
10528 if (FAILED(mac.rc())) return mac.rc();
10529 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
10530 DeviceType_T devType = pMedium->getDeviceType();
10531 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
10532 && devType == DeviceType_HardDisk)
10533 || (cleanupMode == CleanupMode_Full)
10534 )
10535 {
10536 llMedia.push_back(pMedium);
10537 ComObjPtr<Medium> pParent = pMedium->getParent();
10538 /*
10539 * Search for medias which are not attached to any machine, but
10540 * in the chain to an attached disk. Mediums are only consided
10541 * if they are:
10542 * - have only one child
10543 * - no references to any machines
10544 * - are of normal medium type
10545 */
10546 while (!pParent.isNull())
10547 {
10548 AutoCaller mac1(pParent);
10549 if (FAILED(mac1.rc())) return mac1.rc();
10550 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
10551 if (pParent->getChildren().size() == 1)
10552 {
10553 if ( pParent->getMachineBackRefCount() == 0
10554 && pParent->getType() == MediumType_Normal
10555 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
10556 llMedia.push_back(pParent);
10557 }
10558 else
10559 break;
10560 pParent = pParent->getParent();
10561 }
10562 }
10563 }
10564
10565 // real machine: then we need to use the proper method
10566 rc = detachDevice(pAttach, writeLock, pSnapshot);
10567
10568 if (FAILED(rc))
10569 return rc;
10570 }
10571
10572 return S_OK;
10573}
10574
10575/**
10576 * Perform deferred hard disk detachments.
10577 *
10578 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10579 * backed up).
10580 *
10581 * If @a aOnline is @c true then this method will also unlock the old hard disks
10582 * for which the new implicit diffs were created and will lock these new diffs for
10583 * writing.
10584 *
10585 * @param aOnline Whether the VM was online prior to this operation.
10586 *
10587 * @note Locks this object for writing!
10588 */
10589void Machine::commitMedia(bool aOnline /*= false*/)
10590{
10591 AutoCaller autoCaller(this);
10592 AssertComRCReturnVoid(autoCaller.rc());
10593
10594 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10595
10596 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
10597
10598 HRESULT rc = S_OK;
10599
10600 /* no attach/detach operations -- nothing to do */
10601 if (!mMediaData.isBackedUp())
10602 return;
10603
10604 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10605 bool fMediaNeedsLocking = false;
10606
10607 /* enumerate new attachments */
10608 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10609 it != mMediaData->mAttachments.end();
10610 ++it)
10611 {
10612 MediumAttachment *pAttach = *it;
10613
10614 pAttach->commit();
10615
10616 Medium* pMedium = pAttach->getMedium();
10617 bool fImplicit = pAttach->isImplicit();
10618
10619 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
10620 (pMedium) ? pMedium->getName().c_str() : "NULL",
10621 fImplicit));
10622
10623 /** @todo convert all this Machine-based voodoo to MediumAttachment
10624 * based commit logic. */
10625 if (fImplicit)
10626 {
10627 /* convert implicit attachment to normal */
10628 pAttach->setImplicit(false);
10629
10630 if ( aOnline
10631 && pMedium
10632 && pAttach->getType() == DeviceType_HardDisk
10633 )
10634 {
10635 ComObjPtr<Medium> parent = pMedium->getParent();
10636 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
10637
10638 /* update the appropriate lock list */
10639 MediumLockList *pMediumLockList;
10640 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10641 AssertComRC(rc);
10642 if (pMediumLockList)
10643 {
10644 /* unlock if there's a need to change the locking */
10645 if (!fMediaNeedsLocking)
10646 {
10647 rc = mData->mSession.mLockedMedia.Unlock();
10648 AssertComRC(rc);
10649 fMediaNeedsLocking = true;
10650 }
10651 rc = pMediumLockList->Update(parent, false);
10652 AssertComRC(rc);
10653 rc = pMediumLockList->Append(pMedium, true);
10654 AssertComRC(rc);
10655 }
10656 }
10657
10658 continue;
10659 }
10660
10661 if (pMedium)
10662 {
10663 /* was this medium attached before? */
10664 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
10665 oldIt != oldAtts.end();
10666 ++oldIt)
10667 {
10668 MediumAttachment *pOldAttach = *oldIt;
10669 if (pOldAttach->getMedium() == pMedium)
10670 {
10671 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
10672
10673 /* yes: remove from old to avoid de-association */
10674 oldAtts.erase(oldIt);
10675 break;
10676 }
10677 }
10678 }
10679 }
10680
10681 /* enumerate remaining old attachments and de-associate from the
10682 * current machine state */
10683 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
10684 it != oldAtts.end();
10685 ++it)
10686 {
10687 MediumAttachment *pAttach = *it;
10688 Medium* pMedium = pAttach->getMedium();
10689
10690 /* Detach only hard disks, since DVD/floppy media is detached
10691 * instantly in MountMedium. */
10692 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
10693 {
10694 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
10695
10696 /* now de-associate from the current machine state */
10697 rc = pMedium->removeBackReference(mData->mUuid);
10698 AssertComRC(rc);
10699
10700 if (aOnline)
10701 {
10702 /* unlock since medium is not used anymore */
10703 MediumLockList *pMediumLockList;
10704 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10705 AssertComRC(rc);
10706 if (pMediumLockList)
10707 {
10708 rc = mData->mSession.mLockedMedia.Remove(pAttach);
10709 AssertComRC(rc);
10710 }
10711 }
10712 }
10713 }
10714
10715 /* take media locks again so that the locking state is consistent */
10716 if (fMediaNeedsLocking)
10717 {
10718 Assert(aOnline);
10719 rc = mData->mSession.mLockedMedia.Lock();
10720 AssertComRC(rc);
10721 }
10722
10723 /* commit the hard disk changes */
10724 mMediaData.commit();
10725
10726 if (isSessionMachine())
10727 {
10728 /*
10729 * Update the parent machine to point to the new owner.
10730 * This is necessary because the stored parent will point to the
10731 * session machine otherwise and cause crashes or errors later
10732 * when the session machine gets invalid.
10733 */
10734 /** @todo Change the MediumAttachment class to behave like any other
10735 * class in this regard by creating peer MediumAttachment
10736 * objects for session machines and share the data with the peer
10737 * machine.
10738 */
10739 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10740 it != mMediaData->mAttachments.end();
10741 ++it)
10742 {
10743 (*it)->updateParentMachine(mPeer);
10744 }
10745
10746 /* attach new data to the primary machine and reshare it */
10747 mPeer->mMediaData.attach(mMediaData);
10748 }
10749
10750 return;
10751}
10752
10753/**
10754 * Perform deferred deletion of implicitly created diffs.
10755 *
10756 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10757 * backed up).
10758 *
10759 * @note Locks this object for writing!
10760 */
10761void Machine::rollbackMedia()
10762{
10763 AutoCaller autoCaller(this);
10764 AssertComRCReturnVoid (autoCaller.rc());
10765
10766 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10767
10768 LogFlowThisFunc(("Entering\n"));
10769
10770 HRESULT rc = S_OK;
10771
10772 /* no attach/detach operations -- nothing to do */
10773 if (!mMediaData.isBackedUp())
10774 return;
10775
10776 /* enumerate new attachments */
10777 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10778 it != mMediaData->mAttachments.end();
10779 ++it)
10780 {
10781 MediumAttachment *pAttach = *it;
10782 /* Fix up the backrefs for DVD/floppy media. */
10783 if (pAttach->getType() != DeviceType_HardDisk)
10784 {
10785 Medium* pMedium = pAttach->getMedium();
10786 if (pMedium)
10787 {
10788 rc = pMedium->removeBackReference(mData->mUuid);
10789 AssertComRC(rc);
10790 }
10791 }
10792
10793 (*it)->rollback();
10794
10795 pAttach = *it;
10796 /* Fix up the backrefs for DVD/floppy media. */
10797 if (pAttach->getType() != DeviceType_HardDisk)
10798 {
10799 Medium* pMedium = pAttach->getMedium();
10800 if (pMedium)
10801 {
10802 rc = pMedium->addBackReference(mData->mUuid);
10803 AssertComRC(rc);
10804 }
10805 }
10806 }
10807
10808 /** @todo convert all this Machine-based voodoo to MediumAttachment
10809 * based rollback logic. */
10810 deleteImplicitDiffs();
10811
10812 return;
10813}
10814
10815/**
10816 * Returns true if the settings file is located in the directory named exactly
10817 * as the machine; this means, among other things, that the machine directory
10818 * should be auto-renamed.
10819 *
10820 * @param aSettingsDir if not NULL, the full machine settings file directory
10821 * name will be assigned there.
10822 *
10823 * @note Doesn't lock anything.
10824 * @note Not thread safe (must be called from this object's lock).
10825 */
10826bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
10827{
10828 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
10829 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
10830 if (aSettingsDir)
10831 *aSettingsDir = strMachineDirName;
10832 strMachineDirName.stripPath(); // vmname
10833 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
10834 strConfigFileOnly.stripPath() // vmname.vbox
10835 .stripExt(); // vmname
10836
10837 AssertReturn(!strMachineDirName.isEmpty(), false);
10838 AssertReturn(!strConfigFileOnly.isEmpty(), false);
10839
10840 return strMachineDirName == strConfigFileOnly;
10841}
10842
10843/**
10844 * Discards all changes to machine settings.
10845 *
10846 * @param aNotify Whether to notify the direct session about changes or not.
10847 *
10848 * @note Locks objects for writing!
10849 */
10850void Machine::rollback(bool aNotify)
10851{
10852 AutoCaller autoCaller(this);
10853 AssertComRCReturn(autoCaller.rc(), (void)0);
10854
10855 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10856
10857 if (!mStorageControllers.isNull())
10858 {
10859 if (mStorageControllers.isBackedUp())
10860 {
10861 /* unitialize all new devices (absent in the backed up list). */
10862 StorageControllerList::const_iterator it = mStorageControllers->begin();
10863 StorageControllerList *backedList = mStorageControllers.backedUpData();
10864 while (it != mStorageControllers->end())
10865 {
10866 if ( std::find(backedList->begin(), backedList->end(), *it)
10867 == backedList->end()
10868 )
10869 {
10870 (*it)->uninit();
10871 }
10872 ++it;
10873 }
10874
10875 /* restore the list */
10876 mStorageControllers.rollback();
10877 }
10878
10879 /* rollback any changes to devices after restoring the list */
10880 if (mData->flModifications & IsModified_Storage)
10881 {
10882 StorageControllerList::const_iterator it = mStorageControllers->begin();
10883 while (it != mStorageControllers->end())
10884 {
10885 (*it)->rollback();
10886 ++it;
10887 }
10888 }
10889 }
10890
10891 mUserData.rollback();
10892
10893 mHWData.rollback();
10894
10895 if (mData->flModifications & IsModified_Storage)
10896 rollbackMedia();
10897
10898 if (mBIOSSettings)
10899 mBIOSSettings->rollback();
10900
10901 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
10902 mVRDEServer->rollback();
10903
10904 if (mAudioAdapter)
10905 mAudioAdapter->rollback();
10906
10907 if (mUSBController && (mData->flModifications & IsModified_USB))
10908 mUSBController->rollback();
10909
10910 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
10911 mBandwidthControl->rollback();
10912
10913 if (!mHWData.isNull())
10914 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
10915 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
10916 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
10917 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
10918
10919 if (mData->flModifications & IsModified_NetworkAdapters)
10920 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
10921 if ( mNetworkAdapters[slot]
10922 && mNetworkAdapters[slot]->isModified())
10923 {
10924 mNetworkAdapters[slot]->rollback();
10925 networkAdapters[slot] = mNetworkAdapters[slot];
10926 }
10927
10928 if (mData->flModifications & IsModified_SerialPorts)
10929 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
10930 if ( mSerialPorts[slot]
10931 && mSerialPorts[slot]->isModified())
10932 {
10933 mSerialPorts[slot]->rollback();
10934 serialPorts[slot] = mSerialPorts[slot];
10935 }
10936
10937 if (mData->flModifications & IsModified_ParallelPorts)
10938 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
10939 if ( mParallelPorts[slot]
10940 && mParallelPorts[slot]->isModified())
10941 {
10942 mParallelPorts[slot]->rollback();
10943 parallelPorts[slot] = mParallelPorts[slot];
10944 }
10945
10946 if (aNotify)
10947 {
10948 /* inform the direct session about changes */
10949
10950 ComObjPtr<Machine> that = this;
10951 uint32_t flModifications = mData->flModifications;
10952 alock.release();
10953
10954 if (flModifications & IsModified_SharedFolders)
10955 that->onSharedFolderChange();
10956
10957 if (flModifications & IsModified_VRDEServer)
10958 that->onVRDEServerChange(/* aRestart */ TRUE);
10959 if (flModifications & IsModified_USB)
10960 that->onUSBControllerChange();
10961
10962 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
10963 if (networkAdapters[slot])
10964 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
10965 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
10966 if (serialPorts[slot])
10967 that->onSerialPortChange(serialPorts[slot]);
10968 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
10969 if (parallelPorts[slot])
10970 that->onParallelPortChange(parallelPorts[slot]);
10971
10972 if (flModifications & IsModified_Storage)
10973 that->onStorageControllerChange();
10974
10975#if 0
10976 if (flModifications & IsModified_BandwidthControl)
10977 that->onBandwidthControlChange();
10978#endif
10979 }
10980}
10981
10982/**
10983 * Commits all the changes to machine settings.
10984 *
10985 * Note that this operation is supposed to never fail.
10986 *
10987 * @note Locks this object and children for writing.
10988 */
10989void Machine::commit()
10990{
10991 AutoCaller autoCaller(this);
10992 AssertComRCReturnVoid(autoCaller.rc());
10993
10994 AutoCaller peerCaller(mPeer);
10995 AssertComRCReturnVoid(peerCaller.rc());
10996
10997 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
10998
10999 /*
11000 * use safe commit to ensure Snapshot machines (that share mUserData)
11001 * will still refer to a valid memory location
11002 */
11003 mUserData.commitCopy();
11004
11005 mHWData.commit();
11006
11007 if (mMediaData.isBackedUp())
11008 commitMedia();
11009
11010 mBIOSSettings->commit();
11011 mVRDEServer->commit();
11012 mAudioAdapter->commit();
11013 mUSBController->commit();
11014 mBandwidthControl->commit();
11015
11016 /* Keep the original network adapter count until this point, so that
11017 * discarding a chipset type change will not lose settings. */
11018 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11019 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11020 mNetworkAdapters[slot]->commit();
11021 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11022 mSerialPorts[slot]->commit();
11023 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11024 mParallelPorts[slot]->commit();
11025
11026 bool commitStorageControllers = false;
11027
11028 if (mStorageControllers.isBackedUp())
11029 {
11030 mStorageControllers.commit();
11031
11032 if (mPeer)
11033 {
11034 AutoWriteLock peerlock(mPeer COMMA_LOCKVAL_SRC_POS);
11035
11036 /* Commit all changes to new controllers (this will reshare data with
11037 * peers for those who have peers) */
11038 StorageControllerList *newList = new StorageControllerList();
11039 StorageControllerList::const_iterator it = mStorageControllers->begin();
11040 while (it != mStorageControllers->end())
11041 {
11042 (*it)->commit();
11043
11044 /* look if this controller has a peer device */
11045 ComObjPtr<StorageController> peer = (*it)->getPeer();
11046 if (!peer)
11047 {
11048 /* no peer means the device is a newly created one;
11049 * create a peer owning data this device share it with */
11050 peer.createObject();
11051 peer->init(mPeer, *it, true /* aReshare */);
11052 }
11053 else
11054 {
11055 /* remove peer from the old list */
11056 mPeer->mStorageControllers->remove(peer);
11057 }
11058 /* and add it to the new list */
11059 newList->push_back(peer);
11060
11061 ++it;
11062 }
11063
11064 /* uninit old peer's controllers that are left */
11065 it = mPeer->mStorageControllers->begin();
11066 while (it != mPeer->mStorageControllers->end())
11067 {
11068 (*it)->uninit();
11069 ++it;
11070 }
11071
11072 /* attach new list of controllers to our peer */
11073 mPeer->mStorageControllers.attach(newList);
11074 }
11075 else
11076 {
11077 /* we have no peer (our parent is the newly created machine);
11078 * just commit changes to devices */
11079 commitStorageControllers = true;
11080 }
11081 }
11082 else
11083 {
11084 /* the list of controllers itself is not changed,
11085 * just commit changes to controllers themselves */
11086 commitStorageControllers = true;
11087 }
11088
11089 if (commitStorageControllers)
11090 {
11091 StorageControllerList::const_iterator it = mStorageControllers->begin();
11092 while (it != mStorageControllers->end())
11093 {
11094 (*it)->commit();
11095 ++it;
11096 }
11097 }
11098
11099 if (isSessionMachine())
11100 {
11101 /* attach new data to the primary machine and reshare it */
11102 mPeer->mUserData.attach(mUserData);
11103 mPeer->mHWData.attach(mHWData);
11104 /* mMediaData is reshared by fixupMedia */
11105 // mPeer->mMediaData.attach(mMediaData);
11106 Assert(mPeer->mMediaData.data() == mMediaData.data());
11107 }
11108}
11109
11110/**
11111 * Copies all the hardware data from the given machine.
11112 *
11113 * Currently, only called when the VM is being restored from a snapshot. In
11114 * particular, this implies that the VM is not running during this method's
11115 * call.
11116 *
11117 * @note This method must be called from under this object's lock.
11118 *
11119 * @note This method doesn't call #commit(), so all data remains backed up and
11120 * unsaved.
11121 */
11122void Machine::copyFrom(Machine *aThat)
11123{
11124 AssertReturnVoid(!isSnapshotMachine());
11125 AssertReturnVoid(aThat->isSnapshotMachine());
11126
11127 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11128
11129 mHWData.assignCopy(aThat->mHWData);
11130
11131 // create copies of all shared folders (mHWData after attaching a copy
11132 // contains just references to original objects)
11133 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11134 it != mHWData->mSharedFolders.end();
11135 ++it)
11136 {
11137 ComObjPtr<SharedFolder> folder;
11138 folder.createObject();
11139 HRESULT rc = folder->initCopy(getMachine(), *it);
11140 AssertComRC(rc);
11141 *it = folder;
11142 }
11143
11144 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
11145 mVRDEServer->copyFrom(aThat->mVRDEServer);
11146 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
11147 mUSBController->copyFrom(aThat->mUSBController);
11148 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
11149
11150 /* create private copies of all controllers */
11151 mStorageControllers.backup();
11152 mStorageControllers->clear();
11153 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11154 it != aThat->mStorageControllers->end();
11155 ++it)
11156 {
11157 ComObjPtr<StorageController> ctrl;
11158 ctrl.createObject();
11159 ctrl->initCopy(this, *it);
11160 mStorageControllers->push_back(ctrl);
11161 }
11162
11163 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11164 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
11165 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11166 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
11167 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11168 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
11169}
11170
11171/**
11172 * Returns whether the given storage controller is hotplug capable.
11173 *
11174 * @returns true if the controller supports hotplugging
11175 * false otherwise.
11176 * @param enmCtrlType The controller type to check for.
11177 */
11178bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11179{
11180 switch (enmCtrlType)
11181 {
11182 case StorageControllerType_IntelAhci:
11183 return true;
11184 case StorageControllerType_LsiLogic:
11185 case StorageControllerType_LsiLogicSas:
11186 case StorageControllerType_BusLogic:
11187 case StorageControllerType_PIIX3:
11188 case StorageControllerType_PIIX4:
11189 case StorageControllerType_ICH6:
11190 case StorageControllerType_I82078:
11191 default:
11192 return false;
11193 }
11194}
11195
11196#ifdef VBOX_WITH_RESOURCE_USAGE_API
11197
11198void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11199{
11200 AssertReturnVoid(isWriteLockOnCurrentThread());
11201 AssertPtrReturnVoid(aCollector);
11202
11203 pm::CollectorHAL *hal = aCollector->getHAL();
11204 /* Create sub metrics */
11205 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11206 "Percentage of processor time spent in user mode by the VM process.");
11207 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11208 "Percentage of processor time spent in kernel mode by the VM process.");
11209 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11210 "Size of resident portion of VM process in memory.");
11211 /* Create and register base metrics */
11212 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11213 cpuLoadUser, cpuLoadKernel);
11214 aCollector->registerBaseMetric(cpuLoad);
11215 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11216 ramUsageUsed);
11217 aCollector->registerBaseMetric(ramUsage);
11218
11219 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11220 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11221 new pm::AggregateAvg()));
11222 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11223 new pm::AggregateMin()));
11224 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11225 new pm::AggregateMax()));
11226 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11227 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11228 new pm::AggregateAvg()));
11229 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11230 new pm::AggregateMin()));
11231 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11232 new pm::AggregateMax()));
11233
11234 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11235 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11236 new pm::AggregateAvg()));
11237 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11238 new pm::AggregateMin()));
11239 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11240 new pm::AggregateMax()));
11241
11242
11243 /* Guest metrics collector */
11244 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11245 aCollector->registerGuest(mCollectorGuest);
11246 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11247 this, __PRETTY_FUNCTION__, mCollectorGuest));
11248
11249 /* Create sub metrics */
11250 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11251 "Percentage of processor time spent in user mode as seen by the guest.");
11252 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11253 "Percentage of processor time spent in kernel mode as seen by the guest.");
11254 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11255 "Percentage of processor time spent idling as seen by the guest.");
11256
11257 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11258 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11259 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11260 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11261 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11262 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11263
11264 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11265
11266 /* Create and register base metrics */
11267 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11268 guestLoadUser, guestLoadKernel, guestLoadIdle);
11269 aCollector->registerBaseMetric(guestCpuLoad);
11270
11271 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11272 guestMemTotal, guestMemFree,
11273 guestMemBalloon, guestMemShared,
11274 guestMemCache, guestPagedTotal);
11275 aCollector->registerBaseMetric(guestCpuMem);
11276
11277 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11278 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11279 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11280 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11281
11282 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11283 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11284 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11285 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11286
11287 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11288 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11289 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11290 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11291
11292 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11293 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11294 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11295 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11296
11297 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11298 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11299 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11300 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11301
11302 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11303 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11304 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11305 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11306
11307 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11308 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11309 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11310 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11311
11312 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11313 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11314 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11315 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11316
11317 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11318 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11319 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11320 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11321}
11322
11323void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11324{
11325 AssertReturnVoid(isWriteLockOnCurrentThread());
11326
11327 if (aCollector)
11328 {
11329 aCollector->unregisterMetricsFor(aMachine);
11330 aCollector->unregisterBaseMetricsFor(aMachine);
11331 }
11332}
11333
11334#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11335
11336
11337////////////////////////////////////////////////////////////////////////////////
11338
11339DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11340
11341HRESULT SessionMachine::FinalConstruct()
11342{
11343 LogFlowThisFunc(("\n"));
11344
11345#if defined(RT_OS_WINDOWS)
11346 mIPCSem = NULL;
11347#elif defined(RT_OS_OS2)
11348 mIPCSem = NULLHANDLE;
11349#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11350 mIPCSem = -1;
11351#else
11352# error "Port me!"
11353#endif
11354
11355 return BaseFinalConstruct();
11356}
11357
11358void SessionMachine::FinalRelease()
11359{
11360 LogFlowThisFunc(("\n"));
11361
11362 uninit(Uninit::Unexpected);
11363
11364 BaseFinalRelease();
11365}
11366
11367/**
11368 * @note Must be called only by Machine::openSession() from its own write lock.
11369 */
11370HRESULT SessionMachine::init(Machine *aMachine)
11371{
11372 LogFlowThisFuncEnter();
11373 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
11374
11375 AssertReturn(aMachine, E_INVALIDARG);
11376
11377 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
11378
11379 /* Enclose the state transition NotReady->InInit->Ready */
11380 AutoInitSpan autoInitSpan(this);
11381 AssertReturn(autoInitSpan.isOk(), E_FAIL);
11382
11383 /* create the interprocess semaphore */
11384#if defined(RT_OS_WINDOWS)
11385 mIPCSemName = aMachine->mData->m_strConfigFileFull;
11386 for (size_t i = 0; i < mIPCSemName.length(); i++)
11387 if (mIPCSemName.raw()[i] == '\\')
11388 mIPCSemName.raw()[i] = '/';
11389 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
11390 ComAssertMsgRet(mIPCSem,
11391 ("Cannot create IPC mutex '%ls', err=%d",
11392 mIPCSemName.raw(), ::GetLastError()),
11393 E_FAIL);
11394#elif defined(RT_OS_OS2)
11395 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
11396 aMachine->mData->mUuid.raw());
11397 mIPCSemName = ipcSem;
11398 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
11399 ComAssertMsgRet(arc == NO_ERROR,
11400 ("Cannot create IPC mutex '%s', arc=%ld",
11401 ipcSem.c_str(), arc),
11402 E_FAIL);
11403#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11404# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11405# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
11406 /** @todo Check that this still works correctly. */
11407 AssertCompileSize(key_t, 8);
11408# else
11409 AssertCompileSize(key_t, 4);
11410# endif
11411 key_t key;
11412 mIPCSem = -1;
11413 mIPCKey = "0";
11414 for (uint32_t i = 0; i < 1 << 24; i++)
11415 {
11416 key = ((uint32_t)'V' << 24) | i;
11417 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
11418 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
11419 {
11420 mIPCSem = sem;
11421 if (sem >= 0)
11422 mIPCKey = BstrFmt("%u", key);
11423 break;
11424 }
11425 }
11426# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11427 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
11428 char *pszSemName = NULL;
11429 RTStrUtf8ToCurrentCP(&pszSemName, semName);
11430 key_t key = ::ftok(pszSemName, 'V');
11431 RTStrFree(pszSemName);
11432
11433 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
11434# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11435
11436 int errnoSave = errno;
11437 if (mIPCSem < 0 && errnoSave == ENOSYS)
11438 {
11439 setError(E_FAIL,
11440 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
11441 "support for SysV IPC. Check the host kernel configuration for "
11442 "CONFIG_SYSVIPC=y"));
11443 return E_FAIL;
11444 }
11445 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
11446 * the IPC semaphores */
11447 if (mIPCSem < 0 && errnoSave == ENOSPC)
11448 {
11449#ifdef RT_OS_LINUX
11450 setError(E_FAIL,
11451 tr("Cannot create IPC semaphore because the system limit for the "
11452 "maximum number of semaphore sets (SEMMNI), or the system wide "
11453 "maximum number of semaphores (SEMMNS) would be exceeded. The "
11454 "current set of SysV IPC semaphores can be determined from "
11455 "the file /proc/sysvipc/sem"));
11456#else
11457 setError(E_FAIL,
11458 tr("Cannot create IPC semaphore because the system-imposed limit "
11459 "on the maximum number of allowed semaphores or semaphore "
11460 "identifiers system-wide would be exceeded"));
11461#endif
11462 return E_FAIL;
11463 }
11464 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
11465 E_FAIL);
11466 /* set the initial value to 1 */
11467 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
11468 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
11469 E_FAIL);
11470#else
11471# error "Port me!"
11472#endif
11473
11474 /* memorize the peer Machine */
11475 unconst(mPeer) = aMachine;
11476 /* share the parent pointer */
11477 unconst(mParent) = aMachine->mParent;
11478
11479 /* take the pointers to data to share */
11480 mData.share(aMachine->mData);
11481 mSSData.share(aMachine->mSSData);
11482
11483 mUserData.share(aMachine->mUserData);
11484 mHWData.share(aMachine->mHWData);
11485 mMediaData.share(aMachine->mMediaData);
11486
11487 mStorageControllers.allocate();
11488 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
11489 it != aMachine->mStorageControllers->end();
11490 ++it)
11491 {
11492 ComObjPtr<StorageController> ctl;
11493 ctl.createObject();
11494 ctl->init(this, *it);
11495 mStorageControllers->push_back(ctl);
11496 }
11497
11498 unconst(mBIOSSettings).createObject();
11499 mBIOSSettings->init(this, aMachine->mBIOSSettings);
11500 /* create another VRDEServer object that will be mutable */
11501 unconst(mVRDEServer).createObject();
11502 mVRDEServer->init(this, aMachine->mVRDEServer);
11503 /* create another audio adapter object that will be mutable */
11504 unconst(mAudioAdapter).createObject();
11505 mAudioAdapter->init(this, aMachine->mAudioAdapter);
11506 /* create a list of serial ports that will be mutable */
11507 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11508 {
11509 unconst(mSerialPorts[slot]).createObject();
11510 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
11511 }
11512 /* create a list of parallel ports that will be mutable */
11513 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11514 {
11515 unconst(mParallelPorts[slot]).createObject();
11516 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
11517 }
11518 /* create another USB controller object that will be mutable */
11519 unconst(mUSBController).createObject();
11520 mUSBController->init(this, aMachine->mUSBController);
11521
11522 /* create a list of network adapters that will be mutable */
11523 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
11524 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11525 {
11526 unconst(mNetworkAdapters[slot]).createObject();
11527 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
11528 }
11529
11530 /* create another bandwidth control object that will be mutable */
11531 unconst(mBandwidthControl).createObject();
11532 mBandwidthControl->init(this, aMachine->mBandwidthControl);
11533
11534 /* default is to delete saved state on Saved -> PoweredOff transition */
11535 mRemoveSavedState = true;
11536
11537 /* Confirm a successful initialization when it's the case */
11538 autoInitSpan.setSucceeded();
11539
11540 LogFlowThisFuncLeave();
11541 return S_OK;
11542}
11543
11544/**
11545 * Uninitializes this session object. If the reason is other than
11546 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
11547 *
11548 * @param aReason uninitialization reason
11549 *
11550 * @note Locks mParent + this object for writing.
11551 */
11552void SessionMachine::uninit(Uninit::Reason aReason)
11553{
11554 LogFlowThisFuncEnter();
11555 LogFlowThisFunc(("reason=%d\n", aReason));
11556
11557 /*
11558 * Strongly reference ourselves to prevent this object deletion after
11559 * mData->mSession.mMachine.setNull() below (which can release the last
11560 * reference and call the destructor). Important: this must be done before
11561 * accessing any members (and before AutoUninitSpan that does it as well).
11562 * This self reference will be released as the very last step on return.
11563 */
11564 ComObjPtr<SessionMachine> selfRef = this;
11565
11566 /* Enclose the state transition Ready->InUninit->NotReady */
11567 AutoUninitSpan autoUninitSpan(this);
11568 if (autoUninitSpan.uninitDone())
11569 {
11570 LogFlowThisFunc(("Already uninitialized\n"));
11571 LogFlowThisFuncLeave();
11572 return;
11573 }
11574
11575 if (autoUninitSpan.initFailed())
11576 {
11577 /* We've been called by init() because it's failed. It's not really
11578 * necessary (nor it's safe) to perform the regular uninit sequence
11579 * below, the following is enough.
11580 */
11581 LogFlowThisFunc(("Initialization failed.\n"));
11582#if defined(RT_OS_WINDOWS)
11583 if (mIPCSem)
11584 ::CloseHandle(mIPCSem);
11585 mIPCSem = NULL;
11586#elif defined(RT_OS_OS2)
11587 if (mIPCSem != NULLHANDLE)
11588 ::DosCloseMutexSem(mIPCSem);
11589 mIPCSem = NULLHANDLE;
11590#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11591 if (mIPCSem >= 0)
11592 ::semctl(mIPCSem, 0, IPC_RMID);
11593 mIPCSem = -1;
11594# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11595 mIPCKey = "0";
11596# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
11597#else
11598# error "Port me!"
11599#endif
11600 uninitDataAndChildObjects();
11601 mData.free();
11602 unconst(mParent) = NULL;
11603 unconst(mPeer) = NULL;
11604 LogFlowThisFuncLeave();
11605 return;
11606 }
11607
11608 MachineState_T lastState;
11609 {
11610 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
11611 lastState = mData->mMachineState;
11612 }
11613 NOREF(lastState);
11614
11615#ifdef VBOX_WITH_USB
11616 // release all captured USB devices, but do this before requesting the locks below
11617 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
11618 {
11619 /* Console::captureUSBDevices() is called in the VM process only after
11620 * setting the machine state to Starting or Restoring.
11621 * Console::detachAllUSBDevices() will be called upon successful
11622 * termination. So, we need to release USB devices only if there was
11623 * an abnormal termination of a running VM.
11624 *
11625 * This is identical to SessionMachine::DetachAllUSBDevices except
11626 * for the aAbnormal argument. */
11627 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
11628 AssertComRC(rc);
11629 NOREF(rc);
11630
11631 USBProxyService *service = mParent->host()->usbProxyService();
11632 if (service)
11633 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
11634 }
11635#endif /* VBOX_WITH_USB */
11636
11637 // we need to lock this object in uninit() because the lock is shared
11638 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
11639 // and others need mParent lock, and USB needs host lock.
11640 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
11641
11642#if 0
11643 // Trigger async cleanup tasks, avoid doing things here which are not
11644 // vital to be done immediately and maybe need more locks. This calls
11645 // Machine::unregisterMetrics().
11646 mParent->onMachineUninit(mPeer);
11647#else
11648 /*
11649 * It is safe to call Machine::unregisterMetrics() here because
11650 * PerformanceCollector::samplerCallback no longer accesses guest methods
11651 * holding the lock.
11652 */
11653 unregisterMetrics(mParent->performanceCollector(), mPeer);
11654#endif
11655 /* The guest must be unregistered after its metrics (@bugref{5949}). */
11656 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11657 this, __PRETTY_FUNCTION__, mCollectorGuest));
11658 if (mCollectorGuest)
11659 {
11660 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
11661 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
11662 mCollectorGuest = NULL;
11663 }
11664
11665 if (aReason == Uninit::Abnormal)
11666 {
11667 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
11668 Global::IsOnlineOrTransient(lastState)));
11669
11670 /* reset the state to Aborted */
11671 if (mData->mMachineState != MachineState_Aborted)
11672 setMachineState(MachineState_Aborted);
11673 }
11674
11675 // any machine settings modified?
11676 if (mData->flModifications)
11677 {
11678 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
11679 rollback(false /* aNotify */);
11680 }
11681
11682 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
11683 || !mConsoleTaskData.mSnapshot);
11684 if (!mConsoleTaskData.strStateFilePath.isEmpty())
11685 {
11686 LogWarningThisFunc(("canceling failed save state request!\n"));
11687 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
11688 }
11689 else if (!mConsoleTaskData.mSnapshot.isNull())
11690 {
11691 LogWarningThisFunc(("canceling untaken snapshot!\n"));
11692
11693 /* delete all differencing hard disks created (this will also attach
11694 * their parents back by rolling back mMediaData) */
11695 rollbackMedia();
11696
11697 // delete the saved state file (it might have been already created)
11698 // AFTER killing the snapshot so that releaseSavedStateFile() won't
11699 // think it's still in use
11700 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
11701 mConsoleTaskData.mSnapshot->uninit();
11702 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
11703 }
11704
11705 if (!mData->mSession.mType.isEmpty())
11706 {
11707 /* mType is not null when this machine's process has been started by
11708 * Machine::LaunchVMProcess(), therefore it is our child. We
11709 * need to queue the PID to reap the process (and avoid zombies on
11710 * Linux). */
11711 Assert(mData->mSession.mPID != NIL_RTPROCESS);
11712 mParent->addProcessToReap(mData->mSession.mPID);
11713 }
11714
11715 mData->mSession.mPID = NIL_RTPROCESS;
11716
11717 if (aReason == Uninit::Unexpected)
11718 {
11719 /* Uninitialization didn't come from #checkForDeath(), so tell the
11720 * client watcher thread to update the set of machines that have open
11721 * sessions. */
11722 mParent->updateClientWatcher();
11723 }
11724
11725 /* uninitialize all remote controls */
11726 if (mData->mSession.mRemoteControls.size())
11727 {
11728 LogFlowThisFunc(("Closing remote sessions (%d):\n",
11729 mData->mSession.mRemoteControls.size()));
11730
11731 Data::Session::RemoteControlList::iterator it =
11732 mData->mSession.mRemoteControls.begin();
11733 while (it != mData->mSession.mRemoteControls.end())
11734 {
11735 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
11736 HRESULT rc = (*it)->Uninitialize();
11737 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
11738 if (FAILED(rc))
11739 LogWarningThisFunc(("Forgot to close the remote session?\n"));
11740 ++it;
11741 }
11742 mData->mSession.mRemoteControls.clear();
11743 }
11744
11745 /*
11746 * An expected uninitialization can come only from #checkForDeath().
11747 * Otherwise it means that something's gone really wrong (for example,
11748 * the Session implementation has released the VirtualBox reference
11749 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
11750 * etc). However, it's also possible, that the client releases the IPC
11751 * semaphore correctly (i.e. before it releases the VirtualBox reference),
11752 * but the VirtualBox release event comes first to the server process.
11753 * This case is practically possible, so we should not assert on an
11754 * unexpected uninit, just log a warning.
11755 */
11756
11757 if ((aReason == Uninit::Unexpected))
11758 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
11759
11760 if (aReason != Uninit::Normal)
11761 {
11762 mData->mSession.mDirectControl.setNull();
11763 }
11764 else
11765 {
11766 /* this must be null here (see #OnSessionEnd()) */
11767 Assert(mData->mSession.mDirectControl.isNull());
11768 Assert(mData->mSession.mState == SessionState_Unlocking);
11769 Assert(!mData->mSession.mProgress.isNull());
11770 }
11771 if (mData->mSession.mProgress)
11772 {
11773 if (aReason == Uninit::Normal)
11774 mData->mSession.mProgress->notifyComplete(S_OK);
11775 else
11776 mData->mSession.mProgress->notifyComplete(E_FAIL,
11777 COM_IIDOF(ISession),
11778 getComponentName(),
11779 tr("The VM session was aborted"));
11780 mData->mSession.mProgress.setNull();
11781 }
11782
11783 /* remove the association between the peer machine and this session machine */
11784 Assert( (SessionMachine*)mData->mSession.mMachine == this
11785 || aReason == Uninit::Unexpected);
11786
11787 /* reset the rest of session data */
11788 mData->mSession.mMachine.setNull();
11789 mData->mSession.mState = SessionState_Unlocked;
11790 mData->mSession.mType.setNull();
11791
11792 /* close the interprocess semaphore before leaving the exclusive lock */
11793#if defined(RT_OS_WINDOWS)
11794 if (mIPCSem)
11795 ::CloseHandle(mIPCSem);
11796 mIPCSem = NULL;
11797#elif defined(RT_OS_OS2)
11798 if (mIPCSem != NULLHANDLE)
11799 ::DosCloseMutexSem(mIPCSem);
11800 mIPCSem = NULLHANDLE;
11801#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11802 if (mIPCSem >= 0)
11803 ::semctl(mIPCSem, 0, IPC_RMID);
11804 mIPCSem = -1;
11805# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11806 mIPCKey = "0";
11807# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
11808#else
11809# error "Port me!"
11810#endif
11811
11812 /* fire an event */
11813 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
11814
11815 uninitDataAndChildObjects();
11816
11817 /* free the essential data structure last */
11818 mData.free();
11819
11820 /* release the exclusive lock before setting the below two to NULL */
11821 multilock.release();
11822
11823 unconst(mParent) = NULL;
11824 unconst(mPeer) = NULL;
11825
11826 LogFlowThisFuncLeave();
11827}
11828
11829// util::Lockable interface
11830////////////////////////////////////////////////////////////////////////////////
11831
11832/**
11833 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
11834 * with the primary Machine instance (mPeer).
11835 */
11836RWLockHandle *SessionMachine::lockHandle() const
11837{
11838 AssertReturn(mPeer != NULL, NULL);
11839 return mPeer->lockHandle();
11840}
11841
11842// IInternalMachineControl methods
11843////////////////////////////////////////////////////////////////////////////////
11844
11845/**
11846 * Passes collected guest statistics to performance collector object
11847 */
11848STDMETHODIMP SessionMachine::ReportGuestStatistics(ULONG aValidStats, ULONG aCpuUser,
11849 ULONG aCpuKernel, ULONG aCpuIdle,
11850 ULONG aMemTotal, ULONG aMemFree,
11851 ULONG aMemBalloon, ULONG aMemShared,
11852 ULONG aMemCache, ULONG aPageTotal,
11853 ULONG aAllocVMM, ULONG aFreeVMM,
11854 ULONG aBalloonedVMM, ULONG aSharedVMM)
11855{
11856 if (mCollectorGuest)
11857 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
11858 aMemTotal, aMemFree, aMemBalloon, aMemShared,
11859 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
11860 aBalloonedVMM, aSharedVMM);
11861
11862 return S_OK;
11863}
11864
11865/**
11866 * @note Locks this object for writing.
11867 */
11868STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
11869{
11870 AutoCaller autoCaller(this);
11871 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11872
11873 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11874
11875 mRemoveSavedState = aRemove;
11876
11877 return S_OK;
11878}
11879
11880/**
11881 * @note Locks the same as #setMachineState() does.
11882 */
11883STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
11884{
11885 return setMachineState(aMachineState);
11886}
11887
11888/**
11889 * @note Locks this object for reading.
11890 */
11891STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
11892{
11893 AutoCaller autoCaller(this);
11894 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11895
11896 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11897
11898#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
11899 mIPCSemName.cloneTo(aId);
11900 return S_OK;
11901#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11902# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11903 mIPCKey.cloneTo(aId);
11904# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11905 mData->m_strConfigFileFull.cloneTo(aId);
11906# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11907 return S_OK;
11908#else
11909# error "Port me!"
11910#endif
11911}
11912
11913/**
11914 * @note Locks this object for writing.
11915 */
11916STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
11917{
11918 LogFlowThisFunc(("aProgress=%p\n", aProgress));
11919 AutoCaller autoCaller(this);
11920 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11921
11922 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11923
11924 if (mData->mSession.mState != SessionState_Locked)
11925 return VBOX_E_INVALID_OBJECT_STATE;
11926
11927 if (!mData->mSession.mProgress.isNull())
11928 mData->mSession.mProgress->setOtherProgressObject(aProgress);
11929
11930 LogFlowThisFunc(("returns S_OK.\n"));
11931 return S_OK;
11932}
11933
11934/**
11935 * @note Locks this object for writing.
11936 */
11937STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
11938{
11939 AutoCaller autoCaller(this);
11940 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11941
11942 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11943
11944 if (mData->mSession.mState != SessionState_Locked)
11945 return VBOX_E_INVALID_OBJECT_STATE;
11946
11947 /* Finalize the LaunchVMProcess progress object. */
11948 if (mData->mSession.mProgress)
11949 {
11950 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
11951 mData->mSession.mProgress.setNull();
11952 }
11953
11954 if (SUCCEEDED((HRESULT)iResult))
11955 {
11956#ifdef VBOX_WITH_RESOURCE_USAGE_API
11957 /* The VM has been powered up successfully, so it makes sense
11958 * now to offer the performance metrics for a running machine
11959 * object. Doing it earlier wouldn't be safe. */
11960 registerMetrics(mParent->performanceCollector(), mPeer,
11961 mData->mSession.mPID);
11962#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11963 }
11964
11965 return S_OK;
11966}
11967
11968/**
11969 * @note Locks this object for writing.
11970 */
11971STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
11972{
11973 LogFlowThisFuncEnter();
11974
11975 CheckComArgOutPointerValid(aProgress);
11976
11977 AutoCaller autoCaller(this);
11978 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11979
11980 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11981
11982 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
11983 E_FAIL);
11984
11985 /* create a progress object to track operation completion */
11986 ComObjPtr<Progress> pProgress;
11987 pProgress.createObject();
11988 pProgress->init(getVirtualBox(),
11989 static_cast<IMachine *>(this) /* aInitiator */,
11990 Bstr(tr("Stopping the virtual machine")).raw(),
11991 FALSE /* aCancelable */);
11992
11993 /* fill in the console task data */
11994 mConsoleTaskData.mLastState = mData->mMachineState;
11995 mConsoleTaskData.mProgress = pProgress;
11996
11997 /* set the state to Stopping (this is expected by Console::PowerDown()) */
11998 setMachineState(MachineState_Stopping);
11999
12000 pProgress.queryInterfaceTo(aProgress);
12001
12002 return S_OK;
12003}
12004
12005/**
12006 * @note Locks this object for writing.
12007 */
12008STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12009{
12010 LogFlowThisFuncEnter();
12011
12012 AutoCaller autoCaller(this);
12013 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12014
12015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12016
12017 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12018 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12019 && mConsoleTaskData.mLastState != MachineState_Null,
12020 E_FAIL);
12021
12022 /*
12023 * On failure, set the state to the state we had when BeginPoweringDown()
12024 * was called (this is expected by Console::PowerDown() and the associated
12025 * task). On success the VM process already changed the state to
12026 * MachineState_PoweredOff, so no need to do anything.
12027 */
12028 if (FAILED(iResult))
12029 setMachineState(mConsoleTaskData.mLastState);
12030
12031 /* notify the progress object about operation completion */
12032 Assert(mConsoleTaskData.mProgress);
12033 if (SUCCEEDED(iResult))
12034 mConsoleTaskData.mProgress->notifyComplete(S_OK);
12035 else
12036 {
12037 Utf8Str strErrMsg(aErrMsg);
12038 if (strErrMsg.length())
12039 mConsoleTaskData.mProgress->notifyComplete(iResult,
12040 COM_IIDOF(ISession),
12041 getComponentName(),
12042 strErrMsg.c_str());
12043 else
12044 mConsoleTaskData.mProgress->notifyComplete(iResult);
12045 }
12046
12047 /* clear out the temporary saved state data */
12048 mConsoleTaskData.mLastState = MachineState_Null;
12049 mConsoleTaskData.mProgress.setNull();
12050
12051 LogFlowThisFuncLeave();
12052 return S_OK;
12053}
12054
12055
12056/**
12057 * Goes through the USB filters of the given machine to see if the given
12058 * device matches any filter or not.
12059 *
12060 * @note Locks the same as USBController::hasMatchingFilter() does.
12061 */
12062STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12063 BOOL *aMatched,
12064 ULONG *aMaskedIfs)
12065{
12066 LogFlowThisFunc(("\n"));
12067
12068 CheckComArgNotNull(aUSBDevice);
12069 CheckComArgOutPointerValid(aMatched);
12070
12071 AutoCaller autoCaller(this);
12072 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12073
12074#ifdef VBOX_WITH_USB
12075 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
12076#else
12077 NOREF(aUSBDevice);
12078 NOREF(aMaskedIfs);
12079 *aMatched = FALSE;
12080#endif
12081
12082 return S_OK;
12083}
12084
12085/**
12086 * @note Locks the same as Host::captureUSBDevice() does.
12087 */
12088STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12089{
12090 LogFlowThisFunc(("\n"));
12091
12092 AutoCaller autoCaller(this);
12093 AssertComRCReturnRC(autoCaller.rc());
12094
12095#ifdef VBOX_WITH_USB
12096 /* if captureDeviceForVM() fails, it must have set extended error info */
12097 clearError();
12098 MultiResult rc = mParent->host()->checkUSBProxyService();
12099 if (FAILED(rc)) return rc;
12100
12101 USBProxyService *service = mParent->host()->usbProxyService();
12102 AssertReturn(service, E_FAIL);
12103 return service->captureDeviceForVM(this, Guid(aId).ref());
12104#else
12105 NOREF(aId);
12106 return E_NOTIMPL;
12107#endif
12108}
12109
12110/**
12111 * @note Locks the same as Host::detachUSBDevice() does.
12112 */
12113STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12114{
12115 LogFlowThisFunc(("\n"));
12116
12117 AutoCaller autoCaller(this);
12118 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12119
12120#ifdef VBOX_WITH_USB
12121 USBProxyService *service = mParent->host()->usbProxyService();
12122 AssertReturn(service, E_FAIL);
12123 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12124#else
12125 NOREF(aId);
12126 NOREF(aDone);
12127 return E_NOTIMPL;
12128#endif
12129}
12130
12131/**
12132 * Inserts all machine filters to the USB proxy service and then calls
12133 * Host::autoCaptureUSBDevices().
12134 *
12135 * Called by Console from the VM process upon VM startup.
12136 *
12137 * @note Locks what called methods lock.
12138 */
12139STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12140{
12141 LogFlowThisFunc(("\n"));
12142
12143 AutoCaller autoCaller(this);
12144 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12145
12146#ifdef VBOX_WITH_USB
12147 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
12148 AssertComRC(rc);
12149 NOREF(rc);
12150
12151 USBProxyService *service = mParent->host()->usbProxyService();
12152 AssertReturn(service, E_FAIL);
12153 return service->autoCaptureDevicesForVM(this);
12154#else
12155 return S_OK;
12156#endif
12157}
12158
12159/**
12160 * Removes all machine filters from the USB proxy service and then calls
12161 * Host::detachAllUSBDevices().
12162 *
12163 * Called by Console from the VM process upon normal VM termination or by
12164 * SessionMachine::uninit() upon abnormal VM termination (from under the
12165 * Machine/SessionMachine lock).
12166 *
12167 * @note Locks what called methods lock.
12168 */
12169STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12170{
12171 LogFlowThisFunc(("\n"));
12172
12173 AutoCaller autoCaller(this);
12174 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12175
12176#ifdef VBOX_WITH_USB
12177 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12178 AssertComRC(rc);
12179 NOREF(rc);
12180
12181 USBProxyService *service = mParent->host()->usbProxyService();
12182 AssertReturn(service, E_FAIL);
12183 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12184#else
12185 NOREF(aDone);
12186 return S_OK;
12187#endif
12188}
12189
12190/**
12191 * @note Locks this object for writing.
12192 */
12193STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12194 IProgress **aProgress)
12195{
12196 LogFlowThisFuncEnter();
12197
12198 AssertReturn(aSession, E_INVALIDARG);
12199 AssertReturn(aProgress, E_INVALIDARG);
12200
12201 AutoCaller autoCaller(this);
12202
12203 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
12204 /*
12205 * We don't assert below because it might happen that a non-direct session
12206 * informs us it is closed right after we've been uninitialized -- it's ok.
12207 */
12208 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12209
12210 /* get IInternalSessionControl interface */
12211 ComPtr<IInternalSessionControl> control(aSession);
12212
12213 ComAssertRet(!control.isNull(), E_INVALIDARG);
12214
12215 /* Creating a Progress object requires the VirtualBox lock, and
12216 * thus locking it here is required by the lock order rules. */
12217 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12218
12219 if (control == mData->mSession.mDirectControl)
12220 {
12221 ComAssertRet(aProgress, E_POINTER);
12222
12223 /* The direct session is being normally closed by the client process
12224 * ----------------------------------------------------------------- */
12225
12226 /* go to the closing state (essential for all open*Session() calls and
12227 * for #checkForDeath()) */
12228 Assert(mData->mSession.mState == SessionState_Locked);
12229 mData->mSession.mState = SessionState_Unlocking;
12230
12231 /* set direct control to NULL to release the remote instance */
12232 mData->mSession.mDirectControl.setNull();
12233 LogFlowThisFunc(("Direct control is set to NULL\n"));
12234
12235 if (mData->mSession.mProgress)
12236 {
12237 /* finalize the progress, someone might wait if a frontend
12238 * closes the session before powering on the VM. */
12239 mData->mSession.mProgress->notifyComplete(E_FAIL,
12240 COM_IIDOF(ISession),
12241 getComponentName(),
12242 tr("The VM session was closed before any attempt to power it on"));
12243 mData->mSession.mProgress.setNull();
12244 }
12245
12246 /* Create the progress object the client will use to wait until
12247 * #checkForDeath() is called to uninitialize this session object after
12248 * it releases the IPC semaphore.
12249 * Note! Because we're "reusing" mProgress here, this must be a proxy
12250 * object just like for LaunchVMProcess. */
12251 Assert(mData->mSession.mProgress.isNull());
12252 ComObjPtr<ProgressProxy> progress;
12253 progress.createObject();
12254 ComPtr<IUnknown> pPeer(mPeer);
12255 progress->init(mParent, pPeer,
12256 Bstr(tr("Closing session")).raw(),
12257 FALSE /* aCancelable */);
12258 progress.queryInterfaceTo(aProgress);
12259 mData->mSession.mProgress = progress;
12260 }
12261 else
12262 {
12263 /* the remote session is being normally closed */
12264 Data::Session::RemoteControlList::iterator it =
12265 mData->mSession.mRemoteControls.begin();
12266 while (it != mData->mSession.mRemoteControls.end())
12267 {
12268 if (control == *it)
12269 break;
12270 ++it;
12271 }
12272 BOOL found = it != mData->mSession.mRemoteControls.end();
12273 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12274 E_INVALIDARG);
12275 // This MUST be erase(it), not remove(*it) as the latter triggers a
12276 // very nasty use after free due to the place where the value "lives".
12277 mData->mSession.mRemoteControls.erase(it);
12278 }
12279
12280 /* signal the client watcher thread, because the client is going away */
12281 mParent->updateClientWatcher();
12282
12283 LogFlowThisFuncLeave();
12284 return S_OK;
12285}
12286
12287/**
12288 * @note Locks this object for writing.
12289 */
12290STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12291{
12292 LogFlowThisFuncEnter();
12293
12294 CheckComArgOutPointerValid(aProgress);
12295 CheckComArgOutPointerValid(aStateFilePath);
12296
12297 AutoCaller autoCaller(this);
12298 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12299
12300 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12301
12302 AssertReturn( mData->mMachineState == MachineState_Paused
12303 && mConsoleTaskData.mLastState == MachineState_Null
12304 && mConsoleTaskData.strStateFilePath.isEmpty(),
12305 E_FAIL);
12306
12307 /* create a progress object to track operation completion */
12308 ComObjPtr<Progress> pProgress;
12309 pProgress.createObject();
12310 pProgress->init(getVirtualBox(),
12311 static_cast<IMachine *>(this) /* aInitiator */,
12312 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12313 FALSE /* aCancelable */);
12314
12315 Utf8Str strStateFilePath;
12316 /* stateFilePath is null when the machine is not running */
12317 if (mData->mMachineState == MachineState_Paused)
12318 composeSavedStateFilename(strStateFilePath);
12319
12320 /* fill in the console task data */
12321 mConsoleTaskData.mLastState = mData->mMachineState;
12322 mConsoleTaskData.strStateFilePath = strStateFilePath;
12323 mConsoleTaskData.mProgress = pProgress;
12324
12325 /* set the state to Saving (this is expected by Console::SaveState()) */
12326 setMachineState(MachineState_Saving);
12327
12328 strStateFilePath.cloneTo(aStateFilePath);
12329 pProgress.queryInterfaceTo(aProgress);
12330
12331 return S_OK;
12332}
12333
12334/**
12335 * @note Locks mParent + this object for writing.
12336 */
12337STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12338{
12339 LogFlowThisFunc(("\n"));
12340
12341 AutoCaller autoCaller(this);
12342 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12343
12344 /* endSavingState() need mParent lock */
12345 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12346
12347 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12348 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12349 && mConsoleTaskData.mLastState != MachineState_Null
12350 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12351 E_FAIL);
12352
12353 /*
12354 * On failure, set the state to the state we had when BeginSavingState()
12355 * was called (this is expected by Console::SaveState() and the associated
12356 * task). On success the VM process already changed the state to
12357 * MachineState_Saved, so no need to do anything.
12358 */
12359 if (FAILED(iResult))
12360 setMachineState(mConsoleTaskData.mLastState);
12361
12362 return endSavingState(iResult, aErrMsg);
12363}
12364
12365/**
12366 * @note Locks this object for writing.
12367 */
12368STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12369{
12370 LogFlowThisFunc(("\n"));
12371
12372 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12373
12374 AutoCaller autoCaller(this);
12375 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12376
12377 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12378
12379 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12380 || mData->mMachineState == MachineState_Teleported
12381 || mData->mMachineState == MachineState_Aborted
12382 , E_FAIL); /** @todo setError. */
12383
12384 Utf8Str stateFilePathFull = aSavedStateFile;
12385 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
12386 if (RT_FAILURE(vrc))
12387 return setError(VBOX_E_FILE_ERROR,
12388 tr("Invalid saved state file path '%ls' (%Rrc)"),
12389 aSavedStateFile,
12390 vrc);
12391
12392 mSSData->strStateFilePath = stateFilePathFull;
12393
12394 /* The below setMachineState() will detect the state transition and will
12395 * update the settings file */
12396
12397 return setMachineState(MachineState_Saved);
12398}
12399
12400STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
12401 ComSafeArrayOut(BSTR, aValues),
12402 ComSafeArrayOut(LONG64, aTimestamps),
12403 ComSafeArrayOut(BSTR, aFlags))
12404{
12405 LogFlowThisFunc(("\n"));
12406
12407#ifdef VBOX_WITH_GUEST_PROPS
12408 using namespace guestProp;
12409
12410 AutoCaller autoCaller(this);
12411 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12412
12413 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12414
12415 CheckComArgOutSafeArrayPointerValid(aNames);
12416 CheckComArgOutSafeArrayPointerValid(aValues);
12417 CheckComArgOutSafeArrayPointerValid(aTimestamps);
12418 CheckComArgOutSafeArrayPointerValid(aFlags);
12419
12420 size_t cEntries = mHWData->mGuestProperties.size();
12421 com::SafeArray<BSTR> names(cEntries);
12422 com::SafeArray<BSTR> values(cEntries);
12423 com::SafeArray<LONG64> timestamps(cEntries);
12424 com::SafeArray<BSTR> flags(cEntries);
12425 unsigned i = 0;
12426 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
12427 it != mHWData->mGuestProperties.end();
12428 ++it)
12429 {
12430 char szFlags[MAX_FLAGS_LEN + 1];
12431 it->strName.cloneTo(&names[i]);
12432 it->strValue.cloneTo(&values[i]);
12433 timestamps[i] = it->mTimestamp;
12434 /* If it is NULL, keep it NULL. */
12435 if (it->mFlags)
12436 {
12437 writeFlags(it->mFlags, szFlags);
12438 Bstr(szFlags).cloneTo(&flags[i]);
12439 }
12440 else
12441 flags[i] = NULL;
12442 ++i;
12443 }
12444 names.detachTo(ComSafeArrayOutArg(aNames));
12445 values.detachTo(ComSafeArrayOutArg(aValues));
12446 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
12447 flags.detachTo(ComSafeArrayOutArg(aFlags));
12448 return S_OK;
12449#else
12450 ReturnComNotImplemented();
12451#endif
12452}
12453
12454STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
12455 IN_BSTR aValue,
12456 LONG64 aTimestamp,
12457 IN_BSTR aFlags)
12458{
12459 LogFlowThisFunc(("\n"));
12460
12461#ifdef VBOX_WITH_GUEST_PROPS
12462 using namespace guestProp;
12463
12464 CheckComArgStrNotEmptyOrNull(aName);
12465 CheckComArgNotNull(aValue);
12466 CheckComArgNotNull(aFlags);
12467
12468 try
12469 {
12470 /*
12471 * Convert input up front.
12472 */
12473 Utf8Str utf8Name(aName);
12474 uint32_t fFlags = NILFLAG;
12475 if (aFlags)
12476 {
12477 Utf8Str utf8Flags(aFlags);
12478 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
12479 AssertRCReturn(vrc, E_INVALIDARG);
12480 }
12481
12482 /*
12483 * Now grab the object lock, validate the state and do the update.
12484 */
12485 AutoCaller autoCaller(this);
12486 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12487
12488 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12489
12490 switch (mData->mMachineState)
12491 {
12492 case MachineState_Paused:
12493 case MachineState_Running:
12494 case MachineState_Teleporting:
12495 case MachineState_TeleportingPausedVM:
12496 case MachineState_LiveSnapshotting:
12497 case MachineState_DeletingSnapshotOnline:
12498 case MachineState_DeletingSnapshotPaused:
12499 case MachineState_Saving:
12500 break;
12501
12502 default:
12503#ifndef DEBUG_sunlover
12504 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
12505 VBOX_E_INVALID_VM_STATE);
12506#else
12507 return VBOX_E_INVALID_VM_STATE;
12508#endif
12509 }
12510
12511 setModified(IsModified_MachineData);
12512 mHWData.backup();
12513
12514 /** @todo r=bird: The careful memory handling doesn't work out here because
12515 * the catch block won't undo any damage we've done. So, if push_back throws
12516 * bad_alloc then you've lost the value.
12517 *
12518 * Another thing. Doing a linear search here isn't extremely efficient, esp.
12519 * since values that changes actually bubbles to the end of the list. Using
12520 * something that has an efficient lookup and can tolerate a bit of updates
12521 * would be nice. RTStrSpace is one suggestion (it's not perfect). Some
12522 * combination of RTStrCache (for sharing names and getting uniqueness into
12523 * the bargain) and hash/tree is another. */
12524 for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();
12525 iter != mHWData->mGuestProperties.end();
12526 ++iter)
12527 if (utf8Name == iter->strName)
12528 {
12529 mHWData->mGuestProperties.erase(iter);
12530 mData->mGuestPropertiesModified = TRUE;
12531 break;
12532 }
12533 if (aValue != NULL)
12534 {
12535 HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };
12536 mHWData->mGuestProperties.push_back(property);
12537 mData->mGuestPropertiesModified = TRUE;
12538 }
12539
12540 /*
12541 * Send a callback notification if appropriate
12542 */
12543 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
12544 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
12545 RTSTR_MAX,
12546 utf8Name.c_str(),
12547 RTSTR_MAX, NULL)
12548 )
12549 {
12550 alock.release();
12551
12552 mParent->onGuestPropertyChange(mData->mUuid,
12553 aName,
12554 aValue,
12555 aFlags);
12556 }
12557 }
12558 catch (...)
12559 {
12560 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
12561 }
12562 return S_OK;
12563#else
12564 ReturnComNotImplemented();
12565#endif
12566}
12567
12568STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
12569 IMediumAttachment **aNewAttachment)
12570{
12571 CheckComArgNotNull(aAttachment);
12572 CheckComArgOutPointerValid(aNewAttachment);
12573
12574 AutoCaller autoCaller(this);
12575 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12576
12577 // request the host lock first, since might be calling Host methods for getting host drives;
12578 // next, protect the media tree all the while we're in here, as well as our member variables
12579 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
12580 this->lockHandle(),
12581 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
12582
12583 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
12584
12585 Bstr ctrlName;
12586 LONG lPort;
12587 LONG lDevice;
12588 bool fTempEject;
12589 {
12590 AutoCaller autoAttachCaller(this);
12591 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12592
12593 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12594
12595 /* Need to query the details first, as the IMediumAttachment reference
12596 * might be to the original settings, which we are going to change. */
12597 ctrlName = pAttach->getControllerName();
12598 lPort = pAttach->getPort();
12599 lDevice = pAttach->getDevice();
12600 fTempEject = pAttach->getTempEject();
12601 }
12602
12603 if (!fTempEject)
12604 {
12605 /* Remember previously mounted medium. The medium before taking the
12606 * backup is not necessarily the same thing. */
12607 ComObjPtr<Medium> oldmedium;
12608 oldmedium = pAttach->getMedium();
12609
12610 setModified(IsModified_Storage);
12611 mMediaData.backup();
12612
12613 // The backup operation makes the pAttach reference point to the
12614 // old settings. Re-get the correct reference.
12615 pAttach = findAttachment(mMediaData->mAttachments,
12616 ctrlName.raw(),
12617 lPort,
12618 lDevice);
12619
12620 {
12621 AutoCaller autoAttachCaller(this);
12622 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12623
12624 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12625 if (!oldmedium.isNull())
12626 oldmedium->removeBackReference(mData->mUuid);
12627
12628 pAttach->updateMedium(NULL);
12629 pAttach->updateEjected();
12630 }
12631
12632 setModified(IsModified_Storage);
12633 }
12634 else
12635 {
12636 {
12637 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12638 pAttach->updateEjected();
12639 }
12640 }
12641
12642 pAttach.queryInterfaceTo(aNewAttachment);
12643
12644 return S_OK;
12645}
12646
12647// public methods only for internal purposes
12648/////////////////////////////////////////////////////////////////////////////
12649
12650/**
12651 * Called from the client watcher thread to check for expected or unexpected
12652 * death of the client process that has a direct session to this machine.
12653 *
12654 * On Win32 and on OS/2, this method is called only when we've got the
12655 * mutex (i.e. the client has either died or terminated normally) so it always
12656 * returns @c true (the client is terminated, the session machine is
12657 * uninitialized).
12658 *
12659 * On other platforms, the method returns @c true if the client process has
12660 * terminated normally or abnormally and the session machine was uninitialized,
12661 * and @c false if the client process is still alive.
12662 *
12663 * @note Locks this object for writing.
12664 */
12665bool SessionMachine::checkForDeath()
12666{
12667 Uninit::Reason reason;
12668 bool terminated = false;
12669
12670 /* Enclose autoCaller with a block because calling uninit() from under it
12671 * will deadlock. */
12672 {
12673 AutoCaller autoCaller(this);
12674 if (!autoCaller.isOk())
12675 {
12676 /* return true if not ready, to cause the client watcher to exclude
12677 * the corresponding session from watching */
12678 LogFlowThisFunc(("Already uninitialized!\n"));
12679 return true;
12680 }
12681
12682 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12683
12684 /* Determine the reason of death: if the session state is Closing here,
12685 * everything is fine. Otherwise it means that the client did not call
12686 * OnSessionEnd() before it released the IPC semaphore. This may happen
12687 * either because the client process has abnormally terminated, or
12688 * because it simply forgot to call ISession::Close() before exiting. We
12689 * threat the latter also as an abnormal termination (see
12690 * Session::uninit() for details). */
12691 reason = mData->mSession.mState == SessionState_Unlocking ?
12692 Uninit::Normal :
12693 Uninit::Abnormal;
12694
12695#if defined(RT_OS_WINDOWS)
12696
12697 AssertMsg(mIPCSem, ("semaphore must be created"));
12698
12699 /* release the IPC mutex */
12700 ::ReleaseMutex(mIPCSem);
12701
12702 terminated = true;
12703
12704#elif defined(RT_OS_OS2)
12705
12706 AssertMsg(mIPCSem, ("semaphore must be created"));
12707
12708 /* release the IPC mutex */
12709 ::DosReleaseMutexSem(mIPCSem);
12710
12711 terminated = true;
12712
12713#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12714
12715 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
12716
12717 int val = ::semctl(mIPCSem, 0, GETVAL);
12718 if (val > 0)
12719 {
12720 /* the semaphore is signaled, meaning the session is terminated */
12721 terminated = true;
12722 }
12723
12724#else
12725# error "Port me!"
12726#endif
12727
12728 } /* AutoCaller block */
12729
12730 if (terminated)
12731 uninit(reason);
12732
12733 return terminated;
12734}
12735
12736/**
12737 * @note Locks this object for reading.
12738 */
12739HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
12740{
12741 LogFlowThisFunc(("\n"));
12742
12743 AutoCaller autoCaller(this);
12744 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12745
12746 ComPtr<IInternalSessionControl> directControl;
12747 {
12748 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12749 directControl = mData->mSession.mDirectControl;
12750 }
12751
12752 /* ignore notifications sent after #OnSessionEnd() is called */
12753 if (!directControl)
12754 return S_OK;
12755
12756 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
12757}
12758
12759/**
12760 * @note Locks this object for reading.
12761 */
12762HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
12763 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
12764{
12765 LogFlowThisFunc(("\n"));
12766
12767 AutoCaller autoCaller(this);
12768 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12769
12770 ComPtr<IInternalSessionControl> directControl;
12771 {
12772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12773 directControl = mData->mSession.mDirectControl;
12774 }
12775
12776 /* ignore notifications sent after #OnSessionEnd() is called */
12777 if (!directControl)
12778 return S_OK;
12779 /*
12780 * instead acting like callback we ask IVirtualBox deliver corresponding event
12781 */
12782
12783 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
12784 return S_OK;
12785}
12786
12787/**
12788 * @note Locks this object for reading.
12789 */
12790HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
12791{
12792 LogFlowThisFunc(("\n"));
12793
12794 AutoCaller autoCaller(this);
12795 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12796
12797 ComPtr<IInternalSessionControl> directControl;
12798 {
12799 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12800 directControl = mData->mSession.mDirectControl;
12801 }
12802
12803 /* ignore notifications sent after #OnSessionEnd() is called */
12804 if (!directControl)
12805 return S_OK;
12806
12807 return directControl->OnSerialPortChange(serialPort);
12808}
12809
12810/**
12811 * @note Locks this object for reading.
12812 */
12813HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
12814{
12815 LogFlowThisFunc(("\n"));
12816
12817 AutoCaller autoCaller(this);
12818 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12819
12820 ComPtr<IInternalSessionControl> directControl;
12821 {
12822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12823 directControl = mData->mSession.mDirectControl;
12824 }
12825
12826 /* ignore notifications sent after #OnSessionEnd() is called */
12827 if (!directControl)
12828 return S_OK;
12829
12830 return directControl->OnParallelPortChange(parallelPort);
12831}
12832
12833/**
12834 * @note Locks this object for reading.
12835 */
12836HRESULT SessionMachine::onStorageControllerChange()
12837{
12838 LogFlowThisFunc(("\n"));
12839
12840 AutoCaller autoCaller(this);
12841 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12842
12843 ComPtr<IInternalSessionControl> directControl;
12844 {
12845 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12846 directControl = mData->mSession.mDirectControl;
12847 }
12848
12849 /* ignore notifications sent after #OnSessionEnd() is called */
12850 if (!directControl)
12851 return S_OK;
12852
12853 return directControl->OnStorageControllerChange();
12854}
12855
12856/**
12857 * @note Locks this object for reading.
12858 */
12859HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
12860{
12861 LogFlowThisFunc(("\n"));
12862
12863 AutoCaller autoCaller(this);
12864 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12865
12866 ComPtr<IInternalSessionControl> directControl;
12867 {
12868 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12869 directControl = mData->mSession.mDirectControl;
12870 }
12871
12872 /* ignore notifications sent after #OnSessionEnd() is called */
12873 if (!directControl)
12874 return S_OK;
12875
12876 return directControl->OnMediumChange(aAttachment, aForce);
12877}
12878
12879/**
12880 * @note Locks this object for reading.
12881 */
12882HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
12883{
12884 LogFlowThisFunc(("\n"));
12885
12886 AutoCaller autoCaller(this);
12887 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
12888
12889 ComPtr<IInternalSessionControl> directControl;
12890 {
12891 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12892 directControl = mData->mSession.mDirectControl;
12893 }
12894
12895 /* ignore notifications sent after #OnSessionEnd() is called */
12896 if (!directControl)
12897 return S_OK;
12898
12899 return directControl->OnCPUChange(aCPU, aRemove);
12900}
12901
12902HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
12903{
12904 LogFlowThisFunc(("\n"));
12905
12906 AutoCaller autoCaller(this);
12907 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
12908
12909 ComPtr<IInternalSessionControl> directControl;
12910 {
12911 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12912 directControl = mData->mSession.mDirectControl;
12913 }
12914
12915 /* ignore notifications sent after #OnSessionEnd() is called */
12916 if (!directControl)
12917 return S_OK;
12918
12919 return directControl->OnCPUExecutionCapChange(aExecutionCap);
12920}
12921
12922/**
12923 * @note Locks this object for reading.
12924 */
12925HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
12926{
12927 LogFlowThisFunc(("\n"));
12928
12929 AutoCaller autoCaller(this);
12930 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12931
12932 ComPtr<IInternalSessionControl> directControl;
12933 {
12934 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12935 directControl = mData->mSession.mDirectControl;
12936 }
12937
12938 /* ignore notifications sent after #OnSessionEnd() is called */
12939 if (!directControl)
12940 return S_OK;
12941
12942 return directControl->OnVRDEServerChange(aRestart);
12943}
12944
12945/**
12946 * @note Locks this object for reading.
12947 */
12948HRESULT SessionMachine::onUSBControllerChange()
12949{
12950 LogFlowThisFunc(("\n"));
12951
12952 AutoCaller autoCaller(this);
12953 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12954
12955 ComPtr<IInternalSessionControl> directControl;
12956 {
12957 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12958 directControl = mData->mSession.mDirectControl;
12959 }
12960
12961 /* ignore notifications sent after #OnSessionEnd() is called */
12962 if (!directControl)
12963 return S_OK;
12964
12965 return directControl->OnUSBControllerChange();
12966}
12967
12968/**
12969 * @note Locks this object for reading.
12970 */
12971HRESULT SessionMachine::onSharedFolderChange()
12972{
12973 LogFlowThisFunc(("\n"));
12974
12975 AutoCaller autoCaller(this);
12976 AssertComRCReturnRC(autoCaller.rc());
12977
12978 ComPtr<IInternalSessionControl> directControl;
12979 {
12980 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12981 directControl = mData->mSession.mDirectControl;
12982 }
12983
12984 /* ignore notifications sent after #OnSessionEnd() is called */
12985 if (!directControl)
12986 return S_OK;
12987
12988 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
12989}
12990
12991/**
12992 * @note Locks this object for reading.
12993 */
12994HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
12995{
12996 LogFlowThisFunc(("\n"));
12997
12998 AutoCaller autoCaller(this);
12999 AssertComRCReturnRC(autoCaller.rc());
13000
13001 ComPtr<IInternalSessionControl> directControl;
13002 {
13003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13004 directControl = mData->mSession.mDirectControl;
13005 }
13006
13007 /* ignore notifications sent after #OnSessionEnd() is called */
13008 if (!directControl)
13009 return S_OK;
13010
13011 return directControl->OnClipboardModeChange(aClipboardMode);
13012}
13013
13014/**
13015 * @note Locks this object for reading.
13016 */
13017HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
13018{
13019 LogFlowThisFunc(("\n"));
13020
13021 AutoCaller autoCaller(this);
13022 AssertComRCReturnRC(autoCaller.rc());
13023
13024 ComPtr<IInternalSessionControl> directControl;
13025 {
13026 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13027 directControl = mData->mSession.mDirectControl;
13028 }
13029
13030 /* ignore notifications sent after #OnSessionEnd() is called */
13031 if (!directControl)
13032 return S_OK;
13033
13034 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
13035}
13036
13037/**
13038 * @note Locks this object for reading.
13039 */
13040HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13041{
13042 LogFlowThisFunc(("\n"));
13043
13044 AutoCaller autoCaller(this);
13045 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
13046
13047 ComPtr<IInternalSessionControl> directControl;
13048 {
13049 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13050 directControl = mData->mSession.mDirectControl;
13051 }
13052
13053 /* ignore notifications sent after #OnSessionEnd() is called */
13054 if (!directControl)
13055 return S_OK;
13056
13057 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13058}
13059
13060/**
13061 * @note Locks this object for reading.
13062 */
13063HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove)
13064{
13065 LogFlowThisFunc(("\n"));
13066
13067 AutoCaller autoCaller(this);
13068 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13069
13070 ComPtr<IInternalSessionControl> directControl;
13071 {
13072 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13073 directControl = mData->mSession.mDirectControl;
13074 }
13075
13076 /* ignore notifications sent after #OnSessionEnd() is called */
13077 if (!directControl)
13078 return S_OK;
13079
13080 return directControl->OnStorageDeviceChange(aAttachment, aRemove);
13081}
13082
13083/**
13084 * Returns @c true if this machine's USB controller reports it has a matching
13085 * filter for the given USB device and @c false otherwise.
13086 *
13087 * @note locks this object for reading.
13088 */
13089bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13090{
13091 AutoCaller autoCaller(this);
13092 /* silently return if not ready -- this method may be called after the
13093 * direct machine session has been called */
13094 if (!autoCaller.isOk())
13095 return false;
13096
13097#ifdef VBOX_WITH_USB
13098 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13099
13100 switch (mData->mMachineState)
13101 {
13102 case MachineState_Starting:
13103 case MachineState_Restoring:
13104 case MachineState_TeleportingIn:
13105 case MachineState_Paused:
13106 case MachineState_Running:
13107 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13108 * elsewhere... */
13109 alock.release();
13110 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
13111 default: break;
13112 }
13113#else
13114 NOREF(aDevice);
13115 NOREF(aMaskedIfs);
13116#endif
13117 return false;
13118}
13119
13120/**
13121 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13122 */
13123HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
13124 IVirtualBoxErrorInfo *aError,
13125 ULONG aMaskedIfs)
13126{
13127 LogFlowThisFunc(("\n"));
13128
13129 AutoCaller autoCaller(this);
13130
13131 /* This notification may happen after the machine object has been
13132 * uninitialized (the session was closed), so don't assert. */
13133 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13134
13135 ComPtr<IInternalSessionControl> directControl;
13136 {
13137 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13138 directControl = mData->mSession.mDirectControl;
13139 }
13140
13141 /* fail on notifications sent after #OnSessionEnd() is called, it is
13142 * expected by the caller */
13143 if (!directControl)
13144 return E_FAIL;
13145
13146 /* No locks should be held at this point. */
13147 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13148 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13149
13150 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13151}
13152
13153/**
13154 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13155 */
13156HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
13157 IVirtualBoxErrorInfo *aError)
13158{
13159 LogFlowThisFunc(("\n"));
13160
13161 AutoCaller autoCaller(this);
13162
13163 /* This notification may happen after the machine object has been
13164 * uninitialized (the session was closed), so don't assert. */
13165 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13166
13167 ComPtr<IInternalSessionControl> directControl;
13168 {
13169 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13170 directControl = mData->mSession.mDirectControl;
13171 }
13172
13173 /* fail on notifications sent after #OnSessionEnd() is called, it is
13174 * expected by the caller */
13175 if (!directControl)
13176 return E_FAIL;
13177
13178 /* No locks should be held at this point. */
13179 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13180 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13181
13182 return directControl->OnUSBDeviceDetach(aId, aError);
13183}
13184
13185// protected methods
13186/////////////////////////////////////////////////////////////////////////////
13187
13188/**
13189 * Helper method to finalize saving the state.
13190 *
13191 * @note Must be called from under this object's lock.
13192 *
13193 * @param aRc S_OK if the snapshot has been taken successfully
13194 * @param aErrMsg human readable error message for failure
13195 *
13196 * @note Locks mParent + this objects for writing.
13197 */
13198HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13199{
13200 LogFlowThisFuncEnter();
13201
13202 AutoCaller autoCaller(this);
13203 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13204
13205 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13206
13207 HRESULT rc = S_OK;
13208
13209 if (SUCCEEDED(aRc))
13210 {
13211 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13212
13213 /* save all VM settings */
13214 rc = saveSettings(NULL);
13215 // no need to check whether VirtualBox.xml needs saving also since
13216 // we can't have a name change pending at this point
13217 }
13218 else
13219 {
13220 // delete the saved state file (it might have been already created);
13221 // we need not check whether this is shared with a snapshot here because
13222 // we certainly created this saved state file here anew
13223 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13224 }
13225
13226 /* notify the progress object about operation completion */
13227 Assert(mConsoleTaskData.mProgress);
13228 if (SUCCEEDED(aRc))
13229 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13230 else
13231 {
13232 if (aErrMsg.length())
13233 mConsoleTaskData.mProgress->notifyComplete(aRc,
13234 COM_IIDOF(ISession),
13235 getComponentName(),
13236 aErrMsg.c_str());
13237 else
13238 mConsoleTaskData.mProgress->notifyComplete(aRc);
13239 }
13240
13241 /* clear out the temporary saved state data */
13242 mConsoleTaskData.mLastState = MachineState_Null;
13243 mConsoleTaskData.strStateFilePath.setNull();
13244 mConsoleTaskData.mProgress.setNull();
13245
13246 LogFlowThisFuncLeave();
13247 return rc;
13248}
13249
13250/**
13251 * Deletes the given file if it is no longer in use by either the current machine state
13252 * (if the machine is "saved") or any of the machine's snapshots.
13253 *
13254 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13255 * but is different for each SnapshotMachine. When calling this, the order of calling this
13256 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13257 * is therefore critical. I know, it's all rather messy.
13258 *
13259 * @param strStateFile
13260 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
13261 */
13262void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
13263 Snapshot *pSnapshotToIgnore)
13264{
13265 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13266 if ( (strStateFile.isNotEmpty())
13267 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13268 )
13269 // ... and it must also not be shared with other snapshots
13270 if ( !mData->mFirstSnapshot
13271 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13272 // this checks the SnapshotMachine's state file paths
13273 )
13274 RTFileDelete(strStateFile.c_str());
13275}
13276
13277/**
13278 * Locks the attached media.
13279 *
13280 * All attached hard disks are locked for writing and DVD/floppy are locked for
13281 * reading. Parents of attached hard disks (if any) are locked for reading.
13282 *
13283 * This method also performs accessibility check of all media it locks: if some
13284 * media is inaccessible, the method will return a failure and a bunch of
13285 * extended error info objects per each inaccessible medium.
13286 *
13287 * Note that this method is atomic: if it returns a success, all media are
13288 * locked as described above; on failure no media is locked at all (all
13289 * succeeded individual locks will be undone).
13290 *
13291 * This method is intended to be called when the machine is in Starting or
13292 * Restoring state and asserts otherwise.
13293 *
13294 * The locks made by this method must be undone by calling #unlockMedia() when
13295 * no more needed.
13296 */
13297HRESULT SessionMachine::lockMedia()
13298{
13299 AutoCaller autoCaller(this);
13300 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13301
13302 AutoMultiWriteLock2 alock(this->lockHandle(),
13303 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13304
13305 AssertReturn( mData->mMachineState == MachineState_Starting
13306 || mData->mMachineState == MachineState_Restoring
13307 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13308 /* bail out if trying to lock things with already set up locking */
13309 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13310
13311 clearError();
13312 MultiResult mrc(S_OK);
13313
13314 /* Collect locking information for all medium objects attached to the VM. */
13315 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13316 it != mMediaData->mAttachments.end();
13317 ++it)
13318 {
13319 MediumAttachment* pAtt = *it;
13320 DeviceType_T devType = pAtt->getType();
13321 Medium *pMedium = pAtt->getMedium();
13322
13323 MediumLockList *pMediumLockList(new MediumLockList());
13324 // There can be attachments without a medium (floppy/dvd), and thus
13325 // it's impossible to create a medium lock list. It still makes sense
13326 // to have the empty medium lock list in the map in case a medium is
13327 // attached later.
13328 if (pMedium != NULL)
13329 {
13330 MediumType_T mediumType = pMedium->getType();
13331 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13332 || mediumType == MediumType_Shareable;
13333 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13334
13335 alock.release();
13336 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13337 !fIsReadOnlyLock /* fMediumLockWrite */,
13338 NULL,
13339 *pMediumLockList);
13340 alock.acquire();
13341 if (FAILED(mrc))
13342 {
13343 delete pMediumLockList;
13344 mData->mSession.mLockedMedia.Clear();
13345 break;
13346 }
13347 }
13348
13349 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13350 if (FAILED(rc))
13351 {
13352 mData->mSession.mLockedMedia.Clear();
13353 mrc = setError(rc,
13354 tr("Collecting locking information for all attached media failed"));
13355 break;
13356 }
13357 }
13358
13359 if (SUCCEEDED(mrc))
13360 {
13361 /* Now lock all media. If this fails, nothing is locked. */
13362 alock.release();
13363 HRESULT rc = mData->mSession.mLockedMedia.Lock();
13364 alock.acquire();
13365 if (FAILED(rc))
13366 {
13367 mrc = setError(rc,
13368 tr("Locking of attached media failed"));
13369 }
13370 }
13371
13372 return mrc;
13373}
13374
13375/**
13376 * Undoes the locks made by by #lockMedia().
13377 */
13378void SessionMachine::unlockMedia()
13379{
13380 AutoCaller autoCaller(this);
13381 AssertComRCReturnVoid(autoCaller.rc());
13382
13383 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13384
13385 /* we may be holding important error info on the current thread;
13386 * preserve it */
13387 ErrorInfoKeeper eik;
13388
13389 HRESULT rc = mData->mSession.mLockedMedia.Clear();
13390 AssertComRC(rc);
13391}
13392
13393/**
13394 * Helper to change the machine state (reimplementation).
13395 *
13396 * @note Locks this object for writing.
13397 * @note This method must not call saveSettings or SaveSettings, otherwise
13398 * it can cause crashes in random places due to unexpectedly committing
13399 * the current settings. The caller is responsible for that. The call
13400 * to saveStateSettings is fine, because this method does not commit.
13401 */
13402HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
13403{
13404 LogFlowThisFuncEnter();
13405 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
13406
13407 AutoCaller autoCaller(this);
13408 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13409
13410 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13411
13412 MachineState_T oldMachineState = mData->mMachineState;
13413
13414 AssertMsgReturn(oldMachineState != aMachineState,
13415 ("oldMachineState=%s, aMachineState=%s\n",
13416 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
13417 E_FAIL);
13418
13419 HRESULT rc = S_OK;
13420
13421 int stsFlags = 0;
13422 bool deleteSavedState = false;
13423
13424 /* detect some state transitions */
13425
13426 if ( ( oldMachineState == MachineState_Saved
13427 && aMachineState == MachineState_Restoring)
13428 || ( ( oldMachineState == MachineState_PoweredOff
13429 || oldMachineState == MachineState_Teleported
13430 || oldMachineState == MachineState_Aborted
13431 )
13432 && ( aMachineState == MachineState_TeleportingIn
13433 || aMachineState == MachineState_Starting
13434 )
13435 )
13436 )
13437 {
13438 /* The EMT thread is about to start */
13439
13440 /* Nothing to do here for now... */
13441
13442 /// @todo NEWMEDIA don't let mDVDDrive and other children
13443 /// change anything when in the Starting/Restoring state
13444 }
13445 else if ( ( oldMachineState == MachineState_Running
13446 || oldMachineState == MachineState_Paused
13447 || oldMachineState == MachineState_Teleporting
13448 || oldMachineState == MachineState_LiveSnapshotting
13449 || oldMachineState == MachineState_Stuck
13450 || oldMachineState == MachineState_Starting
13451 || oldMachineState == MachineState_Stopping
13452 || oldMachineState == MachineState_Saving
13453 || oldMachineState == MachineState_Restoring
13454 || oldMachineState == MachineState_TeleportingPausedVM
13455 || oldMachineState == MachineState_TeleportingIn
13456 )
13457 && ( aMachineState == MachineState_PoweredOff
13458 || aMachineState == MachineState_Saved
13459 || aMachineState == MachineState_Teleported
13460 || aMachineState == MachineState_Aborted
13461 )
13462 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
13463 * snapshot */
13464 && ( mConsoleTaskData.mSnapshot.isNull()
13465 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
13466 )
13467 )
13468 {
13469 /* The EMT thread has just stopped, unlock attached media. Note that as
13470 * opposed to locking that is done from Console, we do unlocking here
13471 * because the VM process may have aborted before having a chance to
13472 * properly unlock all media it locked. */
13473
13474 unlockMedia();
13475 }
13476
13477 if (oldMachineState == MachineState_Restoring)
13478 {
13479 if (aMachineState != MachineState_Saved)
13480 {
13481 /*
13482 * delete the saved state file once the machine has finished
13483 * restoring from it (note that Console sets the state from
13484 * Restoring to Saved if the VM couldn't restore successfully,
13485 * to give the user an ability to fix an error and retry --
13486 * we keep the saved state file in this case)
13487 */
13488 deleteSavedState = true;
13489 }
13490 }
13491 else if ( oldMachineState == MachineState_Saved
13492 && ( aMachineState == MachineState_PoweredOff
13493 || aMachineState == MachineState_Aborted
13494 || aMachineState == MachineState_Teleported
13495 )
13496 )
13497 {
13498 /*
13499 * delete the saved state after Console::ForgetSavedState() is called
13500 * or if the VM process (owning a direct VM session) crashed while the
13501 * VM was Saved
13502 */
13503
13504 /// @todo (dmik)
13505 // Not sure that deleting the saved state file just because of the
13506 // client death before it attempted to restore the VM is a good
13507 // thing. But when it crashes we need to go to the Aborted state
13508 // which cannot have the saved state file associated... The only
13509 // way to fix this is to make the Aborted condition not a VM state
13510 // but a bool flag: i.e., when a crash occurs, set it to true and
13511 // change the state to PoweredOff or Saved depending on the
13512 // saved state presence.
13513
13514 deleteSavedState = true;
13515 mData->mCurrentStateModified = TRUE;
13516 stsFlags |= SaveSTS_CurStateModified;
13517 }
13518
13519 if ( aMachineState == MachineState_Starting
13520 || aMachineState == MachineState_Restoring
13521 || aMachineState == MachineState_TeleportingIn
13522 )
13523 {
13524 /* set the current state modified flag to indicate that the current
13525 * state is no more identical to the state in the
13526 * current snapshot */
13527 if (!mData->mCurrentSnapshot.isNull())
13528 {
13529 mData->mCurrentStateModified = TRUE;
13530 stsFlags |= SaveSTS_CurStateModified;
13531 }
13532 }
13533
13534 if (deleteSavedState)
13535 {
13536 if (mRemoveSavedState)
13537 {
13538 Assert(!mSSData->strStateFilePath.isEmpty());
13539
13540 // it is safe to delete the saved state file if ...
13541 if ( !mData->mFirstSnapshot // ... we have no snapshots or
13542 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
13543 // ... none of the snapshots share the saved state file
13544 )
13545 RTFileDelete(mSSData->strStateFilePath.c_str());
13546 }
13547
13548 mSSData->strStateFilePath.setNull();
13549 stsFlags |= SaveSTS_StateFilePath;
13550 }
13551
13552 /* redirect to the underlying peer machine */
13553 mPeer->setMachineState(aMachineState);
13554
13555 if ( aMachineState == MachineState_PoweredOff
13556 || aMachineState == MachineState_Teleported
13557 || aMachineState == MachineState_Aborted
13558 || aMachineState == MachineState_Saved)
13559 {
13560 /* the machine has stopped execution
13561 * (or the saved state file was adopted) */
13562 stsFlags |= SaveSTS_StateTimeStamp;
13563 }
13564
13565 if ( ( oldMachineState == MachineState_PoweredOff
13566 || oldMachineState == MachineState_Aborted
13567 || oldMachineState == MachineState_Teleported
13568 )
13569 && aMachineState == MachineState_Saved)
13570 {
13571 /* the saved state file was adopted */
13572 Assert(!mSSData->strStateFilePath.isEmpty());
13573 stsFlags |= SaveSTS_StateFilePath;
13574 }
13575
13576#ifdef VBOX_WITH_GUEST_PROPS
13577 if ( aMachineState == MachineState_PoweredOff
13578 || aMachineState == MachineState_Aborted
13579 || aMachineState == MachineState_Teleported)
13580 {
13581 /* Make sure any transient guest properties get removed from the
13582 * property store on shutdown. */
13583
13584 HWData::GuestPropertyList::iterator it;
13585 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
13586 if (!fNeedsSaving)
13587 for (it = mHWData->mGuestProperties.begin();
13588 it != mHWData->mGuestProperties.end(); ++it)
13589 if ( (it->mFlags & guestProp::TRANSIENT)
13590 || (it->mFlags & guestProp::TRANSRESET))
13591 {
13592 fNeedsSaving = true;
13593 break;
13594 }
13595 if (fNeedsSaving)
13596 {
13597 mData->mCurrentStateModified = TRUE;
13598 stsFlags |= SaveSTS_CurStateModified;
13599 }
13600 }
13601#endif
13602
13603 rc = saveStateSettings(stsFlags);
13604
13605 if ( ( oldMachineState != MachineState_PoweredOff
13606 && oldMachineState != MachineState_Aborted
13607 && oldMachineState != MachineState_Teleported
13608 )
13609 && ( aMachineState == MachineState_PoweredOff
13610 || aMachineState == MachineState_Aborted
13611 || aMachineState == MachineState_Teleported
13612 )
13613 )
13614 {
13615 /* we've been shut down for any reason */
13616 /* no special action so far */
13617 }
13618
13619 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
13620 LogFlowThisFuncLeave();
13621 return rc;
13622}
13623
13624/**
13625 * Sends the current machine state value to the VM process.
13626 *
13627 * @note Locks this object for reading, then calls a client process.
13628 */
13629HRESULT SessionMachine::updateMachineStateOnClient()
13630{
13631 AutoCaller autoCaller(this);
13632 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13633
13634 ComPtr<IInternalSessionControl> directControl;
13635 {
13636 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13637 AssertReturn(!!mData, E_FAIL);
13638 directControl = mData->mSession.mDirectControl;
13639
13640 /* directControl may be already set to NULL here in #OnSessionEnd()
13641 * called too early by the direct session process while there is still
13642 * some operation (like deleting the snapshot) in progress. The client
13643 * process in this case is waiting inside Session::close() for the
13644 * "end session" process object to complete, while #uninit() called by
13645 * #checkForDeath() on the Watcher thread is waiting for the pending
13646 * operation to complete. For now, we accept this inconsistent behavior
13647 * and simply do nothing here. */
13648
13649 if (mData->mSession.mState == SessionState_Unlocking)
13650 return S_OK;
13651
13652 AssertReturn(!directControl.isNull(), E_FAIL);
13653 }
13654
13655 return directControl->UpdateMachineState(mData->mMachineState);
13656}
Note: See TracBrowser for help on using the repository browser.

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