VirtualBox

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

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

Main/Machine+NetworkAdapter: properly fix resizing of vector containing network adapters, previously it wasn't handling commit correctly. xtracker 5997

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 458.6 KB
Line 
1/* $Id: MachineImpl.cpp 42825 2012-08-15 13:59:01Z 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 // make a temporary list because detachDevice invalidates iterators into
5948 // mMediaData->mAttachments
5949 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
5950
5951 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
5952 it != llAttachments2.end();
5953 ++it)
5954 {
5955 MediumAttachment *pAttachTemp = *it;
5956
5957 AutoCaller localAutoCaller(pAttachTemp);
5958 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
5959
5960 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
5961
5962 if (pAttachTemp->getControllerName() == aName)
5963 {
5964 rc = detachDevice(pAttachTemp, alock, NULL);
5965 if (FAILED(rc)) return rc;
5966 }
5967 }
5968 }
5969
5970 /* We can remove it now. */
5971 setModified(IsModified_Storage);
5972 mStorageControllers.backup();
5973
5974 ctrl->unshare();
5975
5976 mStorageControllers->remove(ctrl);
5977
5978 /* inform the direct session if any */
5979 alock.release();
5980 onStorageControllerChange();
5981
5982 return S_OK;
5983}
5984
5985STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
5986 ULONG *puOriginX,
5987 ULONG *puOriginY,
5988 ULONG *puWidth,
5989 ULONG *puHeight,
5990 BOOL *pfEnabled)
5991{
5992 LogFlowThisFunc(("\n"));
5993
5994 CheckComArgNotNull(puOriginX);
5995 CheckComArgNotNull(puOriginY);
5996 CheckComArgNotNull(puWidth);
5997 CheckComArgNotNull(puHeight);
5998 CheckComArgNotNull(pfEnabled);
5999
6000 uint32_t u32OriginX= 0;
6001 uint32_t u32OriginY= 0;
6002 uint32_t u32Width = 0;
6003 uint32_t u32Height = 0;
6004 uint16_t u16Flags = 0;
6005
6006 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6007 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6008 if (RT_FAILURE(vrc))
6009 {
6010#ifdef RT_OS_WINDOWS
6011 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6012 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6013 * So just assign fEnable to TRUE again.
6014 * The right fix would be to change GUI API wrappers to make sure that parameters
6015 * are changed only if API succeeds.
6016 */
6017 *pfEnabled = TRUE;
6018#endif
6019 return setError(VBOX_E_IPRT_ERROR,
6020 tr("Saved guest size is not available (%Rrc)"),
6021 vrc);
6022 }
6023
6024 *puOriginX = u32OriginX;
6025 *puOriginY = u32OriginY;
6026 *puWidth = u32Width;
6027 *puHeight = u32Height;
6028 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6029
6030 return S_OK;
6031}
6032
6033STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6034{
6035 LogFlowThisFunc(("\n"));
6036
6037 CheckComArgNotNull(aSize);
6038 CheckComArgNotNull(aWidth);
6039 CheckComArgNotNull(aHeight);
6040
6041 if (aScreenId != 0)
6042 return E_NOTIMPL;
6043
6044 AutoCaller autoCaller(this);
6045 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6046
6047 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6048
6049 uint8_t *pu8Data = NULL;
6050 uint32_t cbData = 0;
6051 uint32_t u32Width = 0;
6052 uint32_t u32Height = 0;
6053
6054 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6055
6056 if (RT_FAILURE(vrc))
6057 return setError(VBOX_E_IPRT_ERROR,
6058 tr("Saved screenshot data is not available (%Rrc)"),
6059 vrc);
6060
6061 *aSize = cbData;
6062 *aWidth = u32Width;
6063 *aHeight = u32Height;
6064
6065 freeSavedDisplayScreenshot(pu8Data);
6066
6067 return S_OK;
6068}
6069
6070STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6071{
6072 LogFlowThisFunc(("\n"));
6073
6074 CheckComArgNotNull(aWidth);
6075 CheckComArgNotNull(aHeight);
6076 CheckComArgOutSafeArrayPointerValid(aData);
6077
6078 if (aScreenId != 0)
6079 return E_NOTIMPL;
6080
6081 AutoCaller autoCaller(this);
6082 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6083
6084 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6085
6086 uint8_t *pu8Data = NULL;
6087 uint32_t cbData = 0;
6088 uint32_t u32Width = 0;
6089 uint32_t u32Height = 0;
6090
6091 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6092
6093 if (RT_FAILURE(vrc))
6094 return setError(VBOX_E_IPRT_ERROR,
6095 tr("Saved screenshot data is not available (%Rrc)"),
6096 vrc);
6097
6098 *aWidth = u32Width;
6099 *aHeight = u32Height;
6100
6101 com::SafeArray<BYTE> bitmap(cbData);
6102 /* Convert pixels to format expected by the API caller. */
6103 if (aBGR)
6104 {
6105 /* [0] B, [1] G, [2] R, [3] A. */
6106 for (unsigned i = 0; i < cbData; i += 4)
6107 {
6108 bitmap[i] = pu8Data[i];
6109 bitmap[i + 1] = pu8Data[i + 1];
6110 bitmap[i + 2] = pu8Data[i + 2];
6111 bitmap[i + 3] = 0xff;
6112 }
6113 }
6114 else
6115 {
6116 /* [0] R, [1] G, [2] B, [3] A. */
6117 for (unsigned i = 0; i < cbData; i += 4)
6118 {
6119 bitmap[i] = pu8Data[i + 2];
6120 bitmap[i + 1] = pu8Data[i + 1];
6121 bitmap[i + 2] = pu8Data[i];
6122 bitmap[i + 3] = 0xff;
6123 }
6124 }
6125 bitmap.detachTo(ComSafeArrayOutArg(aData));
6126
6127 freeSavedDisplayScreenshot(pu8Data);
6128
6129 return S_OK;
6130}
6131
6132
6133STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6134{
6135 LogFlowThisFunc(("\n"));
6136
6137 CheckComArgNotNull(aWidth);
6138 CheckComArgNotNull(aHeight);
6139 CheckComArgOutSafeArrayPointerValid(aData);
6140
6141 if (aScreenId != 0)
6142 return E_NOTIMPL;
6143
6144 AutoCaller autoCaller(this);
6145 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6146
6147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6148
6149 uint8_t *pu8Data = NULL;
6150 uint32_t cbData = 0;
6151 uint32_t u32Width = 0;
6152 uint32_t u32Height = 0;
6153
6154 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6155
6156 if (RT_FAILURE(vrc))
6157 return setError(VBOX_E_IPRT_ERROR,
6158 tr("Saved screenshot data is not available (%Rrc)"),
6159 vrc);
6160
6161 *aWidth = u32Width;
6162 *aHeight = u32Height;
6163
6164 HRESULT rc = S_OK;
6165 uint8_t *pu8PNG = NULL;
6166 uint32_t cbPNG = 0;
6167 uint32_t cxPNG = 0;
6168 uint32_t cyPNG = 0;
6169
6170 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6171
6172 if (RT_SUCCESS(vrc))
6173 {
6174 com::SafeArray<BYTE> screenData(cbPNG);
6175 screenData.initFrom(pu8PNG, cbPNG);
6176 if (pu8PNG)
6177 RTMemFree(pu8PNG);
6178 screenData.detachTo(ComSafeArrayOutArg(aData));
6179 }
6180 else
6181 {
6182 if (pu8PNG)
6183 RTMemFree(pu8PNG);
6184 return setError(VBOX_E_IPRT_ERROR,
6185 tr("Could not convert screenshot to PNG (%Rrc)"),
6186 vrc);
6187 }
6188
6189 freeSavedDisplayScreenshot(pu8Data);
6190
6191 return rc;
6192}
6193
6194STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6195{
6196 LogFlowThisFunc(("\n"));
6197
6198 CheckComArgNotNull(aSize);
6199 CheckComArgNotNull(aWidth);
6200 CheckComArgNotNull(aHeight);
6201
6202 if (aScreenId != 0)
6203 return E_NOTIMPL;
6204
6205 AutoCaller autoCaller(this);
6206 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6207
6208 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6209
6210 uint8_t *pu8Data = NULL;
6211 uint32_t cbData = 0;
6212 uint32_t u32Width = 0;
6213 uint32_t u32Height = 0;
6214
6215 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6216
6217 if (RT_FAILURE(vrc))
6218 return setError(VBOX_E_IPRT_ERROR,
6219 tr("Saved screenshot data is not available (%Rrc)"),
6220 vrc);
6221
6222 *aSize = cbData;
6223 *aWidth = u32Width;
6224 *aHeight = u32Height;
6225
6226 freeSavedDisplayScreenshot(pu8Data);
6227
6228 return S_OK;
6229}
6230
6231STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6232{
6233 LogFlowThisFunc(("\n"));
6234
6235 CheckComArgNotNull(aWidth);
6236 CheckComArgNotNull(aHeight);
6237 CheckComArgOutSafeArrayPointerValid(aData);
6238
6239 if (aScreenId != 0)
6240 return E_NOTIMPL;
6241
6242 AutoCaller autoCaller(this);
6243 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6244
6245 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6246
6247 uint8_t *pu8Data = NULL;
6248 uint32_t cbData = 0;
6249 uint32_t u32Width = 0;
6250 uint32_t u32Height = 0;
6251
6252 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6253
6254 if (RT_FAILURE(vrc))
6255 return setError(VBOX_E_IPRT_ERROR,
6256 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6257 vrc);
6258
6259 *aWidth = u32Width;
6260 *aHeight = u32Height;
6261
6262 com::SafeArray<BYTE> png(cbData);
6263 png.initFrom(pu8Data, cbData);
6264 png.detachTo(ComSafeArrayOutArg(aData));
6265
6266 freeSavedDisplayScreenshot(pu8Data);
6267
6268 return S_OK;
6269}
6270
6271STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6272{
6273 HRESULT rc = S_OK;
6274 LogFlowThisFunc(("\n"));
6275
6276 AutoCaller autoCaller(this);
6277 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6278
6279 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6280
6281 if (!mHWData->mCPUHotPlugEnabled)
6282 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6283
6284 if (aCpu >= mHWData->mCPUCount)
6285 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6286
6287 if (mHWData->mCPUAttached[aCpu])
6288 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6289
6290 alock.release();
6291 rc = onCPUChange(aCpu, false);
6292 alock.acquire();
6293 if (FAILED(rc)) return rc;
6294
6295 setModified(IsModified_MachineData);
6296 mHWData.backup();
6297 mHWData->mCPUAttached[aCpu] = true;
6298
6299 /* Save settings if online */
6300 if (Global::IsOnline(mData->mMachineState))
6301 saveSettings(NULL);
6302
6303 return S_OK;
6304}
6305
6306STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6307{
6308 HRESULT rc = S_OK;
6309 LogFlowThisFunc(("\n"));
6310
6311 AutoCaller autoCaller(this);
6312 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6313
6314 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6315
6316 if (!mHWData->mCPUHotPlugEnabled)
6317 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6318
6319 if (aCpu >= SchemaDefs::MaxCPUCount)
6320 return setError(E_INVALIDARG,
6321 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6322 SchemaDefs::MaxCPUCount);
6323
6324 if (!mHWData->mCPUAttached[aCpu])
6325 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6326
6327 /* CPU 0 can't be detached */
6328 if (aCpu == 0)
6329 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6330
6331 alock.release();
6332 rc = onCPUChange(aCpu, true);
6333 alock.acquire();
6334 if (FAILED(rc)) return rc;
6335
6336 setModified(IsModified_MachineData);
6337 mHWData.backup();
6338 mHWData->mCPUAttached[aCpu] = false;
6339
6340 /* Save settings if online */
6341 if (Global::IsOnline(mData->mMachineState))
6342 saveSettings(NULL);
6343
6344 return S_OK;
6345}
6346
6347STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6348{
6349 LogFlowThisFunc(("\n"));
6350
6351 CheckComArgNotNull(aCpuAttached);
6352
6353 *aCpuAttached = false;
6354
6355 AutoCaller autoCaller(this);
6356 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6357
6358 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6359
6360 /* If hotplug is enabled the CPU is always enabled. */
6361 if (!mHWData->mCPUHotPlugEnabled)
6362 {
6363 if (aCpu < mHWData->mCPUCount)
6364 *aCpuAttached = true;
6365 }
6366 else
6367 {
6368 if (aCpu < SchemaDefs::MaxCPUCount)
6369 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6370 }
6371
6372 return S_OK;
6373}
6374
6375STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6376{
6377 CheckComArgOutPointerValid(aName);
6378
6379 AutoCaller autoCaller(this);
6380 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6381
6382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6383
6384 Utf8Str log = queryLogFilename(aIdx);
6385 if (!RTFileExists(log.c_str()))
6386 log.setNull();
6387 log.cloneTo(aName);
6388
6389 return S_OK;
6390}
6391
6392STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6393{
6394 LogFlowThisFunc(("\n"));
6395 CheckComArgOutSafeArrayPointerValid(aData);
6396 if (aSize < 0)
6397 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6398
6399 AutoCaller autoCaller(this);
6400 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6401
6402 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6403
6404 HRESULT rc = S_OK;
6405 Utf8Str log = queryLogFilename(aIdx);
6406
6407 /* do not unnecessarily hold the lock while doing something which does
6408 * not need the lock and potentially takes a long time. */
6409 alock.release();
6410
6411 /* Limit the chunk size to 32K for now, as that gives better performance
6412 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6413 * One byte expands to approx. 25 bytes of breathtaking XML. */
6414 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6415 com::SafeArray<BYTE> logData(cbData);
6416
6417 RTFILE LogFile;
6418 int vrc = RTFileOpen(&LogFile, log.c_str(),
6419 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6420 if (RT_SUCCESS(vrc))
6421 {
6422 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6423 if (RT_SUCCESS(vrc))
6424 logData.resize(cbData);
6425 else
6426 rc = setError(VBOX_E_IPRT_ERROR,
6427 tr("Could not read log file '%s' (%Rrc)"),
6428 log.c_str(), vrc);
6429 RTFileClose(LogFile);
6430 }
6431 else
6432 rc = setError(VBOX_E_IPRT_ERROR,
6433 tr("Could not open log file '%s' (%Rrc)"),
6434 log.c_str(), vrc);
6435
6436 if (FAILED(rc))
6437 logData.resize(0);
6438 logData.detachTo(ComSafeArrayOutArg(aData));
6439
6440 return rc;
6441}
6442
6443
6444/**
6445 * Currently this method doesn't attach device to the running VM,
6446 * just makes sure it's plugged on next VM start.
6447 */
6448STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6449{
6450 AutoCaller autoCaller(this);
6451 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6452
6453 // lock scope
6454 {
6455 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6456
6457 HRESULT rc = checkStateDependency(MutableStateDep);
6458 if (FAILED(rc)) return rc;
6459
6460 ChipsetType_T aChipset = ChipsetType_PIIX3;
6461 COMGETTER(ChipsetType)(&aChipset);
6462
6463 if (aChipset != ChipsetType_ICH9)
6464 {
6465 return setError(E_INVALIDARG,
6466 tr("Host PCI attachment only supported with ICH9 chipset"));
6467 }
6468
6469 // check if device with this host PCI address already attached
6470 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6471 it != mHWData->mPCIDeviceAssignments.end();
6472 ++it)
6473 {
6474 LONG iHostAddress = -1;
6475 ComPtr<PCIDeviceAttachment> pAttach;
6476 pAttach = *it;
6477 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6478 if (iHostAddress == hostAddress)
6479 return setError(E_INVALIDARG,
6480 tr("Device with host PCI address already attached to this VM"));
6481 }
6482
6483 ComObjPtr<PCIDeviceAttachment> pda;
6484 char name[32];
6485
6486 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6487 Bstr bname(name);
6488 pda.createObject();
6489 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
6490 setModified(IsModified_MachineData);
6491 mHWData.backup();
6492 mHWData->mPCIDeviceAssignments.push_back(pda);
6493 }
6494
6495 return S_OK;
6496}
6497
6498/**
6499 * Currently this method doesn't detach device from the running VM,
6500 * just makes sure it's not plugged on next VM start.
6501 */
6502STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
6503{
6504 AutoCaller autoCaller(this);
6505 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6506
6507 ComObjPtr<PCIDeviceAttachment> pAttach;
6508 bool fRemoved = false;
6509 HRESULT rc;
6510
6511 // lock scope
6512 {
6513 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6514
6515 rc = checkStateDependency(MutableStateDep);
6516 if (FAILED(rc)) return rc;
6517
6518 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6519 it != mHWData->mPCIDeviceAssignments.end();
6520 ++it)
6521 {
6522 LONG iHostAddress = -1;
6523 pAttach = *it;
6524 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6525 if (iHostAddress != -1 && iHostAddress == hostAddress)
6526 {
6527 setModified(IsModified_MachineData);
6528 mHWData.backup();
6529 mHWData->mPCIDeviceAssignments.remove(pAttach);
6530 fRemoved = true;
6531 break;
6532 }
6533 }
6534 }
6535
6536
6537 /* Fire event outside of the lock */
6538 if (fRemoved)
6539 {
6540 Assert(!pAttach.isNull());
6541 ComPtr<IEventSource> es;
6542 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6543 Assert(SUCCEEDED(rc));
6544 Bstr mid;
6545 rc = this->COMGETTER(Id)(mid.asOutParam());
6546 Assert(SUCCEEDED(rc));
6547 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6548 }
6549
6550 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6551 tr("No host PCI device %08x attached"),
6552 hostAddress
6553 );
6554}
6555
6556STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
6557{
6558 CheckComArgOutSafeArrayPointerValid(aAssignments);
6559
6560 AutoCaller autoCaller(this);
6561 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6562
6563 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6564
6565 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
6566 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
6567
6568 return S_OK;
6569}
6570
6571STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
6572{
6573 CheckComArgOutPointerValid(aBandwidthControl);
6574
6575 AutoCaller autoCaller(this);
6576 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6577
6578 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
6579
6580 return S_OK;
6581}
6582
6583STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
6584{
6585 CheckComArgOutPointerValid(pfEnabled);
6586 AutoCaller autoCaller(this);
6587 HRESULT hrc = autoCaller.rc();
6588 if (SUCCEEDED(hrc))
6589 {
6590 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6591 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
6592 }
6593 return hrc;
6594}
6595
6596STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
6597{
6598 AutoCaller autoCaller(this);
6599 HRESULT hrc = autoCaller.rc();
6600 if (SUCCEEDED(hrc))
6601 {
6602 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6603 hrc = checkStateDependency(MutableStateDep);
6604 if (SUCCEEDED(hrc))
6605 {
6606 hrc = mHWData.backupEx();
6607 if (SUCCEEDED(hrc))
6608 {
6609 setModified(IsModified_MachineData);
6610 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
6611 }
6612 }
6613 }
6614 return hrc;
6615}
6616
6617STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
6618{
6619 CheckComArgOutPointerValid(pbstrConfig);
6620 AutoCaller autoCaller(this);
6621 HRESULT hrc = autoCaller.rc();
6622 if (SUCCEEDED(hrc))
6623 {
6624 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6625 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
6626 }
6627 return hrc;
6628}
6629
6630STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
6631{
6632 CheckComArgStr(bstrConfig);
6633 AutoCaller autoCaller(this);
6634 HRESULT hrc = autoCaller.rc();
6635 if (SUCCEEDED(hrc))
6636 {
6637 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6638 hrc = checkStateDependency(MutableStateDep);
6639 if (SUCCEEDED(hrc))
6640 {
6641 hrc = mHWData.backupEx();
6642 if (SUCCEEDED(hrc))
6643 {
6644 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
6645 if (SUCCEEDED(hrc))
6646 setModified(IsModified_MachineData);
6647 }
6648 }
6649 }
6650 return hrc;
6651
6652}
6653
6654STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
6655{
6656 CheckComArgOutPointerValid(pfAllow);
6657 AutoCaller autoCaller(this);
6658 HRESULT hrc = autoCaller.rc();
6659 if (SUCCEEDED(hrc))
6660 {
6661 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6662 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
6663 }
6664 return hrc;
6665}
6666
6667STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
6668{
6669 AutoCaller autoCaller(this);
6670 HRESULT hrc = autoCaller.rc();
6671 if (SUCCEEDED(hrc))
6672 {
6673 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6674 hrc = checkStateDependency(MutableStateDep);
6675 if (SUCCEEDED(hrc))
6676 {
6677 hrc = mHWData.backupEx();
6678 if (SUCCEEDED(hrc))
6679 {
6680 setModified(IsModified_MachineData);
6681 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
6682 }
6683 }
6684 }
6685 return hrc;
6686}
6687
6688STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
6689{
6690 CheckComArgOutPointerValid(pfEnabled);
6691 AutoCaller autoCaller(this);
6692 HRESULT hrc = autoCaller.rc();
6693 if (SUCCEEDED(hrc))
6694 {
6695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6696 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
6697 }
6698 return hrc;
6699}
6700
6701STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
6702{
6703 AutoCaller autoCaller(this);
6704 HRESULT hrc = autoCaller.rc();
6705 if (SUCCEEDED(hrc))
6706 {
6707 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6708 hrc = checkStateDependency(MutableStateDep);
6709 if ( SUCCEEDED(hrc)
6710 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
6711 {
6712 AutostartDb *autostartDb = mParent->getAutostartDb();
6713 int vrc;
6714
6715 if (fEnabled)
6716 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6717 else
6718 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6719
6720 if (RT_SUCCESS(vrc))
6721 {
6722 hrc = mHWData.backupEx();
6723 if (SUCCEEDED(hrc))
6724 {
6725 setModified(IsModified_MachineData);
6726 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
6727 }
6728 }
6729 else if (vrc == VERR_NOT_SUPPORTED)
6730 hrc = setError(VBOX_E_NOT_SUPPORTED,
6731 tr("The VM autostart feature is not supported on this platform"));
6732 else if (vrc == VERR_PATH_NOT_FOUND)
6733 hrc = setError(E_FAIL,
6734 tr("The path to the autostart database is not set"));
6735 else
6736 hrc = setError(E_UNEXPECTED,
6737 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6738 fEnabled ? "Adding" : "Removing",
6739 mUserData->s.strName.c_str(), vrc);
6740 }
6741 }
6742 return hrc;
6743}
6744
6745STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
6746{
6747 CheckComArgOutPointerValid(puDelay);
6748 AutoCaller autoCaller(this);
6749 HRESULT hrc = autoCaller.rc();
6750 if (SUCCEEDED(hrc))
6751 {
6752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6753 *puDelay = mHWData->mAutostart.uAutostartDelay;
6754 }
6755 return hrc;
6756}
6757
6758STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
6759{
6760 AutoCaller autoCaller(this);
6761 HRESULT hrc = autoCaller.rc();
6762 if (SUCCEEDED(hrc))
6763 {
6764 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6765 hrc = checkStateDependency(MutableStateDep);
6766 if (SUCCEEDED(hrc))
6767 {
6768 hrc = mHWData.backupEx();
6769 if (SUCCEEDED(hrc))
6770 {
6771 setModified(IsModified_MachineData);
6772 mHWData->mAutostart.uAutostartDelay = uDelay;
6773 }
6774 }
6775 }
6776 return hrc;
6777}
6778
6779STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
6780{
6781 CheckComArgOutPointerValid(penmAutostopType);
6782 AutoCaller autoCaller(this);
6783 HRESULT hrc = autoCaller.rc();
6784 if (SUCCEEDED(hrc))
6785 {
6786 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6787 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
6788 }
6789 return hrc;
6790}
6791
6792STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
6793{
6794 AutoCaller autoCaller(this);
6795 HRESULT hrc = autoCaller.rc();
6796 if (SUCCEEDED(hrc))
6797 {
6798 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6799 hrc = checkStateDependency(MutableStateDep);
6800 if ( SUCCEEDED(hrc)
6801 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
6802 {
6803 AutostartDb *autostartDb = mParent->getAutostartDb();
6804 int vrc;
6805
6806 if (enmAutostopType != AutostopType_Disabled)
6807 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6808 else
6809 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6810
6811 if (RT_SUCCESS(vrc))
6812 {
6813 hrc = mHWData.backupEx();
6814 if (SUCCEEDED(hrc))
6815 {
6816 setModified(IsModified_MachineData);
6817 mHWData->mAutostart.enmAutostopType = enmAutostopType;
6818 }
6819 }
6820 else if (vrc == VERR_NOT_SUPPORTED)
6821 hrc = setError(VBOX_E_NOT_SUPPORTED,
6822 tr("The VM autostop feature is not supported on this platform"));
6823 else if (vrc == VERR_PATH_NOT_FOUND)
6824 hrc = setError(E_FAIL,
6825 tr("The path to the autostart database is not set"));
6826 else
6827 hrc = setError(E_UNEXPECTED,
6828 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6829 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6830 mUserData->s.strName.c_str(), vrc);
6831 }
6832 }
6833 return hrc;
6834}
6835
6836
6837STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
6838{
6839 LogFlowFuncEnter();
6840
6841 CheckComArgNotNull(pTarget);
6842 CheckComArgOutPointerValid(pProgress);
6843
6844 /* Convert the options. */
6845 RTCList<CloneOptions_T> optList;
6846 if (options != NULL)
6847 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
6848
6849 if (optList.contains(CloneOptions_Link))
6850 {
6851 if (!isSnapshotMachine())
6852 return setError(E_INVALIDARG,
6853 tr("Linked clone can only be created from a snapshot"));
6854 if (mode != CloneMode_MachineState)
6855 return setError(E_INVALIDARG,
6856 tr("Linked clone can only be created for a single machine state"));
6857 }
6858 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6859
6860 AutoCaller autoCaller(this);
6861 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6862
6863
6864 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
6865
6866 HRESULT rc = pWorker->start(pProgress);
6867
6868 LogFlowFuncLeave();
6869
6870 return rc;
6871}
6872
6873// public methods for internal purposes
6874/////////////////////////////////////////////////////////////////////////////
6875
6876/**
6877 * Adds the given IsModified_* flag to the dirty flags of the machine.
6878 * This must be called either during loadSettings or under the machine write lock.
6879 * @param fl
6880 */
6881void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
6882{
6883 mData->flModifications |= fl;
6884 if (fAllowStateModification && isStateModificationAllowed())
6885 mData->mCurrentStateModified = true;
6886}
6887
6888/**
6889 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
6890 * care of the write locking.
6891 *
6892 * @param fModifications The flag to add.
6893 */
6894void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
6895{
6896 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6897 setModified(fModification, fAllowStateModification);
6898}
6899
6900/**
6901 * Saves the registry entry of this machine to the given configuration node.
6902 *
6903 * @param aEntryNode Node to save the registry entry to.
6904 *
6905 * @note locks this object for reading.
6906 */
6907HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
6908{
6909 AutoLimitedCaller autoCaller(this);
6910 AssertComRCReturnRC(autoCaller.rc());
6911
6912 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6913
6914 data.uuid = mData->mUuid;
6915 data.strSettingsFile = mData->m_strConfigFile;
6916
6917 return S_OK;
6918}
6919
6920/**
6921 * Calculates the absolute path of the given path taking the directory of the
6922 * machine settings file as the current directory.
6923 *
6924 * @param aPath Path to calculate the absolute path for.
6925 * @param aResult Where to put the result (used only on success, can be the
6926 * same Utf8Str instance as passed in @a aPath).
6927 * @return IPRT result.
6928 *
6929 * @note Locks this object for reading.
6930 */
6931int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
6932{
6933 AutoCaller autoCaller(this);
6934 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
6935
6936 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6937
6938 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
6939
6940 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
6941
6942 strSettingsDir.stripFilename();
6943 char folder[RTPATH_MAX];
6944 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
6945 if (RT_SUCCESS(vrc))
6946 aResult = folder;
6947
6948 return vrc;
6949}
6950
6951/**
6952 * Copies strSource to strTarget, making it relative to the machine folder
6953 * if it is a subdirectory thereof, or simply copying it otherwise.
6954 *
6955 * @param strSource Path to evaluate and copy.
6956 * @param strTarget Buffer to receive target path.
6957 *
6958 * @note Locks this object for reading.
6959 */
6960void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
6961 Utf8Str &strTarget)
6962{
6963 AutoCaller autoCaller(this);
6964 AssertComRCReturn(autoCaller.rc(), (void)0);
6965
6966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6967
6968 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
6969 // use strTarget as a temporary buffer to hold the machine settings dir
6970 strTarget = mData->m_strConfigFileFull;
6971 strTarget.stripFilename();
6972 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
6973 {
6974 // is relative: then append what's left
6975 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
6976 // for empty paths (only possible for subdirs) use "." to avoid
6977 // triggering default settings for not present config attributes.
6978 if (strTarget.isEmpty())
6979 strTarget = ".";
6980 }
6981 else
6982 // is not relative: then overwrite
6983 strTarget = strSource;
6984}
6985
6986/**
6987 * Returns the full path to the machine's log folder in the
6988 * \a aLogFolder argument.
6989 */
6990void Machine::getLogFolder(Utf8Str &aLogFolder)
6991{
6992 AutoCaller autoCaller(this);
6993 AssertComRCReturnVoid(autoCaller.rc());
6994
6995 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6996
6997 char szTmp[RTPATH_MAX];
6998 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
6999 if (RT_SUCCESS(vrc))
7000 {
7001 if (szTmp[0] && !mUserData.isNull())
7002 {
7003 char szTmp2[RTPATH_MAX];
7004 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7005 if (RT_SUCCESS(vrc))
7006 aLogFolder = BstrFmt("%s%c%s",
7007 szTmp2,
7008 RTPATH_DELIMITER,
7009 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7010 }
7011 else
7012 vrc = VERR_PATH_IS_RELATIVE;
7013 }
7014
7015 if (RT_FAILURE(vrc))
7016 {
7017 // fallback if VBOX_USER_LOGHOME is not set or invalid
7018 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7019 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7020 aLogFolder.append(RTPATH_DELIMITER);
7021 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7022 }
7023}
7024
7025/**
7026 * Returns the full path to the machine's log file for an given index.
7027 */
7028Utf8Str Machine::queryLogFilename(ULONG idx)
7029{
7030 Utf8Str logFolder;
7031 getLogFolder(logFolder);
7032 Assert(logFolder.length());
7033 Utf8Str log;
7034 if (idx == 0)
7035 log = Utf8StrFmt("%s%cVBox.log",
7036 logFolder.c_str(), RTPATH_DELIMITER);
7037 else
7038 log = Utf8StrFmt("%s%cVBox.log.%d",
7039 logFolder.c_str(), RTPATH_DELIMITER, idx);
7040 return log;
7041}
7042
7043/**
7044 * Composes a unique saved state filename based on the current system time. The filename is
7045 * granular to the second so this will work so long as no more than one snapshot is taken on
7046 * a machine per second.
7047 *
7048 * Before version 4.1, we used this formula for saved state files:
7049 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7050 * which no longer works because saved state files can now be shared between the saved state of the
7051 * "saved" machine and an online snapshot, and the following would cause problems:
7052 * 1) save machine
7053 * 2) create online snapshot from that machine state --> reusing saved state file
7054 * 3) save machine again --> filename would be reused, breaking the online snapshot
7055 *
7056 * So instead we now use a timestamp.
7057 *
7058 * @param str
7059 */
7060void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7061{
7062 AutoCaller autoCaller(this);
7063 AssertComRCReturnVoid(autoCaller.rc());
7064
7065 {
7066 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7067 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7068 }
7069
7070 RTTIMESPEC ts;
7071 RTTimeNow(&ts);
7072 RTTIME time;
7073 RTTimeExplode(&time, &ts);
7074
7075 strStateFilePath += RTPATH_DELIMITER;
7076 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7077 time.i32Year, time.u8Month, time.u8MonthDay,
7078 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7079}
7080
7081/**
7082 * @note Locks this object for writing, calls the client process
7083 * (inside the lock).
7084 */
7085HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7086 const Utf8Str &strType,
7087 const Utf8Str &strEnvironment,
7088 ProgressProxy *aProgress)
7089{
7090 LogFlowThisFuncEnter();
7091
7092 AssertReturn(aControl, E_FAIL);
7093 AssertReturn(aProgress, E_FAIL);
7094
7095 AutoCaller autoCaller(this);
7096 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7097
7098 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7099
7100 if (!mData->mRegistered)
7101 return setError(E_UNEXPECTED,
7102 tr("The machine '%s' is not registered"),
7103 mUserData->s.strName.c_str());
7104
7105 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7106
7107 if ( mData->mSession.mState == SessionState_Locked
7108 || mData->mSession.mState == SessionState_Spawning
7109 || mData->mSession.mState == SessionState_Unlocking)
7110 return setError(VBOX_E_INVALID_OBJECT_STATE,
7111 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7112 mUserData->s.strName.c_str());
7113
7114 /* may not be busy */
7115 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7116
7117 /* get the path to the executable */
7118 char szPath[RTPATH_MAX];
7119 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7120 size_t sz = strlen(szPath);
7121 szPath[sz++] = RTPATH_DELIMITER;
7122 szPath[sz] = 0;
7123 char *cmd = szPath + sz;
7124 sz = RTPATH_MAX - sz;
7125
7126 int vrc = VINF_SUCCESS;
7127 RTPROCESS pid = NIL_RTPROCESS;
7128
7129 RTENV env = RTENV_DEFAULT;
7130
7131 if (!strEnvironment.isEmpty())
7132 {
7133 char *newEnvStr = NULL;
7134
7135 do
7136 {
7137 /* clone the current environment */
7138 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7139 AssertRCBreakStmt(vrc2, vrc = vrc2);
7140
7141 newEnvStr = RTStrDup(strEnvironment.c_str());
7142 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7143
7144 /* put new variables to the environment
7145 * (ignore empty variable names here since RTEnv API
7146 * intentionally doesn't do that) */
7147 char *var = newEnvStr;
7148 for (char *p = newEnvStr; *p; ++p)
7149 {
7150 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7151 {
7152 *p = '\0';
7153 if (*var)
7154 {
7155 char *val = strchr(var, '=');
7156 if (val)
7157 {
7158 *val++ = '\0';
7159 vrc2 = RTEnvSetEx(env, var, val);
7160 }
7161 else
7162 vrc2 = RTEnvUnsetEx(env, var);
7163 if (RT_FAILURE(vrc2))
7164 break;
7165 }
7166 var = p + 1;
7167 }
7168 }
7169 if (RT_SUCCESS(vrc2) && *var)
7170 vrc2 = RTEnvPutEx(env, var);
7171
7172 AssertRCBreakStmt(vrc2, vrc = vrc2);
7173 }
7174 while (0);
7175
7176 if (newEnvStr != NULL)
7177 RTStrFree(newEnvStr);
7178 }
7179
7180 /* Qt is default */
7181#ifdef VBOX_WITH_QTGUI
7182 if (strType == "gui" || strType == "GUI/Qt")
7183 {
7184# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7185 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7186# else
7187 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7188# endif
7189 Assert(sz >= sizeof(VirtualBox_exe));
7190 strcpy(cmd, VirtualBox_exe);
7191
7192 Utf8Str idStr = mData->mUuid.toString();
7193 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7194 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7195 }
7196#else /* !VBOX_WITH_QTGUI */
7197 if (0)
7198 ;
7199#endif /* VBOX_WITH_QTGUI */
7200
7201 else
7202
7203#ifdef VBOX_WITH_VBOXSDL
7204 if (strType == "sdl" || strType == "GUI/SDL")
7205 {
7206 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7207 Assert(sz >= sizeof(VBoxSDL_exe));
7208 strcpy(cmd, VBoxSDL_exe);
7209
7210 Utf8Str idStr = mData->mUuid.toString();
7211 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7212 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7213 }
7214#else /* !VBOX_WITH_VBOXSDL */
7215 if (0)
7216 ;
7217#endif /* !VBOX_WITH_VBOXSDL */
7218
7219 else
7220
7221#ifdef VBOX_WITH_HEADLESS
7222 if ( strType == "headless"
7223 || strType == "capture"
7224 || strType == "vrdp" /* Deprecated. Same as headless. */
7225 )
7226 {
7227 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7228 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7229 * and a VM works even if the server has not been installed.
7230 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7231 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7232 * differently in 4.0 and 3.x.
7233 */
7234 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7235 Assert(sz >= sizeof(VBoxHeadless_exe));
7236 strcpy(cmd, VBoxHeadless_exe);
7237
7238 Utf8Str idStr = mData->mUuid.toString();
7239 /* Leave space for "--capture" arg. */
7240 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7241 "--startvm", idStr.c_str(),
7242 "--vrde", "config",
7243 0, /* For "--capture". */
7244 0 };
7245 if (strType == "capture")
7246 {
7247 unsigned pos = RT_ELEMENTS(args) - 2;
7248 args[pos] = "--capture";
7249 }
7250 vrc = RTProcCreate(szPath, args, env,
7251#ifdef RT_OS_WINDOWS
7252 RTPROC_FLAGS_NO_WINDOW
7253#else
7254 0
7255#endif
7256 , &pid);
7257 }
7258#else /* !VBOX_WITH_HEADLESS */
7259 if (0)
7260 ;
7261#endif /* !VBOX_WITH_HEADLESS */
7262 else
7263 {
7264 RTEnvDestroy(env);
7265 return setError(E_INVALIDARG,
7266 tr("Invalid session type: '%s'"),
7267 strType.c_str());
7268 }
7269
7270 RTEnvDestroy(env);
7271
7272 if (RT_FAILURE(vrc))
7273 return setError(VBOX_E_IPRT_ERROR,
7274 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7275 mUserData->s.strName.c_str(), vrc);
7276
7277 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7278
7279 /*
7280 * Note that we don't release the lock here before calling the client,
7281 * because it doesn't need to call us back if called with a NULL argument.
7282 * Releasing the lock here is dangerous because we didn't prepare the
7283 * launch data yet, but the client we've just started may happen to be
7284 * too fast and call openSession() that will fail (because of PID, etc.),
7285 * so that the Machine will never get out of the Spawning session state.
7286 */
7287
7288 /* inform the session that it will be a remote one */
7289 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7290 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
7291 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7292
7293 if (FAILED(rc))
7294 {
7295 /* restore the session state */
7296 mData->mSession.mState = SessionState_Unlocked;
7297 /* The failure may occur w/o any error info (from RPC), so provide one */
7298 return setError(VBOX_E_VM_ERROR,
7299 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7300 }
7301
7302 /* attach launch data to the machine */
7303 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7304 mData->mSession.mRemoteControls.push_back(aControl);
7305 mData->mSession.mProgress = aProgress;
7306 mData->mSession.mPID = pid;
7307 mData->mSession.mState = SessionState_Spawning;
7308 mData->mSession.mType = strType;
7309
7310 LogFlowThisFuncLeave();
7311 return S_OK;
7312}
7313
7314/**
7315 * Returns @c true if the given machine has an open direct session and returns
7316 * the session machine instance and additional session data (on some platforms)
7317 * if so.
7318 *
7319 * Note that when the method returns @c false, the arguments remain unchanged.
7320 *
7321 * @param aMachine Session machine object.
7322 * @param aControl Direct session control object (optional).
7323 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7324 *
7325 * @note locks this object for reading.
7326 */
7327#if defined(RT_OS_WINDOWS)
7328bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7329 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7330 HANDLE *aIPCSem /*= NULL*/,
7331 bool aAllowClosing /*= false*/)
7332#elif defined(RT_OS_OS2)
7333bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7334 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7335 HMTX *aIPCSem /*= NULL*/,
7336 bool aAllowClosing /*= false*/)
7337#else
7338bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7339 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7340 bool aAllowClosing /*= false*/)
7341#endif
7342{
7343 AutoLimitedCaller autoCaller(this);
7344 AssertComRCReturn(autoCaller.rc(), false);
7345
7346 /* just return false for inaccessible machines */
7347 if (autoCaller.state() != Ready)
7348 return false;
7349
7350 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7351
7352 if ( mData->mSession.mState == SessionState_Locked
7353 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7354 )
7355 {
7356 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7357
7358 aMachine = mData->mSession.mMachine;
7359
7360 if (aControl != NULL)
7361 *aControl = mData->mSession.mDirectControl;
7362
7363#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7364 /* Additional session data */
7365 if (aIPCSem != NULL)
7366 *aIPCSem = aMachine->mIPCSem;
7367#endif
7368 return true;
7369 }
7370
7371 return false;
7372}
7373
7374/**
7375 * Returns @c true if the given machine has an spawning direct session and
7376 * returns and additional session data (on some platforms) if so.
7377 *
7378 * Note that when the method returns @c false, the arguments remain unchanged.
7379 *
7380 * @param aPID PID of the spawned direct session process.
7381 *
7382 * @note locks this object for reading.
7383 */
7384#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7385bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7386#else
7387bool Machine::isSessionSpawning()
7388#endif
7389{
7390 AutoLimitedCaller autoCaller(this);
7391 AssertComRCReturn(autoCaller.rc(), false);
7392
7393 /* just return false for inaccessible machines */
7394 if (autoCaller.state() != Ready)
7395 return false;
7396
7397 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7398
7399 if (mData->mSession.mState == SessionState_Spawning)
7400 {
7401#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7402 /* Additional session data */
7403 if (aPID != NULL)
7404 {
7405 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
7406 *aPID = mData->mSession.mPID;
7407 }
7408#endif
7409 return true;
7410 }
7411
7412 return false;
7413}
7414
7415/**
7416 * Called from the client watcher thread to check for unexpected client process
7417 * death during Session_Spawning state (e.g. before it successfully opened a
7418 * direct session).
7419 *
7420 * On Win32 and on OS/2, this method is called only when we've got the
7421 * direct client's process termination notification, so it always returns @c
7422 * true.
7423 *
7424 * On other platforms, this method returns @c true if the client process is
7425 * terminated and @c false if it's still alive.
7426 *
7427 * @note Locks this object for writing.
7428 */
7429bool Machine::checkForSpawnFailure()
7430{
7431 AutoCaller autoCaller(this);
7432 if (!autoCaller.isOk())
7433 {
7434 /* nothing to do */
7435 LogFlowThisFunc(("Already uninitialized!\n"));
7436 return true;
7437 }
7438
7439 /* VirtualBox::addProcessToReap() needs a write lock */
7440 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
7441
7442 if (mData->mSession.mState != SessionState_Spawning)
7443 {
7444 /* nothing to do */
7445 LogFlowThisFunc(("Not spawning any more!\n"));
7446 return true;
7447 }
7448
7449 HRESULT rc = S_OK;
7450
7451#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7452
7453 /* the process was already unexpectedly terminated, we just need to set an
7454 * error and finalize session spawning */
7455 rc = setError(E_FAIL,
7456 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
7457 getName().c_str());
7458#else
7459
7460 /* PID not yet initialized, skip check. */
7461 if (mData->mSession.mPID == NIL_RTPROCESS)
7462 return false;
7463
7464 RTPROCSTATUS status;
7465 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
7466 &status);
7467
7468 if (vrc != VERR_PROCESS_RUNNING)
7469 {
7470 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7471 rc = setError(E_FAIL,
7472 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7473 getName().c_str(), status.iStatus);
7474 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7475 rc = setError(E_FAIL,
7476 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7477 getName().c_str(), status.iStatus);
7478 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7479 rc = setError(E_FAIL,
7480 tr("The virtual machine '%s' has terminated abnormally"),
7481 getName().c_str(), status.iStatus);
7482 else
7483 rc = setError(E_FAIL,
7484 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7485 getName().c_str(), rc);
7486 }
7487
7488#endif
7489
7490 if (FAILED(rc))
7491 {
7492 /* Close the remote session, remove the remote control from the list
7493 * and reset session state to Closed (@note keep the code in sync with
7494 * the relevant part in checkForSpawnFailure()). */
7495
7496 Assert(mData->mSession.mRemoteControls.size() == 1);
7497 if (mData->mSession.mRemoteControls.size() == 1)
7498 {
7499 ErrorInfoKeeper eik;
7500 mData->mSession.mRemoteControls.front()->Uninitialize();
7501 }
7502
7503 mData->mSession.mRemoteControls.clear();
7504 mData->mSession.mState = SessionState_Unlocked;
7505
7506 /* finalize the progress after setting the state */
7507 if (!mData->mSession.mProgress.isNull())
7508 {
7509 mData->mSession.mProgress->notifyComplete(rc);
7510 mData->mSession.mProgress.setNull();
7511 }
7512
7513 mParent->addProcessToReap(mData->mSession.mPID);
7514 mData->mSession.mPID = NIL_RTPROCESS;
7515
7516 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7517 return true;
7518 }
7519
7520 return false;
7521}
7522
7523/**
7524 * Checks whether the machine can be registered. If so, commits and saves
7525 * all settings.
7526 *
7527 * @note Must be called from mParent's write lock. Locks this object and
7528 * children for writing.
7529 */
7530HRESULT Machine::prepareRegister()
7531{
7532 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7533
7534 AutoLimitedCaller autoCaller(this);
7535 AssertComRCReturnRC(autoCaller.rc());
7536
7537 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7538
7539 /* wait for state dependents to drop to zero */
7540 ensureNoStateDependencies();
7541
7542 if (!mData->mAccessible)
7543 return setError(VBOX_E_INVALID_OBJECT_STATE,
7544 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7545 mUserData->s.strName.c_str(),
7546 mData->mUuid.toString().c_str());
7547
7548 AssertReturn(autoCaller.state() == Ready, E_FAIL);
7549
7550 if (mData->mRegistered)
7551 return setError(VBOX_E_INVALID_OBJECT_STATE,
7552 tr("The machine '%s' with UUID {%s} is already registered"),
7553 mUserData->s.strName.c_str(),
7554 mData->mUuid.toString().c_str());
7555
7556 HRESULT rc = S_OK;
7557
7558 // Ensure the settings are saved. If we are going to be registered and
7559 // no config file exists yet, create it by calling saveSettings() too.
7560 if ( (mData->flModifications)
7561 || (!mData->pMachineConfigFile->fileExists())
7562 )
7563 {
7564 rc = saveSettings(NULL);
7565 // no need to check whether VirtualBox.xml needs saving too since
7566 // we can't have a machine XML file rename pending
7567 if (FAILED(rc)) return rc;
7568 }
7569
7570 /* more config checking goes here */
7571
7572 if (SUCCEEDED(rc))
7573 {
7574 /* we may have had implicit modifications we want to fix on success */
7575 commit();
7576
7577 mData->mRegistered = true;
7578 }
7579 else
7580 {
7581 /* we may have had implicit modifications we want to cancel on failure*/
7582 rollback(false /* aNotify */);
7583 }
7584
7585 return rc;
7586}
7587
7588/**
7589 * Increases the number of objects dependent on the machine state or on the
7590 * registered state. Guarantees that these two states will not change at least
7591 * until #releaseStateDependency() is called.
7592 *
7593 * Depending on the @a aDepType value, additional state checks may be made.
7594 * These checks will set extended error info on failure. See
7595 * #checkStateDependency() for more info.
7596 *
7597 * If this method returns a failure, the dependency is not added and the caller
7598 * is not allowed to rely on any particular machine state or registration state
7599 * value and may return the failed result code to the upper level.
7600 *
7601 * @param aDepType Dependency type to add.
7602 * @param aState Current machine state (NULL if not interested).
7603 * @param aRegistered Current registered state (NULL if not interested).
7604 *
7605 * @note Locks this object for writing.
7606 */
7607HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7608 MachineState_T *aState /* = NULL */,
7609 BOOL *aRegistered /* = NULL */)
7610{
7611 AutoCaller autoCaller(this);
7612 AssertComRCReturnRC(autoCaller.rc());
7613
7614 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7615
7616 HRESULT rc = checkStateDependency(aDepType);
7617 if (FAILED(rc)) return rc;
7618
7619 {
7620 if (mData->mMachineStateChangePending != 0)
7621 {
7622 /* ensureNoStateDependencies() is waiting for state dependencies to
7623 * drop to zero so don't add more. It may make sense to wait a bit
7624 * and retry before reporting an error (since the pending state
7625 * transition should be really quick) but let's just assert for
7626 * now to see if it ever happens on practice. */
7627
7628 AssertFailed();
7629
7630 return setError(E_ACCESSDENIED,
7631 tr("Machine state change is in progress. Please retry the operation later."));
7632 }
7633
7634 ++mData->mMachineStateDeps;
7635 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7636 }
7637
7638 if (aState)
7639 *aState = mData->mMachineState;
7640 if (aRegistered)
7641 *aRegistered = mData->mRegistered;
7642
7643 return S_OK;
7644}
7645
7646/**
7647 * Decreases the number of objects dependent on the machine state.
7648 * Must always complete the #addStateDependency() call after the state
7649 * dependency is no more necessary.
7650 */
7651void Machine::releaseStateDependency()
7652{
7653 AutoCaller autoCaller(this);
7654 AssertComRCReturnVoid(autoCaller.rc());
7655
7656 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7657
7658 /* releaseStateDependency() w/o addStateDependency()? */
7659 AssertReturnVoid(mData->mMachineStateDeps != 0);
7660 -- mData->mMachineStateDeps;
7661
7662 if (mData->mMachineStateDeps == 0)
7663 {
7664 /* inform ensureNoStateDependencies() that there are no more deps */
7665 if (mData->mMachineStateChangePending != 0)
7666 {
7667 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7668 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7669 }
7670 }
7671}
7672
7673// protected methods
7674/////////////////////////////////////////////////////////////////////////////
7675
7676/**
7677 * Performs machine state checks based on the @a aDepType value. If a check
7678 * fails, this method will set extended error info, otherwise it will return
7679 * S_OK. It is supposed, that on failure, the caller will immediately return
7680 * the return value of this method to the upper level.
7681 *
7682 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7683 *
7684 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7685 * current state of this machine object allows to change settings of the
7686 * machine (i.e. the machine is not registered, or registered but not running
7687 * and not saved). It is useful to call this method from Machine setters
7688 * before performing any change.
7689 *
7690 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7691 * as for MutableStateDep except that if the machine is saved, S_OK is also
7692 * returned. This is useful in setters which allow changing machine
7693 * properties when it is in the saved state.
7694 *
7695 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7696 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7697 * Aborted).
7698 *
7699 * @param aDepType Dependency type to check.
7700 *
7701 * @note Non Machine based classes should use #addStateDependency() and
7702 * #releaseStateDependency() methods or the smart AutoStateDependency
7703 * template.
7704 *
7705 * @note This method must be called from under this object's read or write
7706 * lock.
7707 */
7708HRESULT Machine::checkStateDependency(StateDependency aDepType)
7709{
7710 switch (aDepType)
7711 {
7712 case AnyStateDep:
7713 {
7714 break;
7715 }
7716 case MutableStateDep:
7717 {
7718 if ( mData->mRegistered
7719 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7720 || ( mData->mMachineState != MachineState_Paused
7721 && mData->mMachineState != MachineState_Running
7722 && mData->mMachineState != MachineState_Aborted
7723 && mData->mMachineState != MachineState_Teleported
7724 && mData->mMachineState != MachineState_PoweredOff
7725 )
7726 )
7727 )
7728 return setError(VBOX_E_INVALID_VM_STATE,
7729 tr("The machine is not mutable (state is %s)"),
7730 Global::stringifyMachineState(mData->mMachineState));
7731 break;
7732 }
7733 case MutableOrSavedStateDep:
7734 {
7735 if ( mData->mRegistered
7736 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7737 || ( mData->mMachineState != MachineState_Paused
7738 && mData->mMachineState != MachineState_Running
7739 && mData->mMachineState != MachineState_Aborted
7740 && mData->mMachineState != MachineState_Teleported
7741 && mData->mMachineState != MachineState_Saved
7742 && mData->mMachineState != MachineState_PoweredOff
7743 )
7744 )
7745 )
7746 return setError(VBOX_E_INVALID_VM_STATE,
7747 tr("The machine is not mutable (state is %s)"),
7748 Global::stringifyMachineState(mData->mMachineState));
7749 break;
7750 }
7751 case OfflineStateDep:
7752 {
7753 if ( mData->mRegistered
7754 && ( !isSessionMachine()
7755 || ( mData->mMachineState != MachineState_PoweredOff
7756 && mData->mMachineState != MachineState_Saved
7757 && mData->mMachineState != MachineState_Aborted
7758 && mData->mMachineState != MachineState_Teleported
7759 )
7760 )
7761 )
7762 return setError(VBOX_E_INVALID_VM_STATE,
7763 tr("The machine is not offline (state is %s)"),
7764 Global::stringifyMachineState(mData->mMachineState));
7765 break;
7766 }
7767 }
7768
7769 return S_OK;
7770}
7771
7772/**
7773 * Helper to initialize all associated child objects and allocate data
7774 * structures.
7775 *
7776 * This method must be called as a part of the object's initialization procedure
7777 * (usually done in the #init() method).
7778 *
7779 * @note Must be called only from #init() or from #registeredInit().
7780 */
7781HRESULT Machine::initDataAndChildObjects()
7782{
7783 AutoCaller autoCaller(this);
7784 AssertComRCReturnRC(autoCaller.rc());
7785 AssertComRCReturn(autoCaller.state() == InInit ||
7786 autoCaller.state() == Limited, E_FAIL);
7787
7788 AssertReturn(!mData->mAccessible, E_FAIL);
7789
7790 /* allocate data structures */
7791 mSSData.allocate();
7792 mUserData.allocate();
7793 mHWData.allocate();
7794 mMediaData.allocate();
7795 mStorageControllers.allocate();
7796
7797 /* initialize mOSTypeId */
7798 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
7799
7800 /* create associated BIOS settings object */
7801 unconst(mBIOSSettings).createObject();
7802 mBIOSSettings->init(this);
7803
7804 /* create an associated VRDE object (default is disabled) */
7805 unconst(mVRDEServer).createObject();
7806 mVRDEServer->init(this);
7807
7808 /* create associated serial port objects */
7809 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
7810 {
7811 unconst(mSerialPorts[slot]).createObject();
7812 mSerialPorts[slot]->init(this, slot);
7813 }
7814
7815 /* create associated parallel port objects */
7816 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7817 {
7818 unconst(mParallelPorts[slot]).createObject();
7819 mParallelPorts[slot]->init(this, slot);
7820 }
7821
7822 /* create the audio adapter object (always present, default is disabled) */
7823 unconst(mAudioAdapter).createObject();
7824 mAudioAdapter->init(this);
7825
7826 /* create the USB controller object (always present, default is disabled) */
7827 unconst(mUSBController).createObject();
7828 mUSBController->init(this);
7829
7830 /* create associated network adapter objects */
7831 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
7832 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7833 {
7834 unconst(mNetworkAdapters[slot]).createObject();
7835 mNetworkAdapters[slot]->init(this, slot);
7836 }
7837
7838 /* create the bandwidth control */
7839 unconst(mBandwidthControl).createObject();
7840 mBandwidthControl->init(this);
7841
7842 return S_OK;
7843}
7844
7845/**
7846 * Helper to uninitialize all associated child objects and to free all data
7847 * structures.
7848 *
7849 * This method must be called as a part of the object's uninitialization
7850 * procedure (usually done in the #uninit() method).
7851 *
7852 * @note Must be called only from #uninit() or from #registeredInit().
7853 */
7854void Machine::uninitDataAndChildObjects()
7855{
7856 AutoCaller autoCaller(this);
7857 AssertComRCReturnVoid(autoCaller.rc());
7858 AssertComRCReturnVoid( autoCaller.state() == InUninit
7859 || autoCaller.state() == Limited);
7860
7861 /* tell all our other child objects we've been uninitialized */
7862 if (mBandwidthControl)
7863 {
7864 mBandwidthControl->uninit();
7865 unconst(mBandwidthControl).setNull();
7866 }
7867
7868 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7869 {
7870 if (mNetworkAdapters[slot])
7871 {
7872 mNetworkAdapters[slot]->uninit();
7873 unconst(mNetworkAdapters[slot]).setNull();
7874 }
7875 }
7876
7877 if (mUSBController)
7878 {
7879 mUSBController->uninit();
7880 unconst(mUSBController).setNull();
7881 }
7882
7883 if (mAudioAdapter)
7884 {
7885 mAudioAdapter->uninit();
7886 unconst(mAudioAdapter).setNull();
7887 }
7888
7889 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7890 {
7891 if (mParallelPorts[slot])
7892 {
7893 mParallelPorts[slot]->uninit();
7894 unconst(mParallelPorts[slot]).setNull();
7895 }
7896 }
7897
7898 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
7899 {
7900 if (mSerialPorts[slot])
7901 {
7902 mSerialPorts[slot]->uninit();
7903 unconst(mSerialPorts[slot]).setNull();
7904 }
7905 }
7906
7907 if (mVRDEServer)
7908 {
7909 mVRDEServer->uninit();
7910 unconst(mVRDEServer).setNull();
7911 }
7912
7913 if (mBIOSSettings)
7914 {
7915 mBIOSSettings->uninit();
7916 unconst(mBIOSSettings).setNull();
7917 }
7918
7919 /* Deassociate hard disks (only when a real Machine or a SnapshotMachine
7920 * instance is uninitialized; SessionMachine instances refer to real
7921 * Machine hard disks). This is necessary for a clean re-initialization of
7922 * the VM after successfully re-checking the accessibility state. Note
7923 * that in case of normal Machine or SnapshotMachine uninitialization (as
7924 * a result of unregistering or deleting the snapshot), outdated hard
7925 * disk attachments will already be uninitialized and deleted, so this
7926 * code will not affect them. */
7927 if ( !!mMediaData
7928 && (!isSessionMachine())
7929 )
7930 {
7931 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
7932 it != mMediaData->mAttachments.end();
7933 ++it)
7934 {
7935 ComObjPtr<Medium> hd = (*it)->getMedium();
7936 if (hd.isNull())
7937 continue;
7938 HRESULT rc = hd->removeBackReference(mData->mUuid, getSnapshotId());
7939 AssertComRC(rc);
7940 }
7941 }
7942
7943 if (!isSessionMachine() && !isSnapshotMachine())
7944 {
7945 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
7946 if (mData->mFirstSnapshot)
7947 {
7948 // snapshots tree is protected by media write lock; strictly
7949 // this isn't necessary here since we're deleting the entire
7950 // machine, but otherwise we assert in Snapshot::uninit()
7951 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7952 mData->mFirstSnapshot->uninit();
7953 mData->mFirstSnapshot.setNull();
7954 }
7955
7956 mData->mCurrentSnapshot.setNull();
7957 }
7958
7959 /* free data structures (the essential mData structure is not freed here
7960 * since it may be still in use) */
7961 mMediaData.free();
7962 mStorageControllers.free();
7963 mHWData.free();
7964 mUserData.free();
7965 mSSData.free();
7966}
7967
7968/**
7969 * Returns a pointer to the Machine object for this machine that acts like a
7970 * parent for complex machine data objects such as shared folders, etc.
7971 *
7972 * For primary Machine objects and for SnapshotMachine objects, returns this
7973 * object's pointer itself. For SessionMachine objects, returns the peer
7974 * (primary) machine pointer.
7975 */
7976Machine* Machine::getMachine()
7977{
7978 if (isSessionMachine())
7979 return (Machine*)mPeer;
7980 return this;
7981}
7982
7983/**
7984 * Makes sure that there are no machine state dependents. If necessary, waits
7985 * for the number of dependents to drop to zero.
7986 *
7987 * Make sure this method is called from under this object's write lock to
7988 * guarantee that no new dependents may be added when this method returns
7989 * control to the caller.
7990 *
7991 * @note Locks this object for writing. The lock will be released while waiting
7992 * (if necessary).
7993 *
7994 * @warning To be used only in methods that change the machine state!
7995 */
7996void Machine::ensureNoStateDependencies()
7997{
7998 AssertReturnVoid(isWriteLockOnCurrentThread());
7999
8000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8001
8002 /* Wait for all state dependents if necessary */
8003 if (mData->mMachineStateDeps != 0)
8004 {
8005 /* lazy semaphore creation */
8006 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8007 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8008
8009 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8010 mData->mMachineStateDeps));
8011
8012 ++mData->mMachineStateChangePending;
8013
8014 /* reset the semaphore before waiting, the last dependent will signal
8015 * it */
8016 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8017
8018 alock.release();
8019
8020 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8021
8022 alock.acquire();
8023
8024 -- mData->mMachineStateChangePending;
8025 }
8026}
8027
8028/**
8029 * Changes the machine state and informs callbacks.
8030 *
8031 * This method is not intended to fail so it either returns S_OK or asserts (and
8032 * returns a failure).
8033 *
8034 * @note Locks this object for writing.
8035 */
8036HRESULT Machine::setMachineState(MachineState_T aMachineState)
8037{
8038 LogFlowThisFuncEnter();
8039 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8040
8041 AutoCaller autoCaller(this);
8042 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8043
8044 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8045
8046 /* wait for state dependents to drop to zero */
8047 ensureNoStateDependencies();
8048
8049 if (mData->mMachineState != aMachineState)
8050 {
8051 mData->mMachineState = aMachineState;
8052
8053 RTTimeNow(&mData->mLastStateChange);
8054
8055 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8056 }
8057
8058 LogFlowThisFuncLeave();
8059 return S_OK;
8060}
8061
8062/**
8063 * Searches for a shared folder with the given logical name
8064 * in the collection of shared folders.
8065 *
8066 * @param aName logical name of the shared folder
8067 * @param aSharedFolder where to return the found object
8068 * @param aSetError whether to set the error info if the folder is
8069 * not found
8070 * @return
8071 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8072 *
8073 * @note
8074 * must be called from under the object's lock!
8075 */
8076HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8077 ComObjPtr<SharedFolder> &aSharedFolder,
8078 bool aSetError /* = false */)
8079{
8080 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8081 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8082 it != mHWData->mSharedFolders.end();
8083 ++it)
8084 {
8085 SharedFolder *pSF = *it;
8086 AutoCaller autoCaller(pSF);
8087 if (pSF->getName() == aName)
8088 {
8089 aSharedFolder = pSF;
8090 rc = S_OK;
8091 break;
8092 }
8093 }
8094
8095 if (aSetError && FAILED(rc))
8096 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8097
8098 return rc;
8099}
8100
8101/**
8102 * Initializes all machine instance data from the given settings structures
8103 * from XML. The exception is the machine UUID which needs special handling
8104 * depending on the caller's use case, so the caller needs to set that herself.
8105 *
8106 * This gets called in several contexts during machine initialization:
8107 *
8108 * -- When machine XML exists on disk already and needs to be loaded into memory,
8109 * for example, from registeredInit() to load all registered machines on
8110 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8111 * attached to the machine should be part of some media registry already.
8112 *
8113 * -- During OVF import, when a machine config has been constructed from an
8114 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8115 * ensure that the media listed as attachments in the config (which have
8116 * been imported from the OVF) receive the correct registry ID.
8117 *
8118 * -- During VM cloning.
8119 *
8120 * @param config Machine settings from XML.
8121 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8122 * @return
8123 */
8124HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8125 const Guid *puuidRegistry)
8126{
8127 // copy name, description, OS type, teleporter, UTC etc.
8128 mUserData->s = config.machineUserData;
8129
8130 // look up the object by Id to check it is valid
8131 ComPtr<IGuestOSType> guestOSType;
8132 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8133 guestOSType.asOutParam());
8134 if (FAILED(rc)) return rc;
8135
8136 // stateFile (optional)
8137 if (config.strStateFile.isEmpty())
8138 mSSData->strStateFilePath.setNull();
8139 else
8140 {
8141 Utf8Str stateFilePathFull(config.strStateFile);
8142 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8143 if (RT_FAILURE(vrc))
8144 return setError(E_FAIL,
8145 tr("Invalid saved state file path '%s' (%Rrc)"),
8146 config.strStateFile.c_str(),
8147 vrc);
8148 mSSData->strStateFilePath = stateFilePathFull;
8149 }
8150
8151 // snapshot folder needs special processing so set it again
8152 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8153 if (FAILED(rc)) return rc;
8154
8155 /* Copy the extra data items (Not in any case config is already the same as
8156 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8157 * make sure the extra data map is copied). */
8158 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8159
8160 /* currentStateModified (optional, default is true) */
8161 mData->mCurrentStateModified = config.fCurrentStateModified;
8162
8163 mData->mLastStateChange = config.timeLastStateChange;
8164
8165 /*
8166 * note: all mUserData members must be assigned prior this point because
8167 * we need to commit changes in order to let mUserData be shared by all
8168 * snapshot machine instances.
8169 */
8170 mUserData.commitCopy();
8171
8172 // machine registry, if present (must be loaded before snapshots)
8173 if (config.canHaveOwnMediaRegistry())
8174 {
8175 // determine machine folder
8176 Utf8Str strMachineFolder = getSettingsFileFull();
8177 strMachineFolder.stripFilename();
8178 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8179 config.mediaRegistry,
8180 strMachineFolder);
8181 if (FAILED(rc)) return rc;
8182 }
8183
8184 /* Snapshot node (optional) */
8185 size_t cRootSnapshots;
8186 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8187 {
8188 // there must be only one root snapshot
8189 Assert(cRootSnapshots == 1);
8190
8191 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8192
8193 rc = loadSnapshot(snap,
8194 config.uuidCurrentSnapshot,
8195 NULL); // no parent == first snapshot
8196 if (FAILED(rc)) return rc;
8197 }
8198
8199 // hardware data
8200 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8201 if (FAILED(rc)) return rc;
8202
8203 // load storage controllers
8204 rc = loadStorageControllers(config.storageMachine,
8205 puuidRegistry,
8206 NULL /* puuidSnapshot */);
8207 if (FAILED(rc)) return rc;
8208
8209 /*
8210 * NOTE: the assignment below must be the last thing to do,
8211 * otherwise it will be not possible to change the settings
8212 * somewhere in the code above because all setters will be
8213 * blocked by checkStateDependency(MutableStateDep).
8214 */
8215
8216 /* set the machine state to Aborted or Saved when appropriate */
8217 if (config.fAborted)
8218 {
8219 mSSData->strStateFilePath.setNull();
8220
8221 /* no need to use setMachineState() during init() */
8222 mData->mMachineState = MachineState_Aborted;
8223 }
8224 else if (!mSSData->strStateFilePath.isEmpty())
8225 {
8226 /* no need to use setMachineState() during init() */
8227 mData->mMachineState = MachineState_Saved;
8228 }
8229
8230 // after loading settings, we are no longer different from the XML on disk
8231 mData->flModifications = 0;
8232
8233 return S_OK;
8234}
8235
8236/**
8237 * Recursively loads all snapshots starting from the given.
8238 *
8239 * @param aNode <Snapshot> node.
8240 * @param aCurSnapshotId Current snapshot ID from the settings file.
8241 * @param aParentSnapshot Parent snapshot.
8242 */
8243HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8244 const Guid &aCurSnapshotId,
8245 Snapshot *aParentSnapshot)
8246{
8247 AssertReturn(!isSnapshotMachine(), E_FAIL);
8248 AssertReturn(!isSessionMachine(), E_FAIL);
8249
8250 HRESULT rc = S_OK;
8251
8252 Utf8Str strStateFile;
8253 if (!data.strStateFile.isEmpty())
8254 {
8255 /* optional */
8256 strStateFile = data.strStateFile;
8257 int vrc = calculateFullPath(strStateFile, strStateFile);
8258 if (RT_FAILURE(vrc))
8259 return setError(E_FAIL,
8260 tr("Invalid saved state file path '%s' (%Rrc)"),
8261 strStateFile.c_str(),
8262 vrc);
8263 }
8264
8265 /* create a snapshot machine object */
8266 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8267 pSnapshotMachine.createObject();
8268 rc = pSnapshotMachine->initFromSettings(this,
8269 data.hardware,
8270 &data.debugging,
8271 &data.autostart,
8272 data.storage,
8273 data.uuid.ref(),
8274 strStateFile);
8275 if (FAILED(rc)) return rc;
8276
8277 /* create a snapshot object */
8278 ComObjPtr<Snapshot> pSnapshot;
8279 pSnapshot.createObject();
8280 /* initialize the snapshot */
8281 rc = pSnapshot->init(mParent, // VirtualBox object
8282 data.uuid,
8283 data.strName,
8284 data.strDescription,
8285 data.timestamp,
8286 pSnapshotMachine,
8287 aParentSnapshot);
8288 if (FAILED(rc)) return rc;
8289
8290 /* memorize the first snapshot if necessary */
8291 if (!mData->mFirstSnapshot)
8292 mData->mFirstSnapshot = pSnapshot;
8293
8294 /* memorize the current snapshot when appropriate */
8295 if ( !mData->mCurrentSnapshot
8296 && pSnapshot->getId() == aCurSnapshotId
8297 )
8298 mData->mCurrentSnapshot = pSnapshot;
8299
8300 // now create the children
8301 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8302 it != data.llChildSnapshots.end();
8303 ++it)
8304 {
8305 const settings::Snapshot &childData = *it;
8306 // recurse
8307 rc = loadSnapshot(childData,
8308 aCurSnapshotId,
8309 pSnapshot); // parent = the one we created above
8310 if (FAILED(rc)) return rc;
8311 }
8312
8313 return rc;
8314}
8315
8316/**
8317 * Loads settings into mHWData.
8318 *
8319 * @param data Reference to the hardware settings.
8320 * @param pDbg Pointer to the debugging settings.
8321 * @param pAutostart Pointer to the autostart settings.
8322 */
8323HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8324 const settings::Autostart *pAutostart)
8325{
8326 AssertReturn(!isSessionMachine(), E_FAIL);
8327
8328 HRESULT rc = S_OK;
8329
8330 try
8331 {
8332 /* The hardware version attribute (optional). */
8333 mHWData->mHWVersion = data.strVersion;
8334 mHWData->mHardwareUUID = data.uuid;
8335
8336 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8337 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8338 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8339 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8340 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8341 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8342 mHWData->mPAEEnabled = data.fPAE;
8343 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8344
8345 mHWData->mCPUCount = data.cCPUs;
8346 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8347 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8348
8349 // cpu
8350 if (mHWData->mCPUHotPlugEnabled)
8351 {
8352 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8353 it != data.llCpus.end();
8354 ++it)
8355 {
8356 const settings::Cpu &cpu = *it;
8357
8358 mHWData->mCPUAttached[cpu.ulId] = true;
8359 }
8360 }
8361
8362 // cpuid leafs
8363 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8364 it != data.llCpuIdLeafs.end();
8365 ++it)
8366 {
8367 const settings::CpuIdLeaf &leaf = *it;
8368
8369 switch (leaf.ulId)
8370 {
8371 case 0x0:
8372 case 0x1:
8373 case 0x2:
8374 case 0x3:
8375 case 0x4:
8376 case 0x5:
8377 case 0x6:
8378 case 0x7:
8379 case 0x8:
8380 case 0x9:
8381 case 0xA:
8382 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8383 break;
8384
8385 case 0x80000000:
8386 case 0x80000001:
8387 case 0x80000002:
8388 case 0x80000003:
8389 case 0x80000004:
8390 case 0x80000005:
8391 case 0x80000006:
8392 case 0x80000007:
8393 case 0x80000008:
8394 case 0x80000009:
8395 case 0x8000000A:
8396 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8397 break;
8398
8399 default:
8400 /* just ignore */
8401 break;
8402 }
8403 }
8404
8405 mHWData->mMemorySize = data.ulMemorySizeMB;
8406 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8407
8408 // boot order
8409 for (size_t i = 0;
8410 i < RT_ELEMENTS(mHWData->mBootOrder);
8411 i++)
8412 {
8413 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8414 if (it == data.mapBootOrder.end())
8415 mHWData->mBootOrder[i] = DeviceType_Null;
8416 else
8417 mHWData->mBootOrder[i] = it->second;
8418 }
8419
8420 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8421 mHWData->mMonitorCount = data.cMonitors;
8422 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8423 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8424 mHWData->mFirmwareType = data.firmwareType;
8425 mHWData->mPointingHIDType = data.pointingHIDType;
8426 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8427 mHWData->mChipsetType = data.chipsetType;
8428 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8429 mHWData->mHPETEnabled = data.fHPETEnabled;
8430
8431 /* VRDEServer */
8432 rc = mVRDEServer->loadSettings(data.vrdeSettings);
8433 if (FAILED(rc)) return rc;
8434
8435 /* BIOS */
8436 rc = mBIOSSettings->loadSettings(data.biosSettings);
8437 if (FAILED(rc)) return rc;
8438
8439 // Bandwidth control (must come before network adapters)
8440 rc = mBandwidthControl->loadSettings(data.ioSettings);
8441 if (FAILED(rc)) return rc;
8442
8443 /* USB Controller */
8444 rc = mUSBController->loadSettings(data.usbController);
8445 if (FAILED(rc)) return rc;
8446
8447 // network adapters
8448 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8449 uint32_t oldCount = mNetworkAdapters.size();
8450 if (newCount > oldCount)
8451 {
8452 mNetworkAdapters.resize(newCount);
8453 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
8454 {
8455 unconst(mNetworkAdapters[slot]).createObject();
8456 mNetworkAdapters[slot]->init(this, slot);
8457 }
8458 }
8459 else if (newCount < oldCount)
8460 mNetworkAdapters.resize(newCount);
8461 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8462 it != data.llNetworkAdapters.end();
8463 ++it)
8464 {
8465 const settings::NetworkAdapter &nic = *it;
8466
8467 /* slot unicity is guaranteed by XML Schema */
8468 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8469 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
8470 if (FAILED(rc)) return rc;
8471 }
8472
8473 // serial ports
8474 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8475 it != data.llSerialPorts.end();
8476 ++it)
8477 {
8478 const settings::SerialPort &s = *it;
8479
8480 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8481 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
8482 if (FAILED(rc)) return rc;
8483 }
8484
8485 // parallel ports (optional)
8486 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8487 it != data.llParallelPorts.end();
8488 ++it)
8489 {
8490 const settings::ParallelPort &p = *it;
8491
8492 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8493 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
8494 if (FAILED(rc)) return rc;
8495 }
8496
8497 /* AudioAdapter */
8498 rc = mAudioAdapter->loadSettings(data.audioAdapter);
8499 if (FAILED(rc)) return rc;
8500
8501 /* Shared folders */
8502 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8503 it != data.llSharedFolders.end();
8504 ++it)
8505 {
8506 const settings::SharedFolder &sf = *it;
8507
8508 ComObjPtr<SharedFolder> sharedFolder;
8509 /* Check for double entries. Not allowed! */
8510 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8511 if (SUCCEEDED(rc))
8512 return setError(VBOX_E_OBJECT_IN_USE,
8513 tr("Shared folder named '%s' already exists"),
8514 sf.strName.c_str());
8515
8516 /* Create the new shared folder. Don't break on error. This will be
8517 * reported when the machine starts. */
8518 sharedFolder.createObject();
8519 rc = sharedFolder->init(getMachine(),
8520 sf.strName,
8521 sf.strHostPath,
8522 RT_BOOL(sf.fWritable),
8523 RT_BOOL(sf.fAutoMount),
8524 false /* fFailOnError */);
8525 if (FAILED(rc)) return rc;
8526 mHWData->mSharedFolders.push_back(sharedFolder);
8527 }
8528
8529 // Clipboard
8530 mHWData->mClipboardMode = data.clipboardMode;
8531
8532 // drag'n'drop
8533 mHWData->mDragAndDropMode = data.dragAndDropMode;
8534
8535 // guest settings
8536 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8537
8538 // IO settings
8539 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8540 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8541
8542 // Host PCI devices
8543 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8544 it != data.pciAttachments.end();
8545 ++it)
8546 {
8547 const settings::HostPCIDeviceAttachment &hpda = *it;
8548 ComObjPtr<PCIDeviceAttachment> pda;
8549
8550 pda.createObject();
8551 pda->loadSettings(this, hpda);
8552 mHWData->mPCIDeviceAssignments.push_back(pda);
8553 }
8554
8555 /*
8556 * (The following isn't really real hardware, but it lives in HWData
8557 * for reasons of convenience.)
8558 */
8559
8560#ifdef VBOX_WITH_GUEST_PROPS
8561 /* Guest properties (optional) */
8562 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8563 it != data.llGuestProperties.end();
8564 ++it)
8565 {
8566 const settings::GuestProperty &prop = *it;
8567 uint32_t fFlags = guestProp::NILFLAG;
8568 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8569 HWData::GuestProperty property = { prop.strName, prop.strValue, (LONG64) prop.timestamp, fFlags };
8570 mHWData->mGuestProperties.push_back(property);
8571 }
8572
8573 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8574#endif /* VBOX_WITH_GUEST_PROPS defined */
8575
8576 rc = loadDebugging(pDbg);
8577 if (FAILED(rc))
8578 return rc;
8579
8580 mHWData->mAutostart = *pAutostart;
8581 }
8582 catch(std::bad_alloc &)
8583 {
8584 return E_OUTOFMEMORY;
8585 }
8586
8587 AssertComRC(rc);
8588 return rc;
8589}
8590
8591/**
8592 * Called from Machine::loadHardware() to load the debugging settings of the
8593 * machine.
8594 *
8595 * @param pDbg Pointer to the settings.
8596 */
8597HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
8598{
8599 mHWData->mDebugging = *pDbg;
8600 /* no more processing currently required, this will probably change. */
8601 return S_OK;
8602}
8603
8604/**
8605 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
8606 *
8607 * @param data
8608 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8609 * @param puuidSnapshot
8610 * @return
8611 */
8612HRESULT Machine::loadStorageControllers(const settings::Storage &data,
8613 const Guid *puuidRegistry,
8614 const Guid *puuidSnapshot)
8615{
8616 AssertReturn(!isSessionMachine(), E_FAIL);
8617
8618 HRESULT rc = S_OK;
8619
8620 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8621 it != data.llStorageControllers.end();
8622 ++it)
8623 {
8624 const settings::StorageController &ctlData = *it;
8625
8626 ComObjPtr<StorageController> pCtl;
8627 /* Try to find one with the name first. */
8628 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8629 if (SUCCEEDED(rc))
8630 return setError(VBOX_E_OBJECT_IN_USE,
8631 tr("Storage controller named '%s' already exists"),
8632 ctlData.strName.c_str());
8633
8634 pCtl.createObject();
8635 rc = pCtl->init(this,
8636 ctlData.strName,
8637 ctlData.storageBus,
8638 ctlData.ulInstance,
8639 ctlData.fBootable);
8640 if (FAILED(rc)) return rc;
8641
8642 mStorageControllers->push_back(pCtl);
8643
8644 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8645 if (FAILED(rc)) return rc;
8646
8647 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8648 if (FAILED(rc)) return rc;
8649
8650 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8651 if (FAILED(rc)) return rc;
8652
8653 /* Set IDE emulation settings (only for AHCI controller). */
8654 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8655 {
8656 if ( (FAILED(rc = pCtl->SetIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8657 || (FAILED(rc = pCtl->SetIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8658 || (FAILED(rc = pCtl->SetIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8659 || (FAILED(rc = pCtl->SetIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8660 )
8661 return rc;
8662 }
8663
8664 /* Load the attached devices now. */
8665 rc = loadStorageDevices(pCtl,
8666 ctlData,
8667 puuidRegistry,
8668 puuidSnapshot);
8669 if (FAILED(rc)) return rc;
8670 }
8671
8672 return S_OK;
8673}
8674
8675/**
8676 * Called from loadStorageControllers for a controller's devices.
8677 *
8678 * @param aStorageController
8679 * @param data
8680 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8681 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
8682 * @return
8683 */
8684HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
8685 const settings::StorageController &data,
8686 const Guid *puuidRegistry,
8687 const Guid *puuidSnapshot)
8688{
8689 HRESULT rc = S_OK;
8690
8691 /* paranoia: detect duplicate attachments */
8692 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8693 it != data.llAttachedDevices.end();
8694 ++it)
8695 {
8696 const settings::AttachedDevice &ad = *it;
8697
8698 for (settings::AttachedDevicesList::const_iterator it2 = it;
8699 it2 != data.llAttachedDevices.end();
8700 ++it2)
8701 {
8702 if (it == it2)
8703 continue;
8704
8705 const settings::AttachedDevice &ad2 = *it2;
8706
8707 if ( ad.lPort == ad2.lPort
8708 && ad.lDevice == ad2.lDevice)
8709 {
8710 return setError(E_FAIL,
8711 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
8712 aStorageController->getName().c_str(),
8713 ad.lPort,
8714 ad.lDevice,
8715 mUserData->s.strName.c_str());
8716 }
8717 }
8718 }
8719
8720 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8721 it != data.llAttachedDevices.end();
8722 ++it)
8723 {
8724 const settings::AttachedDevice &dev = *it;
8725 ComObjPtr<Medium> medium;
8726
8727 switch (dev.deviceType)
8728 {
8729 case DeviceType_Floppy:
8730 case DeviceType_DVD:
8731 if (dev.strHostDriveSrc.isNotEmpty())
8732 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
8733 else
8734 rc = mParent->findRemoveableMedium(dev.deviceType,
8735 dev.uuid,
8736 false /* fRefresh */,
8737 false /* aSetError */,
8738 medium);
8739 if (rc == VBOX_E_OBJECT_NOT_FOUND)
8740 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
8741 rc = S_OK;
8742 break;
8743
8744 case DeviceType_HardDisk:
8745 {
8746 /* find a hard disk by UUID */
8747 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
8748 if (FAILED(rc))
8749 {
8750 if (isSnapshotMachine())
8751 {
8752 // wrap another error message around the "cannot find hard disk" set by findHardDisk
8753 // so the user knows that the bad disk is in a snapshot somewhere
8754 com::ErrorInfo info;
8755 return setError(E_FAIL,
8756 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
8757 puuidSnapshot->raw(),
8758 info.getText().raw());
8759 }
8760 else
8761 return rc;
8762 }
8763
8764 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
8765
8766 if (medium->getType() == MediumType_Immutable)
8767 {
8768 if (isSnapshotMachine())
8769 return setError(E_FAIL,
8770 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8771 "of the virtual machine '%s' ('%s')"),
8772 medium->getLocationFull().c_str(),
8773 dev.uuid.raw(),
8774 puuidSnapshot->raw(),
8775 mUserData->s.strName.c_str(),
8776 mData->m_strConfigFileFull.c_str());
8777
8778 return setError(E_FAIL,
8779 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8780 medium->getLocationFull().c_str(),
8781 dev.uuid.raw(),
8782 mUserData->s.strName.c_str(),
8783 mData->m_strConfigFileFull.c_str());
8784 }
8785
8786 if (medium->getType() == MediumType_MultiAttach)
8787 {
8788 if (isSnapshotMachine())
8789 return setError(E_FAIL,
8790 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8791 "of the virtual machine '%s' ('%s')"),
8792 medium->getLocationFull().c_str(),
8793 dev.uuid.raw(),
8794 puuidSnapshot->raw(),
8795 mUserData->s.strName.c_str(),
8796 mData->m_strConfigFileFull.c_str());
8797
8798 return setError(E_FAIL,
8799 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8800 medium->getLocationFull().c_str(),
8801 dev.uuid.raw(),
8802 mUserData->s.strName.c_str(),
8803 mData->m_strConfigFileFull.c_str());
8804 }
8805
8806 if ( !isSnapshotMachine()
8807 && medium->getChildren().size() != 0
8808 )
8809 return setError(E_FAIL,
8810 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
8811 "because it has %d differencing child hard disks"),
8812 medium->getLocationFull().c_str(),
8813 dev.uuid.raw(),
8814 mUserData->s.strName.c_str(),
8815 mData->m_strConfigFileFull.c_str(),
8816 medium->getChildren().size());
8817
8818 if (findAttachment(mMediaData->mAttachments,
8819 medium))
8820 return setError(E_FAIL,
8821 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
8822 medium->getLocationFull().c_str(),
8823 dev.uuid.raw(),
8824 mUserData->s.strName.c_str(),
8825 mData->m_strConfigFileFull.c_str());
8826
8827 break;
8828 }
8829
8830 default:
8831 return setError(E_FAIL,
8832 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
8833 medium->getLocationFull().c_str(),
8834 mUserData->s.strName.c_str(),
8835 mData->m_strConfigFileFull.c_str());
8836 }
8837
8838 if (FAILED(rc))
8839 break;
8840
8841 /* Bandwidth groups are loaded at this point. */
8842 ComObjPtr<BandwidthGroup> pBwGroup;
8843
8844 if (!dev.strBwGroup.isEmpty())
8845 {
8846 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
8847 if (FAILED(rc))
8848 return setError(E_FAIL,
8849 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
8850 medium->getLocationFull().c_str(),
8851 dev.strBwGroup.c_str(),
8852 mUserData->s.strName.c_str(),
8853 mData->m_strConfigFileFull.c_str());
8854 pBwGroup->reference();
8855 }
8856
8857 const Bstr controllerName = aStorageController->getName();
8858 ComObjPtr<MediumAttachment> pAttachment;
8859 pAttachment.createObject();
8860 rc = pAttachment->init(this,
8861 medium,
8862 controllerName,
8863 dev.lPort,
8864 dev.lDevice,
8865 dev.deviceType,
8866 false,
8867 dev.fPassThrough,
8868 dev.fTempEject,
8869 dev.fNonRotational,
8870 dev.fDiscard,
8871 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
8872 if (FAILED(rc)) break;
8873
8874 /* associate the medium with this machine and snapshot */
8875 if (!medium.isNull())
8876 {
8877 AutoCaller medCaller(medium);
8878 if (FAILED(medCaller.rc())) return medCaller.rc();
8879 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
8880
8881 if (isSnapshotMachine())
8882 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
8883 else
8884 rc = medium->addBackReference(mData->mUuid);
8885 /* If the medium->addBackReference fails it sets an appropriate
8886 * error message, so no need to do any guesswork here. */
8887
8888 if (puuidRegistry)
8889 // caller wants registry ID to be set on all attached media (OVF import case)
8890 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
8891 }
8892
8893 if (FAILED(rc))
8894 break;
8895
8896 /* back up mMediaData to let registeredInit() properly rollback on failure
8897 * (= limited accessibility) */
8898 setModified(IsModified_Storage);
8899 mMediaData.backup();
8900 mMediaData->mAttachments.push_back(pAttachment);
8901 }
8902
8903 return rc;
8904}
8905
8906/**
8907 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
8908 *
8909 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
8910 * @param aSnapshot where to return the found snapshot
8911 * @param aSetError true to set extended error info on failure
8912 */
8913HRESULT Machine::findSnapshotById(const Guid &aId,
8914 ComObjPtr<Snapshot> &aSnapshot,
8915 bool aSetError /* = false */)
8916{
8917 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
8918
8919 if (!mData->mFirstSnapshot)
8920 {
8921 if (aSetError)
8922 return setError(E_FAIL, tr("This machine does not have any snapshots"));
8923 return E_FAIL;
8924 }
8925
8926 if (aId.isEmpty())
8927 aSnapshot = mData->mFirstSnapshot;
8928 else
8929 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
8930
8931 if (!aSnapshot)
8932 {
8933 if (aSetError)
8934 return setError(E_FAIL,
8935 tr("Could not find a snapshot with UUID {%s}"),
8936 aId.toString().c_str());
8937 return E_FAIL;
8938 }
8939
8940 return S_OK;
8941}
8942
8943/**
8944 * Returns the snapshot with the given name or fails of no such snapshot.
8945 *
8946 * @param aName snapshot name to find
8947 * @param aSnapshot where to return the found snapshot
8948 * @param aSetError true to set extended error info on failure
8949 */
8950HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
8951 ComObjPtr<Snapshot> &aSnapshot,
8952 bool aSetError /* = false */)
8953{
8954 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
8955
8956 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
8957
8958 if (!mData->mFirstSnapshot)
8959 {
8960 if (aSetError)
8961 return setError(VBOX_E_OBJECT_NOT_FOUND,
8962 tr("This machine does not have any snapshots"));
8963 return VBOX_E_OBJECT_NOT_FOUND;
8964 }
8965
8966 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
8967
8968 if (!aSnapshot)
8969 {
8970 if (aSetError)
8971 return setError(VBOX_E_OBJECT_NOT_FOUND,
8972 tr("Could not find a snapshot named '%s'"), strName.c_str());
8973 return VBOX_E_OBJECT_NOT_FOUND;
8974 }
8975
8976 return S_OK;
8977}
8978
8979/**
8980 * Returns a storage controller object with the given name.
8981 *
8982 * @param aName storage controller name to find
8983 * @param aStorageController where to return the found storage controller
8984 * @param aSetError true to set extended error info on failure
8985 */
8986HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
8987 ComObjPtr<StorageController> &aStorageController,
8988 bool aSetError /* = false */)
8989{
8990 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
8991
8992 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
8993 it != mStorageControllers->end();
8994 ++it)
8995 {
8996 if ((*it)->getName() == aName)
8997 {
8998 aStorageController = (*it);
8999 return S_OK;
9000 }
9001 }
9002
9003 if (aSetError)
9004 return setError(VBOX_E_OBJECT_NOT_FOUND,
9005 tr("Could not find a storage controller named '%s'"),
9006 aName.c_str());
9007 return VBOX_E_OBJECT_NOT_FOUND;
9008}
9009
9010HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9011 MediaData::AttachmentList &atts)
9012{
9013 AutoCaller autoCaller(this);
9014 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9015
9016 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9017
9018 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9019 it != mMediaData->mAttachments.end();
9020 ++it)
9021 {
9022 const ComObjPtr<MediumAttachment> &pAtt = *it;
9023
9024 // should never happen, but deal with NULL pointers in the list.
9025 AssertStmt(!pAtt.isNull(), continue);
9026
9027 // getControllerName() needs caller+read lock
9028 AutoCaller autoAttCaller(pAtt);
9029 if (FAILED(autoAttCaller.rc()))
9030 {
9031 atts.clear();
9032 return autoAttCaller.rc();
9033 }
9034 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9035
9036 if (pAtt->getControllerName() == aName)
9037 atts.push_back(pAtt);
9038 }
9039
9040 return S_OK;
9041}
9042
9043/**
9044 * Helper for #saveSettings. Cares about renaming the settings directory and
9045 * file if the machine name was changed and about creating a new settings file
9046 * if this is a new machine.
9047 *
9048 * @note Must be never called directly but only from #saveSettings().
9049 */
9050HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9051{
9052 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9053
9054 HRESULT rc = S_OK;
9055
9056 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9057
9058 /// @todo need to handle primary group change, too
9059
9060 /* attempt to rename the settings file if machine name is changed */
9061 if ( mUserData->s.fNameSync
9062 && mUserData.isBackedUp()
9063 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9064 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9065 )
9066 {
9067 bool dirRenamed = false;
9068 bool fileRenamed = false;
9069
9070 Utf8Str configFile, newConfigFile;
9071 Utf8Str configFilePrev, newConfigFilePrev;
9072 Utf8Str configDir, newConfigDir;
9073
9074 do
9075 {
9076 int vrc = VINF_SUCCESS;
9077
9078 Utf8Str name = mUserData.backedUpData()->s.strName;
9079 Utf8Str newName = mUserData->s.strName;
9080 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9081 if (group == "/")
9082 group.setNull();
9083 Utf8Str newGroup = mUserData->s.llGroups.front();
9084 if (newGroup == "/")
9085 newGroup.setNull();
9086
9087 configFile = mData->m_strConfigFileFull;
9088
9089 /* first, rename the directory if it matches the group and machine name */
9090 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9091 group.c_str(), RTPATH_DELIMITER, name.c_str());
9092 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9093 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9094 configDir = configFile;
9095 configDir.stripFilename();
9096 newConfigDir = configDir;
9097 if ( configDir.length() >= groupPlusName.length()
9098 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9099 {
9100 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9101 Utf8Str newConfigBaseDir(newConfigDir);
9102 newConfigDir.append(newGroupPlusName);
9103 /* consistency: use \ if appropriate on the platform */
9104 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9105 /* new dir and old dir cannot be equal here because of 'if'
9106 * above and because name != newName */
9107 Assert(configDir != newConfigDir);
9108 if (!fSettingsFileIsNew)
9109 {
9110 /* perform real rename only if the machine is not new */
9111 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9112 if ( vrc == VERR_FILE_NOT_FOUND
9113 || vrc == VERR_PATH_NOT_FOUND)
9114 {
9115 /* create the parent directory, then retry renaming */
9116 Utf8Str parent(newConfigDir);
9117 parent.stripFilename();
9118 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9119 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9120 }
9121 if (RT_FAILURE(vrc))
9122 {
9123 rc = setError(E_FAIL,
9124 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9125 configDir.c_str(),
9126 newConfigDir.c_str(),
9127 vrc);
9128 break;
9129 }
9130 /* delete subdirectories which are no longer needed */
9131 Utf8Str dir(configDir);
9132 dir.stripFilename();
9133 while (dir != newConfigBaseDir && dir != ".")
9134 {
9135 vrc = RTDirRemove(dir.c_str());
9136 if (RT_FAILURE(vrc))
9137 break;
9138 dir.stripFilename();
9139 }
9140 dirRenamed = true;
9141 }
9142 }
9143
9144 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9145 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9146
9147 /* then try to rename the settings file itself */
9148 if (newConfigFile != configFile)
9149 {
9150 /* get the path to old settings file in renamed directory */
9151 configFile = Utf8StrFmt("%s%c%s",
9152 newConfigDir.c_str(),
9153 RTPATH_DELIMITER,
9154 RTPathFilename(configFile.c_str()));
9155 if (!fSettingsFileIsNew)
9156 {
9157 /* perform real rename only if the machine is not new */
9158 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9159 if (RT_FAILURE(vrc))
9160 {
9161 rc = setError(E_FAIL,
9162 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9163 configFile.c_str(),
9164 newConfigFile.c_str(),
9165 vrc);
9166 break;
9167 }
9168 fileRenamed = true;
9169 configFilePrev = configFile;
9170 configFilePrev += "-prev";
9171 newConfigFilePrev = newConfigFile;
9172 newConfigFilePrev += "-prev";
9173 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9174 }
9175 }
9176
9177 // update m_strConfigFileFull amd mConfigFile
9178 mData->m_strConfigFileFull = newConfigFile;
9179 // compute the relative path too
9180 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9181
9182 // store the old and new so that VirtualBox::saveSettings() can update
9183 // the media registry
9184 if ( mData->mRegistered
9185 && configDir != newConfigDir)
9186 {
9187 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9188
9189 if (pfNeedsGlobalSaveSettings)
9190 *pfNeedsGlobalSaveSettings = true;
9191 }
9192
9193 // in the saved state file path, replace the old directory with the new directory
9194 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9195 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9196
9197 // and do the same thing for the saved state file paths of all the online snapshots
9198 if (mData->mFirstSnapshot)
9199 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9200 newConfigDir.c_str());
9201 }
9202 while (0);
9203
9204 if (FAILED(rc))
9205 {
9206 /* silently try to rename everything back */
9207 if (fileRenamed)
9208 {
9209 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9210 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9211 }
9212 if (dirRenamed)
9213 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9214 }
9215
9216 if (FAILED(rc)) return rc;
9217 }
9218
9219 if (fSettingsFileIsNew)
9220 {
9221 /* create a virgin config file */
9222 int vrc = VINF_SUCCESS;
9223
9224 /* ensure the settings directory exists */
9225 Utf8Str path(mData->m_strConfigFileFull);
9226 path.stripFilename();
9227 if (!RTDirExists(path.c_str()))
9228 {
9229 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9230 if (RT_FAILURE(vrc))
9231 {
9232 return setError(E_FAIL,
9233 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9234 path.c_str(),
9235 vrc);
9236 }
9237 }
9238
9239 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9240 path = Utf8Str(mData->m_strConfigFileFull);
9241 RTFILE f = NIL_RTFILE;
9242 vrc = RTFileOpen(&f, path.c_str(),
9243 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9244 if (RT_FAILURE(vrc))
9245 return setError(E_FAIL,
9246 tr("Could not create the settings file '%s' (%Rrc)"),
9247 path.c_str(),
9248 vrc);
9249 RTFileClose(f);
9250 }
9251
9252 return rc;
9253}
9254
9255/**
9256 * Saves and commits machine data, user data and hardware data.
9257 *
9258 * Note that on failure, the data remains uncommitted.
9259 *
9260 * @a aFlags may combine the following flags:
9261 *
9262 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9263 * Used when saving settings after an operation that makes them 100%
9264 * correspond to the settings from the current snapshot.
9265 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9266 * #isReallyModified() returns false. This is necessary for cases when we
9267 * change machine data directly, not through the backup()/commit() mechanism.
9268 * - SaveS_Force: settings will be saved without doing a deep compare of the
9269 * settings structures. This is used when this is called because snapshots
9270 * have changed to avoid the overhead of the deep compare.
9271 *
9272 * @note Must be called from under this object's write lock. Locks children for
9273 * writing.
9274 *
9275 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9276 * initialized to false and that will be set to true by this function if
9277 * the caller must invoke VirtualBox::saveSettings() because the global
9278 * settings have changed. This will happen if a machine rename has been
9279 * saved and the global machine and media registries will therefore need
9280 * updating.
9281 */
9282HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9283 int aFlags /*= 0*/)
9284{
9285 LogFlowThisFuncEnter();
9286
9287 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9288
9289 /* make sure child objects are unable to modify the settings while we are
9290 * saving them */
9291 ensureNoStateDependencies();
9292
9293 AssertReturn(!isSnapshotMachine(),
9294 E_FAIL);
9295
9296 HRESULT rc = S_OK;
9297 bool fNeedsWrite = false;
9298
9299 /* First, prepare to save settings. It will care about renaming the
9300 * settings directory and file if the machine name was changed and about
9301 * creating a new settings file if this is a new machine. */
9302 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9303 if (FAILED(rc)) return rc;
9304
9305 // keep a pointer to the current settings structures
9306 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9307 settings::MachineConfigFile *pNewConfig = NULL;
9308
9309 try
9310 {
9311 // make a fresh one to have everyone write stuff into
9312 pNewConfig = new settings::MachineConfigFile(NULL);
9313 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9314
9315 // now go and copy all the settings data from COM to the settings structures
9316 // (this calles saveSettings() on all the COM objects in the machine)
9317 copyMachineDataToSettings(*pNewConfig);
9318
9319 if (aFlags & SaveS_ResetCurStateModified)
9320 {
9321 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9322 mData->mCurrentStateModified = FALSE;
9323 fNeedsWrite = true; // always, no need to compare
9324 }
9325 else if (aFlags & SaveS_Force)
9326 {
9327 fNeedsWrite = true; // always, no need to compare
9328 }
9329 else
9330 {
9331 if (!mData->mCurrentStateModified)
9332 {
9333 // do a deep compare of the settings that we just saved with the settings
9334 // previously stored in the config file; this invokes MachineConfigFile::operator==
9335 // which does a deep compare of all the settings, which is expensive but less expensive
9336 // than writing out XML in vain
9337 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9338
9339 // could still be modified if any settings changed
9340 mData->mCurrentStateModified = fAnySettingsChanged;
9341
9342 fNeedsWrite = fAnySettingsChanged;
9343 }
9344 else
9345 fNeedsWrite = true;
9346 }
9347
9348 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9349
9350 if (fNeedsWrite)
9351 // now spit it all out!
9352 pNewConfig->write(mData->m_strConfigFileFull);
9353
9354 mData->pMachineConfigFile = pNewConfig;
9355 delete pOldConfig;
9356 commit();
9357
9358 // after saving settings, we are no longer different from the XML on disk
9359 mData->flModifications = 0;
9360 }
9361 catch (HRESULT err)
9362 {
9363 // we assume that error info is set by the thrower
9364 rc = err;
9365
9366 // restore old config
9367 delete pNewConfig;
9368 mData->pMachineConfigFile = pOldConfig;
9369 }
9370 catch (...)
9371 {
9372 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9373 }
9374
9375 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9376 {
9377 /* Fire the data change event, even on failure (since we've already
9378 * committed all data). This is done only for SessionMachines because
9379 * mutable Machine instances are always not registered (i.e. private
9380 * to the client process that creates them) and thus don't need to
9381 * inform callbacks. */
9382 if (isSessionMachine())
9383 mParent->onMachineDataChange(mData->mUuid);
9384 }
9385
9386 LogFlowThisFunc(("rc=%08X\n", rc));
9387 LogFlowThisFuncLeave();
9388 return rc;
9389}
9390
9391/**
9392 * Implementation for saving the machine settings into the given
9393 * settings::MachineConfigFile instance. This copies machine extradata
9394 * from the previous machine config file in the instance data, if any.
9395 *
9396 * This gets called from two locations:
9397 *
9398 * -- Machine::saveSettings(), during the regular XML writing;
9399 *
9400 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9401 * exported to OVF and we write the VirtualBox proprietary XML
9402 * into a <vbox:Machine> tag.
9403 *
9404 * This routine fills all the fields in there, including snapshots, *except*
9405 * for the following:
9406 *
9407 * -- fCurrentStateModified. There is some special logic associated with that.
9408 *
9409 * The caller can then call MachineConfigFile::write() or do something else
9410 * with it.
9411 *
9412 * Caller must hold the machine lock!
9413 *
9414 * This throws XML errors and HRESULT, so the caller must have a catch block!
9415 */
9416void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
9417{
9418 // deep copy extradata
9419 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9420
9421 config.uuid = mData->mUuid;
9422
9423 // copy name, description, OS type, teleport, UTC etc.
9424 config.machineUserData = mUserData->s;
9425
9426 if ( mData->mMachineState == MachineState_Saved
9427 || mData->mMachineState == MachineState_Restoring
9428 // when deleting a snapshot we may or may not have a saved state in the current state,
9429 // so let's not assert here please
9430 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9431 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9432 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9433 && (!mSSData->strStateFilePath.isEmpty())
9434 )
9435 )
9436 {
9437 Assert(!mSSData->strStateFilePath.isEmpty());
9438 /* try to make the file name relative to the settings file dir */
9439 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9440 }
9441 else
9442 {
9443 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9444 config.strStateFile.setNull();
9445 }
9446
9447 if (mData->mCurrentSnapshot)
9448 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
9449 else
9450 config.uuidCurrentSnapshot.clear();
9451
9452 config.timeLastStateChange = mData->mLastStateChange;
9453 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9454 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9455
9456 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9457 if (FAILED(rc)) throw rc;
9458
9459 rc = saveStorageControllers(config.storageMachine);
9460 if (FAILED(rc)) throw rc;
9461
9462 // save machine's media registry if this is VirtualBox 4.0 or later
9463 if (config.canHaveOwnMediaRegistry())
9464 {
9465 // determine machine folder
9466 Utf8Str strMachineFolder = getSettingsFileFull();
9467 strMachineFolder.stripFilename();
9468 mParent->saveMediaRegistry(config.mediaRegistry,
9469 getId(), // only media with registry ID == machine UUID
9470 strMachineFolder);
9471 // this throws HRESULT
9472 }
9473
9474 // save snapshots
9475 rc = saveAllSnapshots(config);
9476 if (FAILED(rc)) throw rc;
9477}
9478
9479/**
9480 * Saves all snapshots of the machine into the given machine config file. Called
9481 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9482 * @param config
9483 * @return
9484 */
9485HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
9486{
9487 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9488
9489 HRESULT rc = S_OK;
9490
9491 try
9492 {
9493 config.llFirstSnapshot.clear();
9494
9495 if (mData->mFirstSnapshot)
9496 {
9497 settings::Snapshot snapNew;
9498 config.llFirstSnapshot.push_back(snapNew);
9499
9500 // get reference to the fresh copy of the snapshot on the list and
9501 // work on that copy directly to avoid excessive copying later
9502 settings::Snapshot &snap = config.llFirstSnapshot.front();
9503
9504 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
9505 if (FAILED(rc)) throw rc;
9506 }
9507
9508// if (mType == IsSessionMachine)
9509// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9510
9511 }
9512 catch (HRESULT err)
9513 {
9514 /* we assume that error info is set by the thrower */
9515 rc = err;
9516 }
9517 catch (...)
9518 {
9519 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9520 }
9521
9522 return rc;
9523}
9524
9525/**
9526 * Saves the VM hardware configuration. It is assumed that the
9527 * given node is empty.
9528 *
9529 * @param data Reference to the settings object for the hardware config.
9530 * @param pDbg Pointer to the settings object for the debugging config
9531 * which happens to live in mHWData.
9532 * @param pAutostart Pointer to the settings object for the autostart config
9533 * which happens to live in mHWData.
9534 */
9535HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9536 settings::Autostart *pAutostart)
9537{
9538 HRESULT rc = S_OK;
9539
9540 try
9541 {
9542 /* The hardware version attribute (optional).
9543 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9544 if ( mHWData->mHWVersion == "1"
9545 && mSSData->strStateFilePath.isEmpty()
9546 )
9547 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. */
9548
9549 data.strVersion = mHWData->mHWVersion;
9550 data.uuid = mHWData->mHardwareUUID;
9551
9552 // CPU
9553 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9554 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
9555 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9556 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9557 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9558 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9559 data.fPAE = !!mHWData->mPAEEnabled;
9560 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9561
9562 /* Standard and Extended CPUID leafs. */
9563 data.llCpuIdLeafs.clear();
9564 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
9565 {
9566 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9567 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9568 }
9569 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
9570 {
9571 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9572 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9573 }
9574
9575 data.cCPUs = mHWData->mCPUCount;
9576 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9577 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9578
9579 data.llCpus.clear();
9580 if (data.fCpuHotPlug)
9581 {
9582 for (unsigned idx = 0; idx < data.cCPUs; idx++)
9583 {
9584 if (mHWData->mCPUAttached[idx])
9585 {
9586 settings::Cpu cpu;
9587 cpu.ulId = idx;
9588 data.llCpus.push_back(cpu);
9589 }
9590 }
9591 }
9592
9593 // memory
9594 data.ulMemorySizeMB = mHWData->mMemorySize;
9595 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9596
9597 // firmware
9598 data.firmwareType = mHWData->mFirmwareType;
9599
9600 // HID
9601 data.pointingHIDType = mHWData->mPointingHIDType;
9602 data.keyboardHIDType = mHWData->mKeyboardHIDType;
9603
9604 // chipset
9605 data.chipsetType = mHWData->mChipsetType;
9606
9607 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9608
9609 // HPET
9610 data.fHPETEnabled = !!mHWData->mHPETEnabled;
9611
9612 // boot order
9613 data.mapBootOrder.clear();
9614 for (size_t i = 0;
9615 i < RT_ELEMENTS(mHWData->mBootOrder);
9616 ++i)
9617 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9618
9619 // display
9620 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9621 data.cMonitors = mHWData->mMonitorCount;
9622 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9623 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9624
9625 /* VRDEServer settings (optional) */
9626 rc = mVRDEServer->saveSettings(data.vrdeSettings);
9627 if (FAILED(rc)) throw rc;
9628
9629 /* BIOS (required) */
9630 rc = mBIOSSettings->saveSettings(data.biosSettings);
9631 if (FAILED(rc)) throw rc;
9632
9633 /* USB Controller (required) */
9634 rc = mUSBController->saveSettings(data.usbController);
9635 if (FAILED(rc)) throw rc;
9636
9637 /* Network adapters (required) */
9638 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
9639 data.llNetworkAdapters.clear();
9640 /* Write out only the nominal number of network adapters for this
9641 * chipset type. Since Machine::commit() hasn't been called there
9642 * may be extra NIC settings in the vector. */
9643 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
9644 {
9645 settings::NetworkAdapter nic;
9646 nic.ulSlot = slot;
9647 /* paranoia check... must not be NULL, but must not crash either. */
9648 if (mNetworkAdapters[slot])
9649 {
9650 rc = mNetworkAdapters[slot]->saveSettings(nic);
9651 if (FAILED(rc)) throw rc;
9652
9653 data.llNetworkAdapters.push_back(nic);
9654 }
9655 }
9656
9657 /* Serial ports */
9658 data.llSerialPorts.clear();
9659 for (ULONG slot = 0;
9660 slot < RT_ELEMENTS(mSerialPorts);
9661 ++slot)
9662 {
9663 settings::SerialPort s;
9664 s.ulSlot = slot;
9665 rc = mSerialPorts[slot]->saveSettings(s);
9666 if (FAILED(rc)) return rc;
9667
9668 data.llSerialPorts.push_back(s);
9669 }
9670
9671 /* Parallel ports */
9672 data.llParallelPorts.clear();
9673 for (ULONG slot = 0;
9674 slot < RT_ELEMENTS(mParallelPorts);
9675 ++slot)
9676 {
9677 settings::ParallelPort p;
9678 p.ulSlot = slot;
9679 rc = mParallelPorts[slot]->saveSettings(p);
9680 if (FAILED(rc)) return rc;
9681
9682 data.llParallelPorts.push_back(p);
9683 }
9684
9685 /* Audio adapter */
9686 rc = mAudioAdapter->saveSettings(data.audioAdapter);
9687 if (FAILED(rc)) return rc;
9688
9689 /* Shared folders */
9690 data.llSharedFolders.clear();
9691 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
9692 it != mHWData->mSharedFolders.end();
9693 ++it)
9694 {
9695 SharedFolder *pSF = *it;
9696 AutoCaller sfCaller(pSF);
9697 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
9698 settings::SharedFolder sf;
9699 sf.strName = pSF->getName();
9700 sf.strHostPath = pSF->getHostPath();
9701 sf.fWritable = !!pSF->isWritable();
9702 sf.fAutoMount = !!pSF->isAutoMounted();
9703
9704 data.llSharedFolders.push_back(sf);
9705 }
9706
9707 // clipboard
9708 data.clipboardMode = mHWData->mClipboardMode;
9709
9710 // drag'n'drop
9711 data.dragAndDropMode = mHWData->mDragAndDropMode;
9712
9713 /* Guest */
9714 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
9715
9716 // IO settings
9717 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
9718 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
9719
9720 /* BandwidthControl (required) */
9721 rc = mBandwidthControl->saveSettings(data.ioSettings);
9722 if (FAILED(rc)) throw rc;
9723
9724 /* Host PCI devices */
9725 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
9726 it != mHWData->mPCIDeviceAssignments.end();
9727 ++it)
9728 {
9729 ComObjPtr<PCIDeviceAttachment> pda = *it;
9730 settings::HostPCIDeviceAttachment hpda;
9731
9732 rc = pda->saveSettings(hpda);
9733 if (FAILED(rc)) throw rc;
9734
9735 data.pciAttachments.push_back(hpda);
9736 }
9737
9738
9739 // guest properties
9740 data.llGuestProperties.clear();
9741#ifdef VBOX_WITH_GUEST_PROPS
9742 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
9743 it != mHWData->mGuestProperties.end();
9744 ++it)
9745 {
9746 HWData::GuestProperty property = *it;
9747
9748 /* Remove transient guest properties at shutdown unless we
9749 * are saving state */
9750 if ( ( mData->mMachineState == MachineState_PoweredOff
9751 || mData->mMachineState == MachineState_Aborted
9752 || mData->mMachineState == MachineState_Teleported)
9753 && ( property.mFlags & guestProp::TRANSIENT
9754 || property.mFlags & guestProp::TRANSRESET))
9755 continue;
9756 settings::GuestProperty prop;
9757 prop.strName = property.strName;
9758 prop.strValue = property.strValue;
9759 prop.timestamp = property.mTimestamp;
9760 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
9761 guestProp::writeFlags(property.mFlags, szFlags);
9762 prop.strFlags = szFlags;
9763
9764 data.llGuestProperties.push_back(prop);
9765 }
9766
9767 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
9768 /* I presume this doesn't require a backup(). */
9769 mData->mGuestPropertiesModified = FALSE;
9770#endif /* VBOX_WITH_GUEST_PROPS defined */
9771
9772 *pDbg = mHWData->mDebugging;
9773 *pAutostart = mHWData->mAutostart;
9774 }
9775 catch(std::bad_alloc &)
9776 {
9777 return E_OUTOFMEMORY;
9778 }
9779
9780 AssertComRC(rc);
9781 return rc;
9782}
9783
9784/**
9785 * Saves the storage controller configuration.
9786 *
9787 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
9788 */
9789HRESULT Machine::saveStorageControllers(settings::Storage &data)
9790{
9791 data.llStorageControllers.clear();
9792
9793 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9794 it != mStorageControllers->end();
9795 ++it)
9796 {
9797 HRESULT rc;
9798 ComObjPtr<StorageController> pCtl = *it;
9799
9800 settings::StorageController ctl;
9801 ctl.strName = pCtl->getName();
9802 ctl.controllerType = pCtl->getControllerType();
9803 ctl.storageBus = pCtl->getStorageBus();
9804 ctl.ulInstance = pCtl->getInstance();
9805 ctl.fBootable = pCtl->getBootable();
9806
9807 /* Save the port count. */
9808 ULONG portCount;
9809 rc = pCtl->COMGETTER(PortCount)(&portCount);
9810 ComAssertComRCRet(rc, rc);
9811 ctl.ulPortCount = portCount;
9812
9813 /* Save fUseHostIOCache */
9814 BOOL fUseHostIOCache;
9815 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
9816 ComAssertComRCRet(rc, rc);
9817 ctl.fUseHostIOCache = !!fUseHostIOCache;
9818
9819 /* Save IDE emulation settings. */
9820 if (ctl.controllerType == StorageControllerType_IntelAhci)
9821 {
9822 if ( (FAILED(rc = pCtl->GetIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
9823 || (FAILED(rc = pCtl->GetIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
9824 || (FAILED(rc = pCtl->GetIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
9825 || (FAILED(rc = pCtl->GetIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
9826 )
9827 ComAssertComRCRet(rc, rc);
9828 }
9829
9830 /* save the devices now. */
9831 rc = saveStorageDevices(pCtl, ctl);
9832 ComAssertComRCRet(rc, rc);
9833
9834 data.llStorageControllers.push_back(ctl);
9835 }
9836
9837 return S_OK;
9838}
9839
9840/**
9841 * Saves the hard disk configuration.
9842 */
9843HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
9844 settings::StorageController &data)
9845{
9846 MediaData::AttachmentList atts;
9847
9848 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
9849 if (FAILED(rc)) return rc;
9850
9851 data.llAttachedDevices.clear();
9852 for (MediaData::AttachmentList::const_iterator it = atts.begin();
9853 it != atts.end();
9854 ++it)
9855 {
9856 settings::AttachedDevice dev;
9857
9858 MediumAttachment *pAttach = *it;
9859 Medium *pMedium = pAttach->getMedium();
9860
9861 dev.deviceType = pAttach->getType();
9862 dev.lPort = pAttach->getPort();
9863 dev.lDevice = pAttach->getDevice();
9864 if (pMedium)
9865 {
9866 if (pMedium->isHostDrive())
9867 dev.strHostDriveSrc = pMedium->getLocationFull();
9868 else
9869 dev.uuid = pMedium->getId();
9870 dev.fPassThrough = pAttach->getPassthrough();
9871 dev.fTempEject = pAttach->getTempEject();
9872 dev.fDiscard = pAttach->getDiscard();
9873 }
9874
9875 dev.strBwGroup = pAttach->getBandwidthGroup();
9876
9877 data.llAttachedDevices.push_back(dev);
9878 }
9879
9880 return S_OK;
9881}
9882
9883/**
9884 * Saves machine state settings as defined by aFlags
9885 * (SaveSTS_* values).
9886 *
9887 * @param aFlags Combination of SaveSTS_* flags.
9888 *
9889 * @note Locks objects for writing.
9890 */
9891HRESULT Machine::saveStateSettings(int aFlags)
9892{
9893 if (aFlags == 0)
9894 return S_OK;
9895
9896 AutoCaller autoCaller(this);
9897 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9898
9899 /* This object's write lock is also necessary to serialize file access
9900 * (prevent concurrent reads and writes) */
9901 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9902
9903 HRESULT rc = S_OK;
9904
9905 Assert(mData->pMachineConfigFile);
9906
9907 try
9908 {
9909 if (aFlags & SaveSTS_CurStateModified)
9910 mData->pMachineConfigFile->fCurrentStateModified = true;
9911
9912 if (aFlags & SaveSTS_StateFilePath)
9913 {
9914 if (!mSSData->strStateFilePath.isEmpty())
9915 /* try to make the file name relative to the settings file dir */
9916 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
9917 else
9918 mData->pMachineConfigFile->strStateFile.setNull();
9919 }
9920
9921 if (aFlags & SaveSTS_StateTimeStamp)
9922 {
9923 Assert( mData->mMachineState != MachineState_Aborted
9924 || mSSData->strStateFilePath.isEmpty());
9925
9926 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
9927
9928 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
9929//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
9930 }
9931
9932 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
9933 }
9934 catch (...)
9935 {
9936 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9937 }
9938
9939 return rc;
9940}
9941
9942/**
9943 * Ensures that the given medium is added to a media registry. If this machine
9944 * was created with 4.0 or later, then the machine registry is used. Otherwise
9945 * the global VirtualBox media registry is used.
9946 *
9947 * Caller must NOT hold machine lock, media tree or any medium locks!
9948 *
9949 * @param pMedium
9950 */
9951void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
9952{
9953 /* Paranoia checks: do not hold machine or media tree locks. */
9954 AssertReturnVoid(!isWriteLockOnCurrentThread());
9955 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
9956
9957 ComObjPtr<Medium> pBase;
9958 {
9959 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9960 pBase = pMedium->getBase();
9961 }
9962
9963 /* Paranoia checks: do not hold medium locks. */
9964 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
9965 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
9966
9967 // decide which medium registry to use now that the medium is attached:
9968 Guid uuid;
9969 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
9970 // machine XML is VirtualBox 4.0 or higher:
9971 uuid = getId(); // machine UUID
9972 else
9973 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
9974
9975 if (pMedium->addRegistry(uuid, false /* fRecurse */))
9976 mParent->markRegistryModified(uuid);
9977
9978 /* For more complex hard disk structures it can happen that the base
9979 * medium isn't yet associated with any medium registry. Do that now. */
9980 if (pMedium != pBase)
9981 {
9982 if (pBase->addRegistry(uuid, true /* fRecurse */))
9983 mParent->markRegistryModified(uuid);
9984 }
9985}
9986
9987/**
9988 * Creates differencing hard disks for all normal hard disks attached to this
9989 * machine and a new set of attachments to refer to created disks.
9990 *
9991 * Used when taking a snapshot or when deleting the current state. Gets called
9992 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
9993 *
9994 * This method assumes that mMediaData contains the original hard disk attachments
9995 * it needs to create diffs for. On success, these attachments will be replaced
9996 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
9997 * called to delete created diffs which will also rollback mMediaData and restore
9998 * whatever was backed up before calling this method.
9999 *
10000 * Attachments with non-normal hard disks are left as is.
10001 *
10002 * If @a aOnline is @c false then the original hard disks that require implicit
10003 * diffs will be locked for reading. Otherwise it is assumed that they are
10004 * already locked for writing (when the VM was started). Note that in the latter
10005 * case it is responsibility of the caller to lock the newly created diffs for
10006 * writing if this method succeeds.
10007 *
10008 * @param aProgress Progress object to run (must contain at least as
10009 * many operations left as the number of hard disks
10010 * attached).
10011 * @param aOnline Whether the VM was online prior to this operation.
10012 *
10013 * @note The progress object is not marked as completed, neither on success nor
10014 * on failure. This is a responsibility of the caller.
10015 *
10016 * @note Locks this object for writing.
10017 */
10018HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10019 ULONG aWeight,
10020 bool aOnline)
10021{
10022 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10023
10024 AutoCaller autoCaller(this);
10025 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10026
10027 AutoMultiWriteLock2 alock(this->lockHandle(),
10028 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10029
10030 /* must be in a protective state because we release the lock below */
10031 AssertReturn( mData->mMachineState == MachineState_Saving
10032 || mData->mMachineState == MachineState_LiveSnapshotting
10033 || mData->mMachineState == MachineState_RestoringSnapshot
10034 || mData->mMachineState == MachineState_DeletingSnapshot
10035 , E_FAIL);
10036
10037 HRESULT rc = S_OK;
10038
10039 MediumLockListMap lockedMediaOffline;
10040 MediumLockListMap *lockedMediaMap;
10041 if (aOnline)
10042 lockedMediaMap = &mData->mSession.mLockedMedia;
10043 else
10044 lockedMediaMap = &lockedMediaOffline;
10045
10046 try
10047 {
10048 if (!aOnline)
10049 {
10050 /* lock all attached hard disks early to detect "in use"
10051 * situations before creating actual diffs */
10052 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10053 it != mMediaData->mAttachments.end();
10054 ++it)
10055 {
10056 MediumAttachment* pAtt = *it;
10057 if (pAtt->getType() == DeviceType_HardDisk)
10058 {
10059 Medium* pMedium = pAtt->getMedium();
10060 Assert(pMedium);
10061
10062 MediumLockList *pMediumLockList(new MediumLockList());
10063 alock.release();
10064 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10065 false /* fMediumLockWrite */,
10066 NULL,
10067 *pMediumLockList);
10068 alock.acquire();
10069 if (FAILED(rc))
10070 {
10071 delete pMediumLockList;
10072 throw rc;
10073 }
10074 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10075 if (FAILED(rc))
10076 {
10077 throw setError(rc,
10078 tr("Collecting locking information for all attached media failed"));
10079 }
10080 }
10081 }
10082
10083 /* Now lock all media. If this fails, nothing is locked. */
10084 alock.release();
10085 rc = lockedMediaMap->Lock();
10086 alock.acquire();
10087 if (FAILED(rc))
10088 {
10089 throw setError(rc,
10090 tr("Locking of attached media failed"));
10091 }
10092 }
10093
10094 /* remember the current list (note that we don't use backup() since
10095 * mMediaData may be already backed up) */
10096 MediaData::AttachmentList atts = mMediaData->mAttachments;
10097
10098 /* start from scratch */
10099 mMediaData->mAttachments.clear();
10100
10101 /* go through remembered attachments and create diffs for normal hard
10102 * disks and attach them */
10103 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10104 it != atts.end();
10105 ++it)
10106 {
10107 MediumAttachment* pAtt = *it;
10108
10109 DeviceType_T devType = pAtt->getType();
10110 Medium* pMedium = pAtt->getMedium();
10111
10112 if ( devType != DeviceType_HardDisk
10113 || pMedium == NULL
10114 || pMedium->getType() != MediumType_Normal)
10115 {
10116 /* copy the attachment as is */
10117
10118 /** @todo the progress object created in Console::TakeSnaphot
10119 * only expects operations for hard disks. Later other
10120 * device types need to show up in the progress as well. */
10121 if (devType == DeviceType_HardDisk)
10122 {
10123 if (pMedium == NULL)
10124 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10125 aWeight); // weight
10126 else
10127 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10128 pMedium->getBase()->getName().c_str()).raw(),
10129 aWeight); // weight
10130 }
10131
10132 mMediaData->mAttachments.push_back(pAtt);
10133 continue;
10134 }
10135
10136 /* need a diff */
10137 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10138 pMedium->getBase()->getName().c_str()).raw(),
10139 aWeight); // weight
10140
10141 Utf8Str strFullSnapshotFolder;
10142 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10143
10144 ComObjPtr<Medium> diff;
10145 diff.createObject();
10146 // store the diff in the same registry as the parent
10147 // (this cannot fail here because we can't create implicit diffs for
10148 // unregistered images)
10149 Guid uuidRegistryParent;
10150 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10151 Assert(fInRegistry); NOREF(fInRegistry);
10152 rc = diff->init(mParent,
10153 pMedium->getPreferredDiffFormat(),
10154 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10155 uuidRegistryParent);
10156 if (FAILED(rc)) throw rc;
10157
10158 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10159 * the push_back? Looks like we're going to release medium with the
10160 * wrong kind of lock (general issue with if we fail anywhere at all)
10161 * and an orphaned VDI in the snapshots folder. */
10162
10163 /* update the appropriate lock list */
10164 MediumLockList *pMediumLockList;
10165 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10166 AssertComRCThrowRC(rc);
10167 if (aOnline)
10168 {
10169 alock.release();
10170 rc = pMediumLockList->Update(pMedium, false);
10171 alock.acquire();
10172 AssertComRCThrowRC(rc);
10173 }
10174
10175 /* release the locks before the potentially lengthy operation */
10176 alock.release();
10177 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10178 pMediumLockList,
10179 NULL /* aProgress */,
10180 true /* aWait */);
10181 alock.acquire();
10182 if (FAILED(rc)) throw rc;
10183
10184 rc = lockedMediaMap->Unlock();
10185 AssertComRCThrowRC(rc);
10186 alock.release();
10187 rc = pMediumLockList->Append(diff, true);
10188 alock.acquire();
10189 AssertComRCThrowRC(rc);
10190 alock.release();
10191 rc = lockedMediaMap->Lock();
10192 alock.acquire();
10193 AssertComRCThrowRC(rc);
10194
10195 rc = diff->addBackReference(mData->mUuid);
10196 AssertComRCThrowRC(rc);
10197
10198 /* add a new attachment */
10199 ComObjPtr<MediumAttachment> attachment;
10200 attachment.createObject();
10201 rc = attachment->init(this,
10202 diff,
10203 pAtt->getControllerName(),
10204 pAtt->getPort(),
10205 pAtt->getDevice(),
10206 DeviceType_HardDisk,
10207 true /* aImplicit */,
10208 false /* aPassthrough */,
10209 false /* aTempEject */,
10210 pAtt->getNonRotational(),
10211 pAtt->getDiscard(),
10212 pAtt->getBandwidthGroup());
10213 if (FAILED(rc)) throw rc;
10214
10215 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10216 AssertComRCThrowRC(rc);
10217 mMediaData->mAttachments.push_back(attachment);
10218 }
10219 }
10220 catch (HRESULT aRC) { rc = aRC; }
10221
10222 /* unlock all hard disks we locked */
10223 if (!aOnline)
10224 {
10225 ErrorInfoKeeper eik;
10226
10227 HRESULT rc1 = lockedMediaMap->Clear();
10228 AssertComRC(rc1);
10229 }
10230
10231 if (FAILED(rc))
10232 {
10233 MultiResult mrc = rc;
10234
10235 alock.release();
10236 mrc = deleteImplicitDiffs();
10237 }
10238
10239 return rc;
10240}
10241
10242/**
10243 * Deletes implicit differencing hard disks created either by
10244 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10245 *
10246 * Note that to delete hard disks created by #AttachDevice() this method is
10247 * called from #fixupMedia() when the changes are rolled back.
10248 *
10249 * @note Locks this object for writing.
10250 */
10251HRESULT Machine::deleteImplicitDiffs()
10252{
10253 AutoCaller autoCaller(this);
10254 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10255
10256 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10257 LogFlowThisFuncEnter();
10258
10259 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10260
10261 HRESULT rc = S_OK;
10262
10263 MediaData::AttachmentList implicitAtts;
10264
10265 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10266
10267 /* enumerate new attachments */
10268 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10269 it != mMediaData->mAttachments.end();
10270 ++it)
10271 {
10272 ComObjPtr<Medium> hd = (*it)->getMedium();
10273 if (hd.isNull())
10274 continue;
10275
10276 if ((*it)->isImplicit())
10277 {
10278 /* deassociate and mark for deletion */
10279 LogFlowThisFunc(("Detaching '%s', pending deletion\n", (*it)->getLogName()));
10280 rc = hd->removeBackReference(mData->mUuid);
10281 AssertComRC(rc);
10282 implicitAtts.push_back(*it);
10283 continue;
10284 }
10285
10286 /* was this hard disk attached before? */
10287 if (!findAttachment(oldAtts, hd))
10288 {
10289 /* no: de-associate */
10290 LogFlowThisFunc(("Detaching '%s', no deletion\n", (*it)->getLogName()));
10291 rc = hd->removeBackReference(mData->mUuid);
10292 AssertComRC(rc);
10293 continue;
10294 }
10295 LogFlowThisFunc(("Not detaching '%s'\n", (*it)->getLogName()));
10296 }
10297
10298 /* rollback hard disk changes */
10299 mMediaData.rollback();
10300
10301 MultiResult mrc(S_OK);
10302
10303 /* delete unused implicit diffs */
10304 if (implicitAtts.size() != 0)
10305 {
10306 /* will release the lock before the potentially lengthy
10307 * operation, so protect with the special state (unless already
10308 * protected) */
10309 MachineState_T oldState = mData->mMachineState;
10310 if ( oldState != MachineState_Saving
10311 && oldState != MachineState_LiveSnapshotting
10312 && oldState != MachineState_RestoringSnapshot
10313 && oldState != MachineState_DeletingSnapshot
10314 && oldState != MachineState_DeletingSnapshotOnline
10315 && oldState != MachineState_DeletingSnapshotPaused
10316 )
10317 setMachineState(MachineState_SettingUp);
10318
10319 alock.release();
10320
10321 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
10322 it != implicitAtts.end();
10323 ++it)
10324 {
10325 LogFlowThisFunc(("Deleting '%s'\n", (*it)->getLogName()));
10326 ComObjPtr<Medium> hd = (*it)->getMedium();
10327
10328 rc = hd->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10329 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, (*it)->getLogName(), hd->getLocationFull().c_str() ));
10330 mrc = rc;
10331 }
10332
10333 alock.acquire();
10334
10335 if (mData->mMachineState == MachineState_SettingUp)
10336 setMachineState(oldState);
10337 }
10338
10339 return mrc;
10340}
10341
10342/**
10343 * Looks through the given list of media attachments for one with the given parameters
10344 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10345 * can be searched as well if needed.
10346 *
10347 * @param list
10348 * @param aControllerName
10349 * @param aControllerPort
10350 * @param aDevice
10351 * @return
10352 */
10353MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10354 IN_BSTR aControllerName,
10355 LONG aControllerPort,
10356 LONG aDevice)
10357{
10358 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10359 it != ll.end();
10360 ++it)
10361 {
10362 MediumAttachment *pAttach = *it;
10363 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
10364 return pAttach;
10365 }
10366
10367 return NULL;
10368}
10369
10370/**
10371 * Looks through the given list of media attachments for one with the given parameters
10372 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10373 * can be searched as well if needed.
10374 *
10375 * @param list
10376 * @param aControllerName
10377 * @param aControllerPort
10378 * @param aDevice
10379 * @return
10380 */
10381MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10382 ComObjPtr<Medium> pMedium)
10383{
10384 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10385 it != ll.end();
10386 ++it)
10387 {
10388 MediumAttachment *pAttach = *it;
10389 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10390 if (pMediumThis == pMedium)
10391 return pAttach;
10392 }
10393
10394 return NULL;
10395}
10396
10397/**
10398 * Looks through the given list of media attachments for one with the given parameters
10399 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10400 * can be searched as well if needed.
10401 *
10402 * @param list
10403 * @param aControllerName
10404 * @param aControllerPort
10405 * @param aDevice
10406 * @return
10407 */
10408MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10409 Guid &id)
10410{
10411 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10412 it != ll.end();
10413 ++it)
10414 {
10415 MediumAttachment *pAttach = *it;
10416 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10417 if (pMediumThis->getId() == id)
10418 return pAttach;
10419 }
10420
10421 return NULL;
10422}
10423
10424/**
10425 * Main implementation for Machine::DetachDevice. This also gets called
10426 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10427 *
10428 * @param pAttach Medium attachment to detach.
10429 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10430 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
10431 * @return
10432 */
10433HRESULT Machine::detachDevice(MediumAttachment *pAttach,
10434 AutoWriteLock &writeLock,
10435 Snapshot *pSnapshot)
10436{
10437 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
10438 DeviceType_T mediumType = pAttach->getType();
10439
10440 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
10441
10442 if (pAttach->isImplicit())
10443 {
10444 /* attempt to implicitly delete the implicitly created diff */
10445
10446 /// @todo move the implicit flag from MediumAttachment to Medium
10447 /// and forbid any hard disk operation when it is implicit. Or maybe
10448 /// a special media state for it to make it even more simple.
10449
10450 Assert(mMediaData.isBackedUp());
10451
10452 /* will release the lock before the potentially lengthy operation, so
10453 * protect with the special state */
10454 MachineState_T oldState = mData->mMachineState;
10455 setMachineState(MachineState_SettingUp);
10456
10457 writeLock.release();
10458
10459 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
10460 true /*aWait*/);
10461
10462 writeLock.acquire();
10463
10464 setMachineState(oldState);
10465
10466 if (FAILED(rc)) return rc;
10467 }
10468
10469 setModified(IsModified_Storage);
10470 mMediaData.backup();
10471 mMediaData->mAttachments.remove(pAttach);
10472
10473 if (!oldmedium.isNull())
10474 {
10475 // if this is from a snapshot, do not defer detachment to commitMedia()
10476 if (pSnapshot)
10477 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
10478 // else if non-hard disk media, do not defer detachment to commitMedia() either
10479 else if (mediumType != DeviceType_HardDisk)
10480 oldmedium->removeBackReference(mData->mUuid);
10481 }
10482
10483 return S_OK;
10484}
10485
10486/**
10487 * Goes thru all media of the given list and
10488 *
10489 * 1) calls detachDevice() on each of them for this machine and
10490 * 2) adds all Medium objects found in the process to the given list,
10491 * depending on cleanupMode.
10492 *
10493 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10494 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10495 * media to the list.
10496 *
10497 * This gets called from Machine::Unregister, both for the actual Machine and
10498 * the SnapshotMachine objects that might be found in the snapshots.
10499 *
10500 * Requires caller and locking. The machine lock must be passed in because it
10501 * will be passed on to detachDevice which needs it for temporary unlocking.
10502 *
10503 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
10504 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
10505 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
10506 * otherwise no media get added.
10507 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
10508 * @return
10509 */
10510HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
10511 Snapshot *pSnapshot,
10512 CleanupMode_T cleanupMode,
10513 MediaList &llMedia)
10514{
10515 Assert(isWriteLockOnCurrentThread());
10516
10517 HRESULT rc;
10518
10519 // make a temporary list because detachDevice invalidates iterators into
10520 // mMediaData->mAttachments
10521 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
10522
10523 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
10524 it != llAttachments2.end();
10525 ++it)
10526 {
10527 ComObjPtr<MediumAttachment> &pAttach = *it;
10528 ComObjPtr<Medium> pMedium = pAttach->getMedium();
10529
10530 if (!pMedium.isNull())
10531 {
10532 AutoCaller mac(pMedium);
10533 if (FAILED(mac.rc())) return mac.rc();
10534 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
10535 DeviceType_T devType = pMedium->getDeviceType();
10536 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
10537 && devType == DeviceType_HardDisk)
10538 || (cleanupMode == CleanupMode_Full)
10539 )
10540 {
10541 llMedia.push_back(pMedium);
10542 ComObjPtr<Medium> pParent = pMedium->getParent();
10543 /*
10544 * Search for medias which are not attached to any machine, but
10545 * in the chain to an attached disk. Mediums are only consided
10546 * if they are:
10547 * - have only one child
10548 * - no references to any machines
10549 * - are of normal medium type
10550 */
10551 while (!pParent.isNull())
10552 {
10553 AutoCaller mac1(pParent);
10554 if (FAILED(mac1.rc())) return mac1.rc();
10555 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
10556 if (pParent->getChildren().size() == 1)
10557 {
10558 if ( pParent->getMachineBackRefCount() == 0
10559 && pParent->getType() == MediumType_Normal
10560 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
10561 llMedia.push_back(pParent);
10562 }
10563 else
10564 break;
10565 pParent = pParent->getParent();
10566 }
10567 }
10568 }
10569
10570 // real machine: then we need to use the proper method
10571 rc = detachDevice(pAttach, writeLock, pSnapshot);
10572
10573 if (FAILED(rc))
10574 return rc;
10575 }
10576
10577 return S_OK;
10578}
10579
10580/**
10581 * Perform deferred hard disk detachments.
10582 *
10583 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10584 * backed up).
10585 *
10586 * If @a aOnline is @c true then this method will also unlock the old hard disks
10587 * for which the new implicit diffs were created and will lock these new diffs for
10588 * writing.
10589 *
10590 * @param aOnline Whether the VM was online prior to this operation.
10591 *
10592 * @note Locks this object for writing!
10593 */
10594void Machine::commitMedia(bool aOnline /*= false*/)
10595{
10596 AutoCaller autoCaller(this);
10597 AssertComRCReturnVoid(autoCaller.rc());
10598
10599 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10600
10601 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
10602
10603 HRESULT rc = S_OK;
10604
10605 /* no attach/detach operations -- nothing to do */
10606 if (!mMediaData.isBackedUp())
10607 return;
10608
10609 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10610 bool fMediaNeedsLocking = false;
10611
10612 /* enumerate new attachments */
10613 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10614 it != mMediaData->mAttachments.end();
10615 ++it)
10616 {
10617 MediumAttachment *pAttach = *it;
10618
10619 pAttach->commit();
10620
10621 Medium* pMedium = pAttach->getMedium();
10622 bool fImplicit = pAttach->isImplicit();
10623
10624 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
10625 (pMedium) ? pMedium->getName().c_str() : "NULL",
10626 fImplicit));
10627
10628 /** @todo convert all this Machine-based voodoo to MediumAttachment
10629 * based commit logic. */
10630 if (fImplicit)
10631 {
10632 /* convert implicit attachment to normal */
10633 pAttach->setImplicit(false);
10634
10635 if ( aOnline
10636 && pMedium
10637 && pAttach->getType() == DeviceType_HardDisk
10638 )
10639 {
10640 ComObjPtr<Medium> parent = pMedium->getParent();
10641 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
10642
10643 /* update the appropriate lock list */
10644 MediumLockList *pMediumLockList;
10645 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10646 AssertComRC(rc);
10647 if (pMediumLockList)
10648 {
10649 /* unlock if there's a need to change the locking */
10650 if (!fMediaNeedsLocking)
10651 {
10652 rc = mData->mSession.mLockedMedia.Unlock();
10653 AssertComRC(rc);
10654 fMediaNeedsLocking = true;
10655 }
10656 rc = pMediumLockList->Update(parent, false);
10657 AssertComRC(rc);
10658 rc = pMediumLockList->Append(pMedium, true);
10659 AssertComRC(rc);
10660 }
10661 }
10662
10663 continue;
10664 }
10665
10666 if (pMedium)
10667 {
10668 /* was this medium attached before? */
10669 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
10670 oldIt != oldAtts.end();
10671 ++oldIt)
10672 {
10673 MediumAttachment *pOldAttach = *oldIt;
10674 if (pOldAttach->getMedium() == pMedium)
10675 {
10676 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
10677
10678 /* yes: remove from old to avoid de-association */
10679 oldAtts.erase(oldIt);
10680 break;
10681 }
10682 }
10683 }
10684 }
10685
10686 /* enumerate remaining old attachments and de-associate from the
10687 * current machine state */
10688 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
10689 it != oldAtts.end();
10690 ++it)
10691 {
10692 MediumAttachment *pAttach = *it;
10693 Medium* pMedium = pAttach->getMedium();
10694
10695 /* Detach only hard disks, since DVD/floppy media is detached
10696 * instantly in MountMedium. */
10697 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
10698 {
10699 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
10700
10701 /* now de-associate from the current machine state */
10702 rc = pMedium->removeBackReference(mData->mUuid);
10703 AssertComRC(rc);
10704
10705 if (aOnline)
10706 {
10707 /* unlock since medium is not used anymore */
10708 MediumLockList *pMediumLockList;
10709 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10710 AssertComRC(rc);
10711 if (pMediumLockList)
10712 {
10713 rc = mData->mSession.mLockedMedia.Remove(pAttach);
10714 AssertComRC(rc);
10715 }
10716 }
10717 }
10718 }
10719
10720 /* take media locks again so that the locking state is consistent */
10721 if (fMediaNeedsLocking)
10722 {
10723 Assert(aOnline);
10724 rc = mData->mSession.mLockedMedia.Lock();
10725 AssertComRC(rc);
10726 }
10727
10728 /* commit the hard disk changes */
10729 mMediaData.commit();
10730
10731 if (isSessionMachine())
10732 {
10733 /*
10734 * Update the parent machine to point to the new owner.
10735 * This is necessary because the stored parent will point to the
10736 * session machine otherwise and cause crashes or errors later
10737 * when the session machine gets invalid.
10738 */
10739 /** @todo Change the MediumAttachment class to behave like any other
10740 * class in this regard by creating peer MediumAttachment
10741 * objects for session machines and share the data with the peer
10742 * machine.
10743 */
10744 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10745 it != mMediaData->mAttachments.end();
10746 ++it)
10747 {
10748 (*it)->updateParentMachine(mPeer);
10749 }
10750
10751 /* attach new data to the primary machine and reshare it */
10752 mPeer->mMediaData.attach(mMediaData);
10753 }
10754
10755 return;
10756}
10757
10758/**
10759 * Perform deferred deletion of implicitly created diffs.
10760 *
10761 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10762 * backed up).
10763 *
10764 * @note Locks this object for writing!
10765 */
10766void Machine::rollbackMedia()
10767{
10768 AutoCaller autoCaller(this);
10769 AssertComRCReturnVoid (autoCaller.rc());
10770
10771 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10772
10773 LogFlowThisFunc(("Entering\n"));
10774
10775 HRESULT rc = S_OK;
10776
10777 /* no attach/detach operations -- nothing to do */
10778 if (!mMediaData.isBackedUp())
10779 return;
10780
10781 /* enumerate new attachments */
10782 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10783 it != mMediaData->mAttachments.end();
10784 ++it)
10785 {
10786 MediumAttachment *pAttach = *it;
10787 /* Fix up the backrefs for DVD/floppy media. */
10788 if (pAttach->getType() != DeviceType_HardDisk)
10789 {
10790 Medium* pMedium = pAttach->getMedium();
10791 if (pMedium)
10792 {
10793 rc = pMedium->removeBackReference(mData->mUuid);
10794 AssertComRC(rc);
10795 }
10796 }
10797
10798 (*it)->rollback();
10799
10800 pAttach = *it;
10801 /* Fix up the backrefs for DVD/floppy media. */
10802 if (pAttach->getType() != DeviceType_HardDisk)
10803 {
10804 Medium* pMedium = pAttach->getMedium();
10805 if (pMedium)
10806 {
10807 rc = pMedium->addBackReference(mData->mUuid);
10808 AssertComRC(rc);
10809 }
10810 }
10811 }
10812
10813 /** @todo convert all this Machine-based voodoo to MediumAttachment
10814 * based rollback logic. */
10815 deleteImplicitDiffs();
10816
10817 return;
10818}
10819
10820/**
10821 * Returns true if the settings file is located in the directory named exactly
10822 * as the machine; this means, among other things, that the machine directory
10823 * should be auto-renamed.
10824 *
10825 * @param aSettingsDir if not NULL, the full machine settings file directory
10826 * name will be assigned there.
10827 *
10828 * @note Doesn't lock anything.
10829 * @note Not thread safe (must be called from this object's lock).
10830 */
10831bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
10832{
10833 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
10834 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
10835 if (aSettingsDir)
10836 *aSettingsDir = strMachineDirName;
10837 strMachineDirName.stripPath(); // vmname
10838 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
10839 strConfigFileOnly.stripPath() // vmname.vbox
10840 .stripExt(); // vmname
10841
10842 AssertReturn(!strMachineDirName.isEmpty(), false);
10843 AssertReturn(!strConfigFileOnly.isEmpty(), false);
10844
10845 return strMachineDirName == strConfigFileOnly;
10846}
10847
10848/**
10849 * Discards all changes to machine settings.
10850 *
10851 * @param aNotify Whether to notify the direct session about changes or not.
10852 *
10853 * @note Locks objects for writing!
10854 */
10855void Machine::rollback(bool aNotify)
10856{
10857 AutoCaller autoCaller(this);
10858 AssertComRCReturn(autoCaller.rc(), (void)0);
10859
10860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10861
10862 if (!mStorageControllers.isNull())
10863 {
10864 if (mStorageControllers.isBackedUp())
10865 {
10866 /* unitialize all new devices (absent in the backed up list). */
10867 StorageControllerList::const_iterator it = mStorageControllers->begin();
10868 StorageControllerList *backedList = mStorageControllers.backedUpData();
10869 while (it != mStorageControllers->end())
10870 {
10871 if ( std::find(backedList->begin(), backedList->end(), *it)
10872 == backedList->end()
10873 )
10874 {
10875 (*it)->uninit();
10876 }
10877 ++it;
10878 }
10879
10880 /* restore the list */
10881 mStorageControllers.rollback();
10882 }
10883
10884 /* rollback any changes to devices after restoring the list */
10885 if (mData->flModifications & IsModified_Storage)
10886 {
10887 StorageControllerList::const_iterator it = mStorageControllers->begin();
10888 while (it != mStorageControllers->end())
10889 {
10890 (*it)->rollback();
10891 ++it;
10892 }
10893 }
10894 }
10895
10896 mUserData.rollback();
10897
10898 mHWData.rollback();
10899
10900 if (mData->flModifications & IsModified_Storage)
10901 rollbackMedia();
10902
10903 if (mBIOSSettings)
10904 mBIOSSettings->rollback();
10905
10906 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
10907 mVRDEServer->rollback();
10908
10909 if (mAudioAdapter)
10910 mAudioAdapter->rollback();
10911
10912 if (mUSBController && (mData->flModifications & IsModified_USB))
10913 mUSBController->rollback();
10914
10915 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
10916 mBandwidthControl->rollback();
10917
10918 if (!mHWData.isNull())
10919 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
10920 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
10921 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
10922 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
10923
10924 if (mData->flModifications & IsModified_NetworkAdapters)
10925 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
10926 if ( mNetworkAdapters[slot]
10927 && mNetworkAdapters[slot]->isModified())
10928 {
10929 mNetworkAdapters[slot]->rollback();
10930 networkAdapters[slot] = mNetworkAdapters[slot];
10931 }
10932
10933 if (mData->flModifications & IsModified_SerialPorts)
10934 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
10935 if ( mSerialPorts[slot]
10936 && mSerialPorts[slot]->isModified())
10937 {
10938 mSerialPorts[slot]->rollback();
10939 serialPorts[slot] = mSerialPorts[slot];
10940 }
10941
10942 if (mData->flModifications & IsModified_ParallelPorts)
10943 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
10944 if ( mParallelPorts[slot]
10945 && mParallelPorts[slot]->isModified())
10946 {
10947 mParallelPorts[slot]->rollback();
10948 parallelPorts[slot] = mParallelPorts[slot];
10949 }
10950
10951 if (aNotify)
10952 {
10953 /* inform the direct session about changes */
10954
10955 ComObjPtr<Machine> that = this;
10956 uint32_t flModifications = mData->flModifications;
10957 alock.release();
10958
10959 if (flModifications & IsModified_SharedFolders)
10960 that->onSharedFolderChange();
10961
10962 if (flModifications & IsModified_VRDEServer)
10963 that->onVRDEServerChange(/* aRestart */ TRUE);
10964 if (flModifications & IsModified_USB)
10965 that->onUSBControllerChange();
10966
10967 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
10968 if (networkAdapters[slot])
10969 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
10970 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
10971 if (serialPorts[slot])
10972 that->onSerialPortChange(serialPorts[slot]);
10973 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
10974 if (parallelPorts[slot])
10975 that->onParallelPortChange(parallelPorts[slot]);
10976
10977 if (flModifications & IsModified_Storage)
10978 that->onStorageControllerChange();
10979
10980#if 0
10981 if (flModifications & IsModified_BandwidthControl)
10982 that->onBandwidthControlChange();
10983#endif
10984 }
10985}
10986
10987/**
10988 * Commits all the changes to machine settings.
10989 *
10990 * Note that this operation is supposed to never fail.
10991 *
10992 * @note Locks this object and children for writing.
10993 */
10994void Machine::commit()
10995{
10996 AutoCaller autoCaller(this);
10997 AssertComRCReturnVoid(autoCaller.rc());
10998
10999 AutoCaller peerCaller(mPeer);
11000 AssertComRCReturnVoid(peerCaller.rc());
11001
11002 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11003
11004 /*
11005 * use safe commit to ensure Snapshot machines (that share mUserData)
11006 * will still refer to a valid memory location
11007 */
11008 mUserData.commitCopy();
11009
11010 mHWData.commit();
11011
11012 if (mMediaData.isBackedUp())
11013 commitMedia();
11014
11015 mBIOSSettings->commit();
11016 mVRDEServer->commit();
11017 mAudioAdapter->commit();
11018 mUSBController->commit();
11019 mBandwidthControl->commit();
11020
11021 /* Since mNetworkAdapters is a list which might have been changed (resized)
11022 * without using the Backupable<> template we need to handle the copying
11023 * of the list entries manually, including the creation of peers for the
11024 * new objects. */
11025 bool commitNetworkAdapters = false;
11026 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11027 if (mPeer)
11028 {
11029 /* commit everything, even the ones which will go away */
11030 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11031 mNetworkAdapters[slot]->commit();
11032 /* copy over the new entries, creating a peer and uninit the original */
11033 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11034 for (size_t slot = 0; slot < newSize; slot++)
11035 {
11036 /* look if this adapter has a peer device */
11037 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
11038 if (!peer)
11039 {
11040 /* no peer means the adapter is a newly created one;
11041 * create a peer owning data this data share it with */
11042 peer.createObject();
11043 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11044 }
11045 mPeer->mNetworkAdapters[slot] = peer;
11046 }
11047 /* uninit any no longer needed network adapters */
11048 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
11049 mNetworkAdapters[slot]->uninit();
11050 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
11051 {
11052 if (mPeer->mNetworkAdapters[slot])
11053 mPeer->mNetworkAdapters[slot]->uninit();
11054 }
11055 /* Keep the original network adapter count until this point, so that
11056 * discarding a chipset type change will not lose settings. */
11057 mNetworkAdapters.resize(newSize);
11058 mPeer->mNetworkAdapters.resize(newSize);
11059 }
11060 else
11061 {
11062 /* we have no peer (our parent is the newly created machine);
11063 * just commit changes to the network adapters */
11064 commitNetworkAdapters = true;
11065 }
11066 if (commitNetworkAdapters)
11067 {
11068 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11069 mNetworkAdapters[slot]->commit();
11070 }
11071
11072 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11073 mSerialPorts[slot]->commit();
11074 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11075 mParallelPorts[slot]->commit();
11076
11077 bool commitStorageControllers = false;
11078
11079 if (mStorageControllers.isBackedUp())
11080 {
11081 mStorageControllers.commit();
11082
11083 if (mPeer)
11084 {
11085 /* Commit all changes to new controllers (this will reshare data with
11086 * peers for those who have peers) */
11087 StorageControllerList *newList = new StorageControllerList();
11088 StorageControllerList::const_iterator it = mStorageControllers->begin();
11089 while (it != mStorageControllers->end())
11090 {
11091 (*it)->commit();
11092
11093 /* look if this controller has a peer device */
11094 ComObjPtr<StorageController> peer = (*it)->getPeer();
11095 if (!peer)
11096 {
11097 /* no peer means the device is a newly created one;
11098 * create a peer owning data this device share it with */
11099 peer.createObject();
11100 peer->init(mPeer, *it, true /* aReshare */);
11101 }
11102 else
11103 {
11104 /* remove peer from the old list */
11105 mPeer->mStorageControllers->remove(peer);
11106 }
11107 /* and add it to the new list */
11108 newList->push_back(peer);
11109
11110 ++it;
11111 }
11112
11113 /* uninit old peer's controllers that are left */
11114 it = mPeer->mStorageControllers->begin();
11115 while (it != mPeer->mStorageControllers->end())
11116 {
11117 (*it)->uninit();
11118 ++it;
11119 }
11120
11121 /* attach new list of controllers to our peer */
11122 mPeer->mStorageControllers.attach(newList);
11123 }
11124 else
11125 {
11126 /* we have no peer (our parent is the newly created machine);
11127 * just commit changes to devices */
11128 commitStorageControllers = true;
11129 }
11130 }
11131 else
11132 {
11133 /* the list of controllers itself is not changed,
11134 * just commit changes to controllers themselves */
11135 commitStorageControllers = true;
11136 }
11137
11138 if (commitStorageControllers)
11139 {
11140 StorageControllerList::const_iterator it = mStorageControllers->begin();
11141 while (it != mStorageControllers->end())
11142 {
11143 (*it)->commit();
11144 ++it;
11145 }
11146 }
11147
11148 if (isSessionMachine())
11149 {
11150 /* attach new data to the primary machine and reshare it */
11151 mPeer->mUserData.attach(mUserData);
11152 mPeer->mHWData.attach(mHWData);
11153 /* mMediaData is reshared by fixupMedia */
11154 // mPeer->mMediaData.attach(mMediaData);
11155 Assert(mPeer->mMediaData.data() == mMediaData.data());
11156 }
11157}
11158
11159/**
11160 * Copies all the hardware data from the given machine.
11161 *
11162 * Currently, only called when the VM is being restored from a snapshot. In
11163 * particular, this implies that the VM is not running during this method's
11164 * call.
11165 *
11166 * @note This method must be called from under this object's lock.
11167 *
11168 * @note This method doesn't call #commit(), so all data remains backed up and
11169 * unsaved.
11170 */
11171void Machine::copyFrom(Machine *aThat)
11172{
11173 AssertReturnVoid(!isSnapshotMachine());
11174 AssertReturnVoid(aThat->isSnapshotMachine());
11175
11176 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11177
11178 mHWData.assignCopy(aThat->mHWData);
11179
11180 // create copies of all shared folders (mHWData after attaching a copy
11181 // contains just references to original objects)
11182 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11183 it != mHWData->mSharedFolders.end();
11184 ++it)
11185 {
11186 ComObjPtr<SharedFolder> folder;
11187 folder.createObject();
11188 HRESULT rc = folder->initCopy(getMachine(), *it);
11189 AssertComRC(rc);
11190 *it = folder;
11191 }
11192
11193 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
11194 mVRDEServer->copyFrom(aThat->mVRDEServer);
11195 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
11196 mUSBController->copyFrom(aThat->mUSBController);
11197 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
11198
11199 /* create private copies of all controllers */
11200 mStorageControllers.backup();
11201 mStorageControllers->clear();
11202 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11203 it != aThat->mStorageControllers->end();
11204 ++it)
11205 {
11206 ComObjPtr<StorageController> ctrl;
11207 ctrl.createObject();
11208 ctrl->initCopy(this, *it);
11209 mStorageControllers->push_back(ctrl);
11210 }
11211
11212 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11213 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
11214 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11215 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
11216 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11217 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
11218}
11219
11220/**
11221 * Returns whether the given storage controller is hotplug capable.
11222 *
11223 * @returns true if the controller supports hotplugging
11224 * false otherwise.
11225 * @param enmCtrlType The controller type to check for.
11226 */
11227bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11228{
11229 switch (enmCtrlType)
11230 {
11231 case StorageControllerType_IntelAhci:
11232 return true;
11233 case StorageControllerType_LsiLogic:
11234 case StorageControllerType_LsiLogicSas:
11235 case StorageControllerType_BusLogic:
11236 case StorageControllerType_PIIX3:
11237 case StorageControllerType_PIIX4:
11238 case StorageControllerType_ICH6:
11239 case StorageControllerType_I82078:
11240 default:
11241 return false;
11242 }
11243}
11244
11245#ifdef VBOX_WITH_RESOURCE_USAGE_API
11246
11247void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11248{
11249 AssertReturnVoid(isWriteLockOnCurrentThread());
11250 AssertPtrReturnVoid(aCollector);
11251
11252 pm::CollectorHAL *hal = aCollector->getHAL();
11253 /* Create sub metrics */
11254 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11255 "Percentage of processor time spent in user mode by the VM process.");
11256 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11257 "Percentage of processor time spent in kernel mode by the VM process.");
11258 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11259 "Size of resident portion of VM process in memory.");
11260 /* Create and register base metrics */
11261 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11262 cpuLoadUser, cpuLoadKernel);
11263 aCollector->registerBaseMetric(cpuLoad);
11264 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11265 ramUsageUsed);
11266 aCollector->registerBaseMetric(ramUsage);
11267
11268 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11269 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11270 new pm::AggregateAvg()));
11271 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11272 new pm::AggregateMin()));
11273 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11274 new pm::AggregateMax()));
11275 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11276 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11277 new pm::AggregateAvg()));
11278 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11279 new pm::AggregateMin()));
11280 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11281 new pm::AggregateMax()));
11282
11283 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11284 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11285 new pm::AggregateAvg()));
11286 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11287 new pm::AggregateMin()));
11288 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11289 new pm::AggregateMax()));
11290
11291
11292 /* Guest metrics collector */
11293 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11294 aCollector->registerGuest(mCollectorGuest);
11295 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11296 this, __PRETTY_FUNCTION__, mCollectorGuest));
11297
11298 /* Create sub metrics */
11299 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11300 "Percentage of processor time spent in user mode as seen by the guest.");
11301 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11302 "Percentage of processor time spent in kernel mode as seen by the guest.");
11303 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11304 "Percentage of processor time spent idling as seen by the guest.");
11305
11306 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11307 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11308 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11309 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11310 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11311 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11312
11313 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11314
11315 /* Create and register base metrics */
11316 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11317 guestLoadUser, guestLoadKernel, guestLoadIdle);
11318 aCollector->registerBaseMetric(guestCpuLoad);
11319
11320 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11321 guestMemTotal, guestMemFree,
11322 guestMemBalloon, guestMemShared,
11323 guestMemCache, guestPagedTotal);
11324 aCollector->registerBaseMetric(guestCpuMem);
11325
11326 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11327 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11328 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11329 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11330
11331 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11332 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11333 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11334 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11335
11336 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11337 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11338 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11339 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11340
11341 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11342 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11343 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11344 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11345
11346 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11347 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11348 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11349 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11350
11351 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11352 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11353 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11354 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11355
11356 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11357 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11358 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11359 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11360
11361 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11362 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11363 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11364 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11365
11366 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11367 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11368 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11369 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11370}
11371
11372void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11373{
11374 AssertReturnVoid(isWriteLockOnCurrentThread());
11375
11376 if (aCollector)
11377 {
11378 aCollector->unregisterMetricsFor(aMachine);
11379 aCollector->unregisterBaseMetricsFor(aMachine);
11380 }
11381}
11382
11383#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11384
11385
11386////////////////////////////////////////////////////////////////////////////////
11387
11388DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11389
11390HRESULT SessionMachine::FinalConstruct()
11391{
11392 LogFlowThisFunc(("\n"));
11393
11394#if defined(RT_OS_WINDOWS)
11395 mIPCSem = NULL;
11396#elif defined(RT_OS_OS2)
11397 mIPCSem = NULLHANDLE;
11398#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11399 mIPCSem = -1;
11400#else
11401# error "Port me!"
11402#endif
11403
11404 return BaseFinalConstruct();
11405}
11406
11407void SessionMachine::FinalRelease()
11408{
11409 LogFlowThisFunc(("\n"));
11410
11411 uninit(Uninit::Unexpected);
11412
11413 BaseFinalRelease();
11414}
11415
11416/**
11417 * @note Must be called only by Machine::openSession() from its own write lock.
11418 */
11419HRESULT SessionMachine::init(Machine *aMachine)
11420{
11421 LogFlowThisFuncEnter();
11422 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
11423
11424 AssertReturn(aMachine, E_INVALIDARG);
11425
11426 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
11427
11428 /* Enclose the state transition NotReady->InInit->Ready */
11429 AutoInitSpan autoInitSpan(this);
11430 AssertReturn(autoInitSpan.isOk(), E_FAIL);
11431
11432 /* create the interprocess semaphore */
11433#if defined(RT_OS_WINDOWS)
11434 mIPCSemName = aMachine->mData->m_strConfigFileFull;
11435 for (size_t i = 0; i < mIPCSemName.length(); i++)
11436 if (mIPCSemName.raw()[i] == '\\')
11437 mIPCSemName.raw()[i] = '/';
11438 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
11439 ComAssertMsgRet(mIPCSem,
11440 ("Cannot create IPC mutex '%ls', err=%d",
11441 mIPCSemName.raw(), ::GetLastError()),
11442 E_FAIL);
11443#elif defined(RT_OS_OS2)
11444 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
11445 aMachine->mData->mUuid.raw());
11446 mIPCSemName = ipcSem;
11447 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
11448 ComAssertMsgRet(arc == NO_ERROR,
11449 ("Cannot create IPC mutex '%s', arc=%ld",
11450 ipcSem.c_str(), arc),
11451 E_FAIL);
11452#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11453# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11454# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
11455 /** @todo Check that this still works correctly. */
11456 AssertCompileSize(key_t, 8);
11457# else
11458 AssertCompileSize(key_t, 4);
11459# endif
11460 key_t key;
11461 mIPCSem = -1;
11462 mIPCKey = "0";
11463 for (uint32_t i = 0; i < 1 << 24; i++)
11464 {
11465 key = ((uint32_t)'V' << 24) | i;
11466 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
11467 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
11468 {
11469 mIPCSem = sem;
11470 if (sem >= 0)
11471 mIPCKey = BstrFmt("%u", key);
11472 break;
11473 }
11474 }
11475# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11476 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
11477 char *pszSemName = NULL;
11478 RTStrUtf8ToCurrentCP(&pszSemName, semName);
11479 key_t key = ::ftok(pszSemName, 'V');
11480 RTStrFree(pszSemName);
11481
11482 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
11483# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11484
11485 int errnoSave = errno;
11486 if (mIPCSem < 0 && errnoSave == ENOSYS)
11487 {
11488 setError(E_FAIL,
11489 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
11490 "support for SysV IPC. Check the host kernel configuration for "
11491 "CONFIG_SYSVIPC=y"));
11492 return E_FAIL;
11493 }
11494 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
11495 * the IPC semaphores */
11496 if (mIPCSem < 0 && errnoSave == ENOSPC)
11497 {
11498#ifdef RT_OS_LINUX
11499 setError(E_FAIL,
11500 tr("Cannot create IPC semaphore because the system limit for the "
11501 "maximum number of semaphore sets (SEMMNI), or the system wide "
11502 "maximum number of semaphores (SEMMNS) would be exceeded. The "
11503 "current set of SysV IPC semaphores can be determined from "
11504 "the file /proc/sysvipc/sem"));
11505#else
11506 setError(E_FAIL,
11507 tr("Cannot create IPC semaphore because the system-imposed limit "
11508 "on the maximum number of allowed semaphores or semaphore "
11509 "identifiers system-wide would be exceeded"));
11510#endif
11511 return E_FAIL;
11512 }
11513 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
11514 E_FAIL);
11515 /* set the initial value to 1 */
11516 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
11517 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
11518 E_FAIL);
11519#else
11520# error "Port me!"
11521#endif
11522
11523 /* memorize the peer Machine */
11524 unconst(mPeer) = aMachine;
11525 /* share the parent pointer */
11526 unconst(mParent) = aMachine->mParent;
11527
11528 /* take the pointers to data to share */
11529 mData.share(aMachine->mData);
11530 mSSData.share(aMachine->mSSData);
11531
11532 mUserData.share(aMachine->mUserData);
11533 mHWData.share(aMachine->mHWData);
11534 mMediaData.share(aMachine->mMediaData);
11535
11536 mStorageControllers.allocate();
11537 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
11538 it != aMachine->mStorageControllers->end();
11539 ++it)
11540 {
11541 ComObjPtr<StorageController> ctl;
11542 ctl.createObject();
11543 ctl->init(this, *it);
11544 mStorageControllers->push_back(ctl);
11545 }
11546
11547 unconst(mBIOSSettings).createObject();
11548 mBIOSSettings->init(this, aMachine->mBIOSSettings);
11549 /* create another VRDEServer object that will be mutable */
11550 unconst(mVRDEServer).createObject();
11551 mVRDEServer->init(this, aMachine->mVRDEServer);
11552 /* create another audio adapter object that will be mutable */
11553 unconst(mAudioAdapter).createObject();
11554 mAudioAdapter->init(this, aMachine->mAudioAdapter);
11555 /* create a list of serial ports that will be mutable */
11556 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11557 {
11558 unconst(mSerialPorts[slot]).createObject();
11559 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
11560 }
11561 /* create a list of parallel ports that will be mutable */
11562 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11563 {
11564 unconst(mParallelPorts[slot]).createObject();
11565 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
11566 }
11567 /* create another USB controller object that will be mutable */
11568 unconst(mUSBController).createObject();
11569 mUSBController->init(this, aMachine->mUSBController);
11570
11571 /* create a list of network adapters that will be mutable */
11572 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
11573 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11574 {
11575 unconst(mNetworkAdapters[slot]).createObject();
11576 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
11577 }
11578
11579 /* create another bandwidth control object that will be mutable */
11580 unconst(mBandwidthControl).createObject();
11581 mBandwidthControl->init(this, aMachine->mBandwidthControl);
11582
11583 /* default is to delete saved state on Saved -> PoweredOff transition */
11584 mRemoveSavedState = true;
11585
11586 /* Confirm a successful initialization when it's the case */
11587 autoInitSpan.setSucceeded();
11588
11589 LogFlowThisFuncLeave();
11590 return S_OK;
11591}
11592
11593/**
11594 * Uninitializes this session object. If the reason is other than
11595 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
11596 *
11597 * @param aReason uninitialization reason
11598 *
11599 * @note Locks mParent + this object for writing.
11600 */
11601void SessionMachine::uninit(Uninit::Reason aReason)
11602{
11603 LogFlowThisFuncEnter();
11604 LogFlowThisFunc(("reason=%d\n", aReason));
11605
11606 /*
11607 * Strongly reference ourselves to prevent this object deletion after
11608 * mData->mSession.mMachine.setNull() below (which can release the last
11609 * reference and call the destructor). Important: this must be done before
11610 * accessing any members (and before AutoUninitSpan that does it as well).
11611 * This self reference will be released as the very last step on return.
11612 */
11613 ComObjPtr<SessionMachine> selfRef = this;
11614
11615 /* Enclose the state transition Ready->InUninit->NotReady */
11616 AutoUninitSpan autoUninitSpan(this);
11617 if (autoUninitSpan.uninitDone())
11618 {
11619 LogFlowThisFunc(("Already uninitialized\n"));
11620 LogFlowThisFuncLeave();
11621 return;
11622 }
11623
11624 if (autoUninitSpan.initFailed())
11625 {
11626 /* We've been called by init() because it's failed. It's not really
11627 * necessary (nor it's safe) to perform the regular uninit sequence
11628 * below, the following is enough.
11629 */
11630 LogFlowThisFunc(("Initialization failed.\n"));
11631#if defined(RT_OS_WINDOWS)
11632 if (mIPCSem)
11633 ::CloseHandle(mIPCSem);
11634 mIPCSem = NULL;
11635#elif defined(RT_OS_OS2)
11636 if (mIPCSem != NULLHANDLE)
11637 ::DosCloseMutexSem(mIPCSem);
11638 mIPCSem = NULLHANDLE;
11639#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11640 if (mIPCSem >= 0)
11641 ::semctl(mIPCSem, 0, IPC_RMID);
11642 mIPCSem = -1;
11643# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11644 mIPCKey = "0";
11645# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
11646#else
11647# error "Port me!"
11648#endif
11649 uninitDataAndChildObjects();
11650 mData.free();
11651 unconst(mParent) = NULL;
11652 unconst(mPeer) = NULL;
11653 LogFlowThisFuncLeave();
11654 return;
11655 }
11656
11657 MachineState_T lastState;
11658 {
11659 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
11660 lastState = mData->mMachineState;
11661 }
11662 NOREF(lastState);
11663
11664#ifdef VBOX_WITH_USB
11665 // release all captured USB devices, but do this before requesting the locks below
11666 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
11667 {
11668 /* Console::captureUSBDevices() is called in the VM process only after
11669 * setting the machine state to Starting or Restoring.
11670 * Console::detachAllUSBDevices() will be called upon successful
11671 * termination. So, we need to release USB devices only if there was
11672 * an abnormal termination of a running VM.
11673 *
11674 * This is identical to SessionMachine::DetachAllUSBDevices except
11675 * for the aAbnormal argument. */
11676 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
11677 AssertComRC(rc);
11678 NOREF(rc);
11679
11680 USBProxyService *service = mParent->host()->usbProxyService();
11681 if (service)
11682 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
11683 }
11684#endif /* VBOX_WITH_USB */
11685
11686 // we need to lock this object in uninit() because the lock is shared
11687 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
11688 // and others need mParent lock, and USB needs host lock.
11689 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
11690
11691#if 0
11692 // Trigger async cleanup tasks, avoid doing things here which are not
11693 // vital to be done immediately and maybe need more locks. This calls
11694 // Machine::unregisterMetrics().
11695 mParent->onMachineUninit(mPeer);
11696#else
11697 /*
11698 * It is safe to call Machine::unregisterMetrics() here because
11699 * PerformanceCollector::samplerCallback no longer accesses guest methods
11700 * holding the lock.
11701 */
11702 unregisterMetrics(mParent->performanceCollector(), mPeer);
11703#endif
11704 /* The guest must be unregistered after its metrics (@bugref{5949}). */
11705 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11706 this, __PRETTY_FUNCTION__, mCollectorGuest));
11707 if (mCollectorGuest)
11708 {
11709 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
11710 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
11711 mCollectorGuest = NULL;
11712 }
11713
11714 if (aReason == Uninit::Abnormal)
11715 {
11716 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
11717 Global::IsOnlineOrTransient(lastState)));
11718
11719 /* reset the state to Aborted */
11720 if (mData->mMachineState != MachineState_Aborted)
11721 setMachineState(MachineState_Aborted);
11722 }
11723
11724 // any machine settings modified?
11725 if (mData->flModifications)
11726 {
11727 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
11728 rollback(false /* aNotify */);
11729 }
11730
11731 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
11732 || !mConsoleTaskData.mSnapshot);
11733 if (!mConsoleTaskData.strStateFilePath.isEmpty())
11734 {
11735 LogWarningThisFunc(("canceling failed save state request!\n"));
11736 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
11737 }
11738 else if (!mConsoleTaskData.mSnapshot.isNull())
11739 {
11740 LogWarningThisFunc(("canceling untaken snapshot!\n"));
11741
11742 /* delete all differencing hard disks created (this will also attach
11743 * their parents back by rolling back mMediaData) */
11744 rollbackMedia();
11745
11746 // delete the saved state file (it might have been already created)
11747 // AFTER killing the snapshot so that releaseSavedStateFile() won't
11748 // think it's still in use
11749 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
11750 mConsoleTaskData.mSnapshot->uninit();
11751 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
11752 }
11753
11754 if (!mData->mSession.mType.isEmpty())
11755 {
11756 /* mType is not null when this machine's process has been started by
11757 * Machine::LaunchVMProcess(), therefore it is our child. We
11758 * need to queue the PID to reap the process (and avoid zombies on
11759 * Linux). */
11760 Assert(mData->mSession.mPID != NIL_RTPROCESS);
11761 mParent->addProcessToReap(mData->mSession.mPID);
11762 }
11763
11764 mData->mSession.mPID = NIL_RTPROCESS;
11765
11766 if (aReason == Uninit::Unexpected)
11767 {
11768 /* Uninitialization didn't come from #checkForDeath(), so tell the
11769 * client watcher thread to update the set of machines that have open
11770 * sessions. */
11771 mParent->updateClientWatcher();
11772 }
11773
11774 /* uninitialize all remote controls */
11775 if (mData->mSession.mRemoteControls.size())
11776 {
11777 LogFlowThisFunc(("Closing remote sessions (%d):\n",
11778 mData->mSession.mRemoteControls.size()));
11779
11780 Data::Session::RemoteControlList::iterator it =
11781 mData->mSession.mRemoteControls.begin();
11782 while (it != mData->mSession.mRemoteControls.end())
11783 {
11784 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
11785 HRESULT rc = (*it)->Uninitialize();
11786 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
11787 if (FAILED(rc))
11788 LogWarningThisFunc(("Forgot to close the remote session?\n"));
11789 ++it;
11790 }
11791 mData->mSession.mRemoteControls.clear();
11792 }
11793
11794 /*
11795 * An expected uninitialization can come only from #checkForDeath().
11796 * Otherwise it means that something's gone really wrong (for example,
11797 * the Session implementation has released the VirtualBox reference
11798 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
11799 * etc). However, it's also possible, that the client releases the IPC
11800 * semaphore correctly (i.e. before it releases the VirtualBox reference),
11801 * but the VirtualBox release event comes first to the server process.
11802 * This case is practically possible, so we should not assert on an
11803 * unexpected uninit, just log a warning.
11804 */
11805
11806 if ((aReason == Uninit::Unexpected))
11807 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
11808
11809 if (aReason != Uninit::Normal)
11810 {
11811 mData->mSession.mDirectControl.setNull();
11812 }
11813 else
11814 {
11815 /* this must be null here (see #OnSessionEnd()) */
11816 Assert(mData->mSession.mDirectControl.isNull());
11817 Assert(mData->mSession.mState == SessionState_Unlocking);
11818 Assert(!mData->mSession.mProgress.isNull());
11819 }
11820 if (mData->mSession.mProgress)
11821 {
11822 if (aReason == Uninit::Normal)
11823 mData->mSession.mProgress->notifyComplete(S_OK);
11824 else
11825 mData->mSession.mProgress->notifyComplete(E_FAIL,
11826 COM_IIDOF(ISession),
11827 getComponentName(),
11828 tr("The VM session was aborted"));
11829 mData->mSession.mProgress.setNull();
11830 }
11831
11832 /* remove the association between the peer machine and this session machine */
11833 Assert( (SessionMachine*)mData->mSession.mMachine == this
11834 || aReason == Uninit::Unexpected);
11835
11836 /* reset the rest of session data */
11837 mData->mSession.mMachine.setNull();
11838 mData->mSession.mState = SessionState_Unlocked;
11839 mData->mSession.mType.setNull();
11840
11841 /* close the interprocess semaphore before leaving the exclusive lock */
11842#if defined(RT_OS_WINDOWS)
11843 if (mIPCSem)
11844 ::CloseHandle(mIPCSem);
11845 mIPCSem = NULL;
11846#elif defined(RT_OS_OS2)
11847 if (mIPCSem != NULLHANDLE)
11848 ::DosCloseMutexSem(mIPCSem);
11849 mIPCSem = NULLHANDLE;
11850#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11851 if (mIPCSem >= 0)
11852 ::semctl(mIPCSem, 0, IPC_RMID);
11853 mIPCSem = -1;
11854# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11855 mIPCKey = "0";
11856# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
11857#else
11858# error "Port me!"
11859#endif
11860
11861 /* fire an event */
11862 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
11863
11864 uninitDataAndChildObjects();
11865
11866 /* free the essential data structure last */
11867 mData.free();
11868
11869 /* release the exclusive lock before setting the below two to NULL */
11870 multilock.release();
11871
11872 unconst(mParent) = NULL;
11873 unconst(mPeer) = NULL;
11874
11875 LogFlowThisFuncLeave();
11876}
11877
11878// util::Lockable interface
11879////////////////////////////////////////////////////////////////////////////////
11880
11881/**
11882 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
11883 * with the primary Machine instance (mPeer).
11884 */
11885RWLockHandle *SessionMachine::lockHandle() const
11886{
11887 AssertReturn(mPeer != NULL, NULL);
11888 return mPeer->lockHandle();
11889}
11890
11891// IInternalMachineControl methods
11892////////////////////////////////////////////////////////////////////////////////
11893
11894/**
11895 * Passes collected guest statistics to performance collector object
11896 */
11897STDMETHODIMP SessionMachine::ReportGuestStatistics(ULONG aValidStats, ULONG aCpuUser,
11898 ULONG aCpuKernel, ULONG aCpuIdle,
11899 ULONG aMemTotal, ULONG aMemFree,
11900 ULONG aMemBalloon, ULONG aMemShared,
11901 ULONG aMemCache, ULONG aPageTotal,
11902 ULONG aAllocVMM, ULONG aFreeVMM,
11903 ULONG aBalloonedVMM, ULONG aSharedVMM)
11904{
11905 if (mCollectorGuest)
11906 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
11907 aMemTotal, aMemFree, aMemBalloon, aMemShared,
11908 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
11909 aBalloonedVMM, aSharedVMM);
11910
11911 return S_OK;
11912}
11913
11914/**
11915 * @note Locks this object for writing.
11916 */
11917STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
11918{
11919 AutoCaller autoCaller(this);
11920 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11921
11922 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11923
11924 mRemoveSavedState = aRemove;
11925
11926 return S_OK;
11927}
11928
11929/**
11930 * @note Locks the same as #setMachineState() does.
11931 */
11932STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
11933{
11934 return setMachineState(aMachineState);
11935}
11936
11937/**
11938 * @note Locks this object for reading.
11939 */
11940STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
11941{
11942 AutoCaller autoCaller(this);
11943 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11944
11945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11946
11947#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
11948 mIPCSemName.cloneTo(aId);
11949 return S_OK;
11950#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11951# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11952 mIPCKey.cloneTo(aId);
11953# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11954 mData->m_strConfigFileFull.cloneTo(aId);
11955# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11956 return S_OK;
11957#else
11958# error "Port me!"
11959#endif
11960}
11961
11962/**
11963 * @note Locks this object for writing.
11964 */
11965STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
11966{
11967 LogFlowThisFunc(("aProgress=%p\n", aProgress));
11968 AutoCaller autoCaller(this);
11969 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11970
11971 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11972
11973 if (mData->mSession.mState != SessionState_Locked)
11974 return VBOX_E_INVALID_OBJECT_STATE;
11975
11976 if (!mData->mSession.mProgress.isNull())
11977 mData->mSession.mProgress->setOtherProgressObject(aProgress);
11978
11979 LogFlowThisFunc(("returns S_OK.\n"));
11980 return S_OK;
11981}
11982
11983/**
11984 * @note Locks this object for writing.
11985 */
11986STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
11987{
11988 AutoCaller autoCaller(this);
11989 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11990
11991 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11992
11993 if (mData->mSession.mState != SessionState_Locked)
11994 return VBOX_E_INVALID_OBJECT_STATE;
11995
11996 /* Finalize the LaunchVMProcess progress object. */
11997 if (mData->mSession.mProgress)
11998 {
11999 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12000 mData->mSession.mProgress.setNull();
12001 }
12002
12003 if (SUCCEEDED((HRESULT)iResult))
12004 {
12005#ifdef VBOX_WITH_RESOURCE_USAGE_API
12006 /* The VM has been powered up successfully, so it makes sense
12007 * now to offer the performance metrics for a running machine
12008 * object. Doing it earlier wouldn't be safe. */
12009 registerMetrics(mParent->performanceCollector(), mPeer,
12010 mData->mSession.mPID);
12011#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12012 }
12013
12014 return S_OK;
12015}
12016
12017/**
12018 * @note Locks this object for writing.
12019 */
12020STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12021{
12022 LogFlowThisFuncEnter();
12023
12024 CheckComArgOutPointerValid(aProgress);
12025
12026 AutoCaller autoCaller(this);
12027 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12028
12029 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12030
12031 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12032 E_FAIL);
12033
12034 /* create a progress object to track operation completion */
12035 ComObjPtr<Progress> pProgress;
12036 pProgress.createObject();
12037 pProgress->init(getVirtualBox(),
12038 static_cast<IMachine *>(this) /* aInitiator */,
12039 Bstr(tr("Stopping the virtual machine")).raw(),
12040 FALSE /* aCancelable */);
12041
12042 /* fill in the console task data */
12043 mConsoleTaskData.mLastState = mData->mMachineState;
12044 mConsoleTaskData.mProgress = pProgress;
12045
12046 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12047 setMachineState(MachineState_Stopping);
12048
12049 pProgress.queryInterfaceTo(aProgress);
12050
12051 return S_OK;
12052}
12053
12054/**
12055 * @note Locks this object for writing.
12056 */
12057STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12058{
12059 LogFlowThisFuncEnter();
12060
12061 AutoCaller autoCaller(this);
12062 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12063
12064 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12065
12066 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12067 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12068 && mConsoleTaskData.mLastState != MachineState_Null,
12069 E_FAIL);
12070
12071 /*
12072 * On failure, set the state to the state we had when BeginPoweringDown()
12073 * was called (this is expected by Console::PowerDown() and the associated
12074 * task). On success the VM process already changed the state to
12075 * MachineState_PoweredOff, so no need to do anything.
12076 */
12077 if (FAILED(iResult))
12078 setMachineState(mConsoleTaskData.mLastState);
12079
12080 /* notify the progress object about operation completion */
12081 Assert(mConsoleTaskData.mProgress);
12082 if (SUCCEEDED(iResult))
12083 mConsoleTaskData.mProgress->notifyComplete(S_OK);
12084 else
12085 {
12086 Utf8Str strErrMsg(aErrMsg);
12087 if (strErrMsg.length())
12088 mConsoleTaskData.mProgress->notifyComplete(iResult,
12089 COM_IIDOF(ISession),
12090 getComponentName(),
12091 strErrMsg.c_str());
12092 else
12093 mConsoleTaskData.mProgress->notifyComplete(iResult);
12094 }
12095
12096 /* clear out the temporary saved state data */
12097 mConsoleTaskData.mLastState = MachineState_Null;
12098 mConsoleTaskData.mProgress.setNull();
12099
12100 LogFlowThisFuncLeave();
12101 return S_OK;
12102}
12103
12104
12105/**
12106 * Goes through the USB filters of the given machine to see if the given
12107 * device matches any filter or not.
12108 *
12109 * @note Locks the same as USBController::hasMatchingFilter() does.
12110 */
12111STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12112 BOOL *aMatched,
12113 ULONG *aMaskedIfs)
12114{
12115 LogFlowThisFunc(("\n"));
12116
12117 CheckComArgNotNull(aUSBDevice);
12118 CheckComArgOutPointerValid(aMatched);
12119
12120 AutoCaller autoCaller(this);
12121 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12122
12123#ifdef VBOX_WITH_USB
12124 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
12125#else
12126 NOREF(aUSBDevice);
12127 NOREF(aMaskedIfs);
12128 *aMatched = FALSE;
12129#endif
12130
12131 return S_OK;
12132}
12133
12134/**
12135 * @note Locks the same as Host::captureUSBDevice() does.
12136 */
12137STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12138{
12139 LogFlowThisFunc(("\n"));
12140
12141 AutoCaller autoCaller(this);
12142 AssertComRCReturnRC(autoCaller.rc());
12143
12144#ifdef VBOX_WITH_USB
12145 /* if captureDeviceForVM() fails, it must have set extended error info */
12146 clearError();
12147 MultiResult rc = mParent->host()->checkUSBProxyService();
12148 if (FAILED(rc)) return rc;
12149
12150 USBProxyService *service = mParent->host()->usbProxyService();
12151 AssertReturn(service, E_FAIL);
12152 return service->captureDeviceForVM(this, Guid(aId).ref());
12153#else
12154 NOREF(aId);
12155 return E_NOTIMPL;
12156#endif
12157}
12158
12159/**
12160 * @note Locks the same as Host::detachUSBDevice() does.
12161 */
12162STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12163{
12164 LogFlowThisFunc(("\n"));
12165
12166 AutoCaller autoCaller(this);
12167 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12168
12169#ifdef VBOX_WITH_USB
12170 USBProxyService *service = mParent->host()->usbProxyService();
12171 AssertReturn(service, E_FAIL);
12172 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12173#else
12174 NOREF(aId);
12175 NOREF(aDone);
12176 return E_NOTIMPL;
12177#endif
12178}
12179
12180/**
12181 * Inserts all machine filters to the USB proxy service and then calls
12182 * Host::autoCaptureUSBDevices().
12183 *
12184 * Called by Console from the VM process upon VM startup.
12185 *
12186 * @note Locks what called methods lock.
12187 */
12188STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12189{
12190 LogFlowThisFunc(("\n"));
12191
12192 AutoCaller autoCaller(this);
12193 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12194
12195#ifdef VBOX_WITH_USB
12196 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
12197 AssertComRC(rc);
12198 NOREF(rc);
12199
12200 USBProxyService *service = mParent->host()->usbProxyService();
12201 AssertReturn(service, E_FAIL);
12202 return service->autoCaptureDevicesForVM(this);
12203#else
12204 return S_OK;
12205#endif
12206}
12207
12208/**
12209 * Removes all machine filters from the USB proxy service and then calls
12210 * Host::detachAllUSBDevices().
12211 *
12212 * Called by Console from the VM process upon normal VM termination or by
12213 * SessionMachine::uninit() upon abnormal VM termination (from under the
12214 * Machine/SessionMachine lock).
12215 *
12216 * @note Locks what called methods lock.
12217 */
12218STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12219{
12220 LogFlowThisFunc(("\n"));
12221
12222 AutoCaller autoCaller(this);
12223 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12224
12225#ifdef VBOX_WITH_USB
12226 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12227 AssertComRC(rc);
12228 NOREF(rc);
12229
12230 USBProxyService *service = mParent->host()->usbProxyService();
12231 AssertReturn(service, E_FAIL);
12232 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12233#else
12234 NOREF(aDone);
12235 return S_OK;
12236#endif
12237}
12238
12239/**
12240 * @note Locks this object for writing.
12241 */
12242STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12243 IProgress **aProgress)
12244{
12245 LogFlowThisFuncEnter();
12246
12247 AssertReturn(aSession, E_INVALIDARG);
12248 AssertReturn(aProgress, E_INVALIDARG);
12249
12250 AutoCaller autoCaller(this);
12251
12252 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
12253 /*
12254 * We don't assert below because it might happen that a non-direct session
12255 * informs us it is closed right after we've been uninitialized -- it's ok.
12256 */
12257 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12258
12259 /* get IInternalSessionControl interface */
12260 ComPtr<IInternalSessionControl> control(aSession);
12261
12262 ComAssertRet(!control.isNull(), E_INVALIDARG);
12263
12264 /* Creating a Progress object requires the VirtualBox lock, and
12265 * thus locking it here is required by the lock order rules. */
12266 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12267
12268 if (control == mData->mSession.mDirectControl)
12269 {
12270 ComAssertRet(aProgress, E_POINTER);
12271
12272 /* The direct session is being normally closed by the client process
12273 * ----------------------------------------------------------------- */
12274
12275 /* go to the closing state (essential for all open*Session() calls and
12276 * for #checkForDeath()) */
12277 Assert(mData->mSession.mState == SessionState_Locked);
12278 mData->mSession.mState = SessionState_Unlocking;
12279
12280 /* set direct control to NULL to release the remote instance */
12281 mData->mSession.mDirectControl.setNull();
12282 LogFlowThisFunc(("Direct control is set to NULL\n"));
12283
12284 if (mData->mSession.mProgress)
12285 {
12286 /* finalize the progress, someone might wait if a frontend
12287 * closes the session before powering on the VM. */
12288 mData->mSession.mProgress->notifyComplete(E_FAIL,
12289 COM_IIDOF(ISession),
12290 getComponentName(),
12291 tr("The VM session was closed before any attempt to power it on"));
12292 mData->mSession.mProgress.setNull();
12293 }
12294
12295 /* Create the progress object the client will use to wait until
12296 * #checkForDeath() is called to uninitialize this session object after
12297 * it releases the IPC semaphore.
12298 * Note! Because we're "reusing" mProgress here, this must be a proxy
12299 * object just like for LaunchVMProcess. */
12300 Assert(mData->mSession.mProgress.isNull());
12301 ComObjPtr<ProgressProxy> progress;
12302 progress.createObject();
12303 ComPtr<IUnknown> pPeer(mPeer);
12304 progress->init(mParent, pPeer,
12305 Bstr(tr("Closing session")).raw(),
12306 FALSE /* aCancelable */);
12307 progress.queryInterfaceTo(aProgress);
12308 mData->mSession.mProgress = progress;
12309 }
12310 else
12311 {
12312 /* the remote session is being normally closed */
12313 Data::Session::RemoteControlList::iterator it =
12314 mData->mSession.mRemoteControls.begin();
12315 while (it != mData->mSession.mRemoteControls.end())
12316 {
12317 if (control == *it)
12318 break;
12319 ++it;
12320 }
12321 BOOL found = it != mData->mSession.mRemoteControls.end();
12322 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12323 E_INVALIDARG);
12324 // This MUST be erase(it), not remove(*it) as the latter triggers a
12325 // very nasty use after free due to the place where the value "lives".
12326 mData->mSession.mRemoteControls.erase(it);
12327 }
12328
12329 /* signal the client watcher thread, because the client is going away */
12330 mParent->updateClientWatcher();
12331
12332 LogFlowThisFuncLeave();
12333 return S_OK;
12334}
12335
12336/**
12337 * @note Locks this object for writing.
12338 */
12339STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12340{
12341 LogFlowThisFuncEnter();
12342
12343 CheckComArgOutPointerValid(aProgress);
12344 CheckComArgOutPointerValid(aStateFilePath);
12345
12346 AutoCaller autoCaller(this);
12347 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12348
12349 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12350
12351 AssertReturn( mData->mMachineState == MachineState_Paused
12352 && mConsoleTaskData.mLastState == MachineState_Null
12353 && mConsoleTaskData.strStateFilePath.isEmpty(),
12354 E_FAIL);
12355
12356 /* create a progress object to track operation completion */
12357 ComObjPtr<Progress> pProgress;
12358 pProgress.createObject();
12359 pProgress->init(getVirtualBox(),
12360 static_cast<IMachine *>(this) /* aInitiator */,
12361 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12362 FALSE /* aCancelable */);
12363
12364 Utf8Str strStateFilePath;
12365 /* stateFilePath is null when the machine is not running */
12366 if (mData->mMachineState == MachineState_Paused)
12367 composeSavedStateFilename(strStateFilePath);
12368
12369 /* fill in the console task data */
12370 mConsoleTaskData.mLastState = mData->mMachineState;
12371 mConsoleTaskData.strStateFilePath = strStateFilePath;
12372 mConsoleTaskData.mProgress = pProgress;
12373
12374 /* set the state to Saving (this is expected by Console::SaveState()) */
12375 setMachineState(MachineState_Saving);
12376
12377 strStateFilePath.cloneTo(aStateFilePath);
12378 pProgress.queryInterfaceTo(aProgress);
12379
12380 return S_OK;
12381}
12382
12383/**
12384 * @note Locks mParent + this object for writing.
12385 */
12386STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12387{
12388 LogFlowThisFunc(("\n"));
12389
12390 AutoCaller autoCaller(this);
12391 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12392
12393 /* endSavingState() need mParent lock */
12394 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12395
12396 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12397 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12398 && mConsoleTaskData.mLastState != MachineState_Null
12399 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12400 E_FAIL);
12401
12402 /*
12403 * On failure, set the state to the state we had when BeginSavingState()
12404 * was called (this is expected by Console::SaveState() and the associated
12405 * task). On success the VM process already changed the state to
12406 * MachineState_Saved, so no need to do anything.
12407 */
12408 if (FAILED(iResult))
12409 setMachineState(mConsoleTaskData.mLastState);
12410
12411 return endSavingState(iResult, aErrMsg);
12412}
12413
12414/**
12415 * @note Locks this object for writing.
12416 */
12417STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12418{
12419 LogFlowThisFunc(("\n"));
12420
12421 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12422
12423 AutoCaller autoCaller(this);
12424 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12425
12426 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12427
12428 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12429 || mData->mMachineState == MachineState_Teleported
12430 || mData->mMachineState == MachineState_Aborted
12431 , E_FAIL); /** @todo setError. */
12432
12433 Utf8Str stateFilePathFull = aSavedStateFile;
12434 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
12435 if (RT_FAILURE(vrc))
12436 return setError(VBOX_E_FILE_ERROR,
12437 tr("Invalid saved state file path '%ls' (%Rrc)"),
12438 aSavedStateFile,
12439 vrc);
12440
12441 mSSData->strStateFilePath = stateFilePathFull;
12442
12443 /* The below setMachineState() will detect the state transition and will
12444 * update the settings file */
12445
12446 return setMachineState(MachineState_Saved);
12447}
12448
12449STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
12450 ComSafeArrayOut(BSTR, aValues),
12451 ComSafeArrayOut(LONG64, aTimestamps),
12452 ComSafeArrayOut(BSTR, aFlags))
12453{
12454 LogFlowThisFunc(("\n"));
12455
12456#ifdef VBOX_WITH_GUEST_PROPS
12457 using namespace guestProp;
12458
12459 AutoCaller autoCaller(this);
12460 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12461
12462 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12463
12464 CheckComArgOutSafeArrayPointerValid(aNames);
12465 CheckComArgOutSafeArrayPointerValid(aValues);
12466 CheckComArgOutSafeArrayPointerValid(aTimestamps);
12467 CheckComArgOutSafeArrayPointerValid(aFlags);
12468
12469 size_t cEntries = mHWData->mGuestProperties.size();
12470 com::SafeArray<BSTR> names(cEntries);
12471 com::SafeArray<BSTR> values(cEntries);
12472 com::SafeArray<LONG64> timestamps(cEntries);
12473 com::SafeArray<BSTR> flags(cEntries);
12474 unsigned i = 0;
12475 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
12476 it != mHWData->mGuestProperties.end();
12477 ++it)
12478 {
12479 char szFlags[MAX_FLAGS_LEN + 1];
12480 it->strName.cloneTo(&names[i]);
12481 it->strValue.cloneTo(&values[i]);
12482 timestamps[i] = it->mTimestamp;
12483 /* If it is NULL, keep it NULL. */
12484 if (it->mFlags)
12485 {
12486 writeFlags(it->mFlags, szFlags);
12487 Bstr(szFlags).cloneTo(&flags[i]);
12488 }
12489 else
12490 flags[i] = NULL;
12491 ++i;
12492 }
12493 names.detachTo(ComSafeArrayOutArg(aNames));
12494 values.detachTo(ComSafeArrayOutArg(aValues));
12495 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
12496 flags.detachTo(ComSafeArrayOutArg(aFlags));
12497 return S_OK;
12498#else
12499 ReturnComNotImplemented();
12500#endif
12501}
12502
12503STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
12504 IN_BSTR aValue,
12505 LONG64 aTimestamp,
12506 IN_BSTR aFlags)
12507{
12508 LogFlowThisFunc(("\n"));
12509
12510#ifdef VBOX_WITH_GUEST_PROPS
12511 using namespace guestProp;
12512
12513 CheckComArgStrNotEmptyOrNull(aName);
12514 CheckComArgNotNull(aValue);
12515 CheckComArgNotNull(aFlags);
12516
12517 try
12518 {
12519 /*
12520 * Convert input up front.
12521 */
12522 Utf8Str utf8Name(aName);
12523 uint32_t fFlags = NILFLAG;
12524 if (aFlags)
12525 {
12526 Utf8Str utf8Flags(aFlags);
12527 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
12528 AssertRCReturn(vrc, E_INVALIDARG);
12529 }
12530
12531 /*
12532 * Now grab the object lock, validate the state and do the update.
12533 */
12534 AutoCaller autoCaller(this);
12535 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12536
12537 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12538
12539 switch (mData->mMachineState)
12540 {
12541 case MachineState_Paused:
12542 case MachineState_Running:
12543 case MachineState_Teleporting:
12544 case MachineState_TeleportingPausedVM:
12545 case MachineState_LiveSnapshotting:
12546 case MachineState_DeletingSnapshotOnline:
12547 case MachineState_DeletingSnapshotPaused:
12548 case MachineState_Saving:
12549 break;
12550
12551 default:
12552#ifndef DEBUG_sunlover
12553 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
12554 VBOX_E_INVALID_VM_STATE);
12555#else
12556 return VBOX_E_INVALID_VM_STATE;
12557#endif
12558 }
12559
12560 setModified(IsModified_MachineData);
12561 mHWData.backup();
12562
12563 /** @todo r=bird: The careful memory handling doesn't work out here because
12564 * the catch block won't undo any damage we've done. So, if push_back throws
12565 * bad_alloc then you've lost the value.
12566 *
12567 * Another thing. Doing a linear search here isn't extremely efficient, esp.
12568 * since values that changes actually bubbles to the end of the list. Using
12569 * something that has an efficient lookup and can tolerate a bit of updates
12570 * would be nice. RTStrSpace is one suggestion (it's not perfect). Some
12571 * combination of RTStrCache (for sharing names and getting uniqueness into
12572 * the bargain) and hash/tree is another. */
12573 for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();
12574 iter != mHWData->mGuestProperties.end();
12575 ++iter)
12576 if (utf8Name == iter->strName)
12577 {
12578 mHWData->mGuestProperties.erase(iter);
12579 mData->mGuestPropertiesModified = TRUE;
12580 break;
12581 }
12582 if (aValue != NULL)
12583 {
12584 HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };
12585 mHWData->mGuestProperties.push_back(property);
12586 mData->mGuestPropertiesModified = TRUE;
12587 }
12588
12589 /*
12590 * Send a callback notification if appropriate
12591 */
12592 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
12593 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
12594 RTSTR_MAX,
12595 utf8Name.c_str(),
12596 RTSTR_MAX, NULL)
12597 )
12598 {
12599 alock.release();
12600
12601 mParent->onGuestPropertyChange(mData->mUuid,
12602 aName,
12603 aValue,
12604 aFlags);
12605 }
12606 }
12607 catch (...)
12608 {
12609 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
12610 }
12611 return S_OK;
12612#else
12613 ReturnComNotImplemented();
12614#endif
12615}
12616
12617STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
12618 IMediumAttachment **aNewAttachment)
12619{
12620 CheckComArgNotNull(aAttachment);
12621 CheckComArgOutPointerValid(aNewAttachment);
12622
12623 AutoCaller autoCaller(this);
12624 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12625
12626 // request the host lock first, since might be calling Host methods for getting host drives;
12627 // next, protect the media tree all the while we're in here, as well as our member variables
12628 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
12629 this->lockHandle(),
12630 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
12631
12632 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
12633
12634 Bstr ctrlName;
12635 LONG lPort;
12636 LONG lDevice;
12637 bool fTempEject;
12638 {
12639 AutoCaller autoAttachCaller(this);
12640 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12641
12642 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12643
12644 /* Need to query the details first, as the IMediumAttachment reference
12645 * might be to the original settings, which we are going to change. */
12646 ctrlName = pAttach->getControllerName();
12647 lPort = pAttach->getPort();
12648 lDevice = pAttach->getDevice();
12649 fTempEject = pAttach->getTempEject();
12650 }
12651
12652 if (!fTempEject)
12653 {
12654 /* Remember previously mounted medium. The medium before taking the
12655 * backup is not necessarily the same thing. */
12656 ComObjPtr<Medium> oldmedium;
12657 oldmedium = pAttach->getMedium();
12658
12659 setModified(IsModified_Storage);
12660 mMediaData.backup();
12661
12662 // The backup operation makes the pAttach reference point to the
12663 // old settings. Re-get the correct reference.
12664 pAttach = findAttachment(mMediaData->mAttachments,
12665 ctrlName.raw(),
12666 lPort,
12667 lDevice);
12668
12669 {
12670 AutoCaller autoAttachCaller(this);
12671 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12672
12673 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12674 if (!oldmedium.isNull())
12675 oldmedium->removeBackReference(mData->mUuid);
12676
12677 pAttach->updateMedium(NULL);
12678 pAttach->updateEjected();
12679 }
12680
12681 setModified(IsModified_Storage);
12682 }
12683 else
12684 {
12685 {
12686 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12687 pAttach->updateEjected();
12688 }
12689 }
12690
12691 pAttach.queryInterfaceTo(aNewAttachment);
12692
12693 return S_OK;
12694}
12695
12696// public methods only for internal purposes
12697/////////////////////////////////////////////////////////////////////////////
12698
12699/**
12700 * Called from the client watcher thread to check for expected or unexpected
12701 * death of the client process that has a direct session to this machine.
12702 *
12703 * On Win32 and on OS/2, this method is called only when we've got the
12704 * mutex (i.e. the client has either died or terminated normally) so it always
12705 * returns @c true (the client is terminated, the session machine is
12706 * uninitialized).
12707 *
12708 * On other platforms, the method returns @c true if the client process has
12709 * terminated normally or abnormally and the session machine was uninitialized,
12710 * and @c false if the client process is still alive.
12711 *
12712 * @note Locks this object for writing.
12713 */
12714bool SessionMachine::checkForDeath()
12715{
12716 Uninit::Reason reason;
12717 bool terminated = false;
12718
12719 /* Enclose autoCaller with a block because calling uninit() from under it
12720 * will deadlock. */
12721 {
12722 AutoCaller autoCaller(this);
12723 if (!autoCaller.isOk())
12724 {
12725 /* return true if not ready, to cause the client watcher to exclude
12726 * the corresponding session from watching */
12727 LogFlowThisFunc(("Already uninitialized!\n"));
12728 return true;
12729 }
12730
12731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12732
12733 /* Determine the reason of death: if the session state is Closing here,
12734 * everything is fine. Otherwise it means that the client did not call
12735 * OnSessionEnd() before it released the IPC semaphore. This may happen
12736 * either because the client process has abnormally terminated, or
12737 * because it simply forgot to call ISession::Close() before exiting. We
12738 * threat the latter also as an abnormal termination (see
12739 * Session::uninit() for details). */
12740 reason = mData->mSession.mState == SessionState_Unlocking ?
12741 Uninit::Normal :
12742 Uninit::Abnormal;
12743
12744#if defined(RT_OS_WINDOWS)
12745
12746 AssertMsg(mIPCSem, ("semaphore must be created"));
12747
12748 /* release the IPC mutex */
12749 ::ReleaseMutex(mIPCSem);
12750
12751 terminated = true;
12752
12753#elif defined(RT_OS_OS2)
12754
12755 AssertMsg(mIPCSem, ("semaphore must be created"));
12756
12757 /* release the IPC mutex */
12758 ::DosReleaseMutexSem(mIPCSem);
12759
12760 terminated = true;
12761
12762#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12763
12764 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
12765
12766 int val = ::semctl(mIPCSem, 0, GETVAL);
12767 if (val > 0)
12768 {
12769 /* the semaphore is signaled, meaning the session is terminated */
12770 terminated = true;
12771 }
12772
12773#else
12774# error "Port me!"
12775#endif
12776
12777 } /* AutoCaller block */
12778
12779 if (terminated)
12780 uninit(reason);
12781
12782 return terminated;
12783}
12784
12785/**
12786 * @note Locks this object for reading.
12787 */
12788HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
12789{
12790 LogFlowThisFunc(("\n"));
12791
12792 AutoCaller autoCaller(this);
12793 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12794
12795 ComPtr<IInternalSessionControl> directControl;
12796 {
12797 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12798 directControl = mData->mSession.mDirectControl;
12799 }
12800
12801 /* ignore notifications sent after #OnSessionEnd() is called */
12802 if (!directControl)
12803 return S_OK;
12804
12805 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
12806}
12807
12808/**
12809 * @note Locks this object for reading.
12810 */
12811HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
12812 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
12813{
12814 LogFlowThisFunc(("\n"));
12815
12816 AutoCaller autoCaller(this);
12817 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12818
12819 ComPtr<IInternalSessionControl> directControl;
12820 {
12821 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12822 directControl = mData->mSession.mDirectControl;
12823 }
12824
12825 /* ignore notifications sent after #OnSessionEnd() is called */
12826 if (!directControl)
12827 return S_OK;
12828 /*
12829 * instead acting like callback we ask IVirtualBox deliver corresponding event
12830 */
12831
12832 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
12833 return S_OK;
12834}
12835
12836/**
12837 * @note Locks this object for reading.
12838 */
12839HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
12840{
12841 LogFlowThisFunc(("\n"));
12842
12843 AutoCaller autoCaller(this);
12844 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12845
12846 ComPtr<IInternalSessionControl> directControl;
12847 {
12848 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12849 directControl = mData->mSession.mDirectControl;
12850 }
12851
12852 /* ignore notifications sent after #OnSessionEnd() is called */
12853 if (!directControl)
12854 return S_OK;
12855
12856 return directControl->OnSerialPortChange(serialPort);
12857}
12858
12859/**
12860 * @note Locks this object for reading.
12861 */
12862HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
12863{
12864 LogFlowThisFunc(("\n"));
12865
12866 AutoCaller autoCaller(this);
12867 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12868
12869 ComPtr<IInternalSessionControl> directControl;
12870 {
12871 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12872 directControl = mData->mSession.mDirectControl;
12873 }
12874
12875 /* ignore notifications sent after #OnSessionEnd() is called */
12876 if (!directControl)
12877 return S_OK;
12878
12879 return directControl->OnParallelPortChange(parallelPort);
12880}
12881
12882/**
12883 * @note Locks this object for reading.
12884 */
12885HRESULT SessionMachine::onStorageControllerChange()
12886{
12887 LogFlowThisFunc(("\n"));
12888
12889 AutoCaller autoCaller(this);
12890 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12891
12892 ComPtr<IInternalSessionControl> directControl;
12893 {
12894 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12895 directControl = mData->mSession.mDirectControl;
12896 }
12897
12898 /* ignore notifications sent after #OnSessionEnd() is called */
12899 if (!directControl)
12900 return S_OK;
12901
12902 return directControl->OnStorageControllerChange();
12903}
12904
12905/**
12906 * @note Locks this object for reading.
12907 */
12908HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
12909{
12910 LogFlowThisFunc(("\n"));
12911
12912 AutoCaller autoCaller(this);
12913 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12914
12915 ComPtr<IInternalSessionControl> directControl;
12916 {
12917 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12918 directControl = mData->mSession.mDirectControl;
12919 }
12920
12921 /* ignore notifications sent after #OnSessionEnd() is called */
12922 if (!directControl)
12923 return S_OK;
12924
12925 return directControl->OnMediumChange(aAttachment, aForce);
12926}
12927
12928/**
12929 * @note Locks this object for reading.
12930 */
12931HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
12932{
12933 LogFlowThisFunc(("\n"));
12934
12935 AutoCaller autoCaller(this);
12936 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
12937
12938 ComPtr<IInternalSessionControl> directControl;
12939 {
12940 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12941 directControl = mData->mSession.mDirectControl;
12942 }
12943
12944 /* ignore notifications sent after #OnSessionEnd() is called */
12945 if (!directControl)
12946 return S_OK;
12947
12948 return directControl->OnCPUChange(aCPU, aRemove);
12949}
12950
12951HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
12952{
12953 LogFlowThisFunc(("\n"));
12954
12955 AutoCaller autoCaller(this);
12956 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
12957
12958 ComPtr<IInternalSessionControl> directControl;
12959 {
12960 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12961 directControl = mData->mSession.mDirectControl;
12962 }
12963
12964 /* ignore notifications sent after #OnSessionEnd() is called */
12965 if (!directControl)
12966 return S_OK;
12967
12968 return directControl->OnCPUExecutionCapChange(aExecutionCap);
12969}
12970
12971/**
12972 * @note Locks this object for reading.
12973 */
12974HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
12975{
12976 LogFlowThisFunc(("\n"));
12977
12978 AutoCaller autoCaller(this);
12979 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12980
12981 ComPtr<IInternalSessionControl> directControl;
12982 {
12983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12984 directControl = mData->mSession.mDirectControl;
12985 }
12986
12987 /* ignore notifications sent after #OnSessionEnd() is called */
12988 if (!directControl)
12989 return S_OK;
12990
12991 return directControl->OnVRDEServerChange(aRestart);
12992}
12993
12994/**
12995 * @note Locks this object for reading.
12996 */
12997HRESULT SessionMachine::onUSBControllerChange()
12998{
12999 LogFlowThisFunc(("\n"));
13000
13001 AutoCaller autoCaller(this);
13002 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13003
13004 ComPtr<IInternalSessionControl> directControl;
13005 {
13006 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13007 directControl = mData->mSession.mDirectControl;
13008 }
13009
13010 /* ignore notifications sent after #OnSessionEnd() is called */
13011 if (!directControl)
13012 return S_OK;
13013
13014 return directControl->OnUSBControllerChange();
13015}
13016
13017/**
13018 * @note Locks this object for reading.
13019 */
13020HRESULT SessionMachine::onSharedFolderChange()
13021{
13022 LogFlowThisFunc(("\n"));
13023
13024 AutoCaller autoCaller(this);
13025 AssertComRCReturnRC(autoCaller.rc());
13026
13027 ComPtr<IInternalSessionControl> directControl;
13028 {
13029 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13030 directControl = mData->mSession.mDirectControl;
13031 }
13032
13033 /* ignore notifications sent after #OnSessionEnd() is called */
13034 if (!directControl)
13035 return S_OK;
13036
13037 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13038}
13039
13040/**
13041 * @note Locks this object for reading.
13042 */
13043HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
13044{
13045 LogFlowThisFunc(("\n"));
13046
13047 AutoCaller autoCaller(this);
13048 AssertComRCReturnRC(autoCaller.rc());
13049
13050 ComPtr<IInternalSessionControl> directControl;
13051 {
13052 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13053 directControl = mData->mSession.mDirectControl;
13054 }
13055
13056 /* ignore notifications sent after #OnSessionEnd() is called */
13057 if (!directControl)
13058 return S_OK;
13059
13060 return directControl->OnClipboardModeChange(aClipboardMode);
13061}
13062
13063/**
13064 * @note Locks this object for reading.
13065 */
13066HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
13067{
13068 LogFlowThisFunc(("\n"));
13069
13070 AutoCaller autoCaller(this);
13071 AssertComRCReturnRC(autoCaller.rc());
13072
13073 ComPtr<IInternalSessionControl> directControl;
13074 {
13075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13076 directControl = mData->mSession.mDirectControl;
13077 }
13078
13079 /* ignore notifications sent after #OnSessionEnd() is called */
13080 if (!directControl)
13081 return S_OK;
13082
13083 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
13084}
13085
13086/**
13087 * @note Locks this object for reading.
13088 */
13089HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13090{
13091 LogFlowThisFunc(("\n"));
13092
13093 AutoCaller autoCaller(this);
13094 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
13095
13096 ComPtr<IInternalSessionControl> directControl;
13097 {
13098 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13099 directControl = mData->mSession.mDirectControl;
13100 }
13101
13102 /* ignore notifications sent after #OnSessionEnd() is called */
13103 if (!directControl)
13104 return S_OK;
13105
13106 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13107}
13108
13109/**
13110 * @note Locks this object for reading.
13111 */
13112HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove)
13113{
13114 LogFlowThisFunc(("\n"));
13115
13116 AutoCaller autoCaller(this);
13117 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13118
13119 ComPtr<IInternalSessionControl> directControl;
13120 {
13121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13122 directControl = mData->mSession.mDirectControl;
13123 }
13124
13125 /* ignore notifications sent after #OnSessionEnd() is called */
13126 if (!directControl)
13127 return S_OK;
13128
13129 return directControl->OnStorageDeviceChange(aAttachment, aRemove);
13130}
13131
13132/**
13133 * Returns @c true if this machine's USB controller reports it has a matching
13134 * filter for the given USB device and @c false otherwise.
13135 *
13136 * @note locks this object for reading.
13137 */
13138bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13139{
13140 AutoCaller autoCaller(this);
13141 /* silently return if not ready -- this method may be called after the
13142 * direct machine session has been called */
13143 if (!autoCaller.isOk())
13144 return false;
13145
13146#ifdef VBOX_WITH_USB
13147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13148
13149 switch (mData->mMachineState)
13150 {
13151 case MachineState_Starting:
13152 case MachineState_Restoring:
13153 case MachineState_TeleportingIn:
13154 case MachineState_Paused:
13155 case MachineState_Running:
13156 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13157 * elsewhere... */
13158 alock.release();
13159 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
13160 default: break;
13161 }
13162#else
13163 NOREF(aDevice);
13164 NOREF(aMaskedIfs);
13165#endif
13166 return false;
13167}
13168
13169/**
13170 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13171 */
13172HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
13173 IVirtualBoxErrorInfo *aError,
13174 ULONG aMaskedIfs)
13175{
13176 LogFlowThisFunc(("\n"));
13177
13178 AutoCaller autoCaller(this);
13179
13180 /* This notification may happen after the machine object has been
13181 * uninitialized (the session was closed), so don't assert. */
13182 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13183
13184 ComPtr<IInternalSessionControl> directControl;
13185 {
13186 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13187 directControl = mData->mSession.mDirectControl;
13188 }
13189
13190 /* fail on notifications sent after #OnSessionEnd() is called, it is
13191 * expected by the caller */
13192 if (!directControl)
13193 return E_FAIL;
13194
13195 /* No locks should be held at this point. */
13196 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13197 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13198
13199 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13200}
13201
13202/**
13203 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13204 */
13205HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
13206 IVirtualBoxErrorInfo *aError)
13207{
13208 LogFlowThisFunc(("\n"));
13209
13210 AutoCaller autoCaller(this);
13211
13212 /* This notification may happen after the machine object has been
13213 * uninitialized (the session was closed), so don't assert. */
13214 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13215
13216 ComPtr<IInternalSessionControl> directControl;
13217 {
13218 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13219 directControl = mData->mSession.mDirectControl;
13220 }
13221
13222 /* fail on notifications sent after #OnSessionEnd() is called, it is
13223 * expected by the caller */
13224 if (!directControl)
13225 return E_FAIL;
13226
13227 /* No locks should be held at this point. */
13228 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13229 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13230
13231 return directControl->OnUSBDeviceDetach(aId, aError);
13232}
13233
13234// protected methods
13235/////////////////////////////////////////////////////////////////////////////
13236
13237/**
13238 * Helper method to finalize saving the state.
13239 *
13240 * @note Must be called from under this object's lock.
13241 *
13242 * @param aRc S_OK if the snapshot has been taken successfully
13243 * @param aErrMsg human readable error message for failure
13244 *
13245 * @note Locks mParent + this objects for writing.
13246 */
13247HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13248{
13249 LogFlowThisFuncEnter();
13250
13251 AutoCaller autoCaller(this);
13252 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13253
13254 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13255
13256 HRESULT rc = S_OK;
13257
13258 if (SUCCEEDED(aRc))
13259 {
13260 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13261
13262 /* save all VM settings */
13263 rc = saveSettings(NULL);
13264 // no need to check whether VirtualBox.xml needs saving also since
13265 // we can't have a name change pending at this point
13266 }
13267 else
13268 {
13269 // delete the saved state file (it might have been already created);
13270 // we need not check whether this is shared with a snapshot here because
13271 // we certainly created this saved state file here anew
13272 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13273 }
13274
13275 /* notify the progress object about operation completion */
13276 Assert(mConsoleTaskData.mProgress);
13277 if (SUCCEEDED(aRc))
13278 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13279 else
13280 {
13281 if (aErrMsg.length())
13282 mConsoleTaskData.mProgress->notifyComplete(aRc,
13283 COM_IIDOF(ISession),
13284 getComponentName(),
13285 aErrMsg.c_str());
13286 else
13287 mConsoleTaskData.mProgress->notifyComplete(aRc);
13288 }
13289
13290 /* clear out the temporary saved state data */
13291 mConsoleTaskData.mLastState = MachineState_Null;
13292 mConsoleTaskData.strStateFilePath.setNull();
13293 mConsoleTaskData.mProgress.setNull();
13294
13295 LogFlowThisFuncLeave();
13296 return rc;
13297}
13298
13299/**
13300 * Deletes the given file if it is no longer in use by either the current machine state
13301 * (if the machine is "saved") or any of the machine's snapshots.
13302 *
13303 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13304 * but is different for each SnapshotMachine. When calling this, the order of calling this
13305 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13306 * is therefore critical. I know, it's all rather messy.
13307 *
13308 * @param strStateFile
13309 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
13310 */
13311void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
13312 Snapshot *pSnapshotToIgnore)
13313{
13314 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13315 if ( (strStateFile.isNotEmpty())
13316 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13317 )
13318 // ... and it must also not be shared with other snapshots
13319 if ( !mData->mFirstSnapshot
13320 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13321 // this checks the SnapshotMachine's state file paths
13322 )
13323 RTFileDelete(strStateFile.c_str());
13324}
13325
13326/**
13327 * Locks the attached media.
13328 *
13329 * All attached hard disks are locked for writing and DVD/floppy are locked for
13330 * reading. Parents of attached hard disks (if any) are locked for reading.
13331 *
13332 * This method also performs accessibility check of all media it locks: if some
13333 * media is inaccessible, the method will return a failure and a bunch of
13334 * extended error info objects per each inaccessible medium.
13335 *
13336 * Note that this method is atomic: if it returns a success, all media are
13337 * locked as described above; on failure no media is locked at all (all
13338 * succeeded individual locks will be undone).
13339 *
13340 * This method is intended to be called when the machine is in Starting or
13341 * Restoring state and asserts otherwise.
13342 *
13343 * The locks made by this method must be undone by calling #unlockMedia() when
13344 * no more needed.
13345 */
13346HRESULT SessionMachine::lockMedia()
13347{
13348 AutoCaller autoCaller(this);
13349 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13350
13351 AutoMultiWriteLock2 alock(this->lockHandle(),
13352 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13353
13354 AssertReturn( mData->mMachineState == MachineState_Starting
13355 || mData->mMachineState == MachineState_Restoring
13356 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13357 /* bail out if trying to lock things with already set up locking */
13358 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13359
13360 clearError();
13361 MultiResult mrc(S_OK);
13362
13363 /* Collect locking information for all medium objects attached to the VM. */
13364 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13365 it != mMediaData->mAttachments.end();
13366 ++it)
13367 {
13368 MediumAttachment* pAtt = *it;
13369 DeviceType_T devType = pAtt->getType();
13370 Medium *pMedium = pAtt->getMedium();
13371
13372 MediumLockList *pMediumLockList(new MediumLockList());
13373 // There can be attachments without a medium (floppy/dvd), and thus
13374 // it's impossible to create a medium lock list. It still makes sense
13375 // to have the empty medium lock list in the map in case a medium is
13376 // attached later.
13377 if (pMedium != NULL)
13378 {
13379 MediumType_T mediumType = pMedium->getType();
13380 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13381 || mediumType == MediumType_Shareable;
13382 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13383
13384 alock.release();
13385 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13386 !fIsReadOnlyLock /* fMediumLockWrite */,
13387 NULL,
13388 *pMediumLockList);
13389 alock.acquire();
13390 if (FAILED(mrc))
13391 {
13392 delete pMediumLockList;
13393 mData->mSession.mLockedMedia.Clear();
13394 break;
13395 }
13396 }
13397
13398 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13399 if (FAILED(rc))
13400 {
13401 mData->mSession.mLockedMedia.Clear();
13402 mrc = setError(rc,
13403 tr("Collecting locking information for all attached media failed"));
13404 break;
13405 }
13406 }
13407
13408 if (SUCCEEDED(mrc))
13409 {
13410 /* Now lock all media. If this fails, nothing is locked. */
13411 alock.release();
13412 HRESULT rc = mData->mSession.mLockedMedia.Lock();
13413 alock.acquire();
13414 if (FAILED(rc))
13415 {
13416 mrc = setError(rc,
13417 tr("Locking of attached media failed"));
13418 }
13419 }
13420
13421 return mrc;
13422}
13423
13424/**
13425 * Undoes the locks made by by #lockMedia().
13426 */
13427void SessionMachine::unlockMedia()
13428{
13429 AutoCaller autoCaller(this);
13430 AssertComRCReturnVoid(autoCaller.rc());
13431
13432 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13433
13434 /* we may be holding important error info on the current thread;
13435 * preserve it */
13436 ErrorInfoKeeper eik;
13437
13438 HRESULT rc = mData->mSession.mLockedMedia.Clear();
13439 AssertComRC(rc);
13440}
13441
13442/**
13443 * Helper to change the machine state (reimplementation).
13444 *
13445 * @note Locks this object for writing.
13446 * @note This method must not call saveSettings or SaveSettings, otherwise
13447 * it can cause crashes in random places due to unexpectedly committing
13448 * the current settings. The caller is responsible for that. The call
13449 * to saveStateSettings is fine, because this method does not commit.
13450 */
13451HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
13452{
13453 LogFlowThisFuncEnter();
13454 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
13455
13456 AutoCaller autoCaller(this);
13457 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13458
13459 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13460
13461 MachineState_T oldMachineState = mData->mMachineState;
13462
13463 AssertMsgReturn(oldMachineState != aMachineState,
13464 ("oldMachineState=%s, aMachineState=%s\n",
13465 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
13466 E_FAIL);
13467
13468 HRESULT rc = S_OK;
13469
13470 int stsFlags = 0;
13471 bool deleteSavedState = false;
13472
13473 /* detect some state transitions */
13474
13475 if ( ( oldMachineState == MachineState_Saved
13476 && aMachineState == MachineState_Restoring)
13477 || ( ( oldMachineState == MachineState_PoweredOff
13478 || oldMachineState == MachineState_Teleported
13479 || oldMachineState == MachineState_Aborted
13480 )
13481 && ( aMachineState == MachineState_TeleportingIn
13482 || aMachineState == MachineState_Starting
13483 )
13484 )
13485 )
13486 {
13487 /* The EMT thread is about to start */
13488
13489 /* Nothing to do here for now... */
13490
13491 /// @todo NEWMEDIA don't let mDVDDrive and other children
13492 /// change anything when in the Starting/Restoring state
13493 }
13494 else if ( ( oldMachineState == MachineState_Running
13495 || oldMachineState == MachineState_Paused
13496 || oldMachineState == MachineState_Teleporting
13497 || oldMachineState == MachineState_LiveSnapshotting
13498 || oldMachineState == MachineState_Stuck
13499 || oldMachineState == MachineState_Starting
13500 || oldMachineState == MachineState_Stopping
13501 || oldMachineState == MachineState_Saving
13502 || oldMachineState == MachineState_Restoring
13503 || oldMachineState == MachineState_TeleportingPausedVM
13504 || oldMachineState == MachineState_TeleportingIn
13505 )
13506 && ( aMachineState == MachineState_PoweredOff
13507 || aMachineState == MachineState_Saved
13508 || aMachineState == MachineState_Teleported
13509 || aMachineState == MachineState_Aborted
13510 )
13511 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
13512 * snapshot */
13513 && ( mConsoleTaskData.mSnapshot.isNull()
13514 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
13515 )
13516 )
13517 {
13518 /* The EMT thread has just stopped, unlock attached media. Note that as
13519 * opposed to locking that is done from Console, we do unlocking here
13520 * because the VM process may have aborted before having a chance to
13521 * properly unlock all media it locked. */
13522
13523 unlockMedia();
13524 }
13525
13526 if (oldMachineState == MachineState_Restoring)
13527 {
13528 if (aMachineState != MachineState_Saved)
13529 {
13530 /*
13531 * delete the saved state file once the machine has finished
13532 * restoring from it (note that Console sets the state from
13533 * Restoring to Saved if the VM couldn't restore successfully,
13534 * to give the user an ability to fix an error and retry --
13535 * we keep the saved state file in this case)
13536 */
13537 deleteSavedState = true;
13538 }
13539 }
13540 else if ( oldMachineState == MachineState_Saved
13541 && ( aMachineState == MachineState_PoweredOff
13542 || aMachineState == MachineState_Aborted
13543 || aMachineState == MachineState_Teleported
13544 )
13545 )
13546 {
13547 /*
13548 * delete the saved state after Console::ForgetSavedState() is called
13549 * or if the VM process (owning a direct VM session) crashed while the
13550 * VM was Saved
13551 */
13552
13553 /// @todo (dmik)
13554 // Not sure that deleting the saved state file just because of the
13555 // client death before it attempted to restore the VM is a good
13556 // thing. But when it crashes we need to go to the Aborted state
13557 // which cannot have the saved state file associated... The only
13558 // way to fix this is to make the Aborted condition not a VM state
13559 // but a bool flag: i.e., when a crash occurs, set it to true and
13560 // change the state to PoweredOff or Saved depending on the
13561 // saved state presence.
13562
13563 deleteSavedState = true;
13564 mData->mCurrentStateModified = TRUE;
13565 stsFlags |= SaveSTS_CurStateModified;
13566 }
13567
13568 if ( aMachineState == MachineState_Starting
13569 || aMachineState == MachineState_Restoring
13570 || aMachineState == MachineState_TeleportingIn
13571 )
13572 {
13573 /* set the current state modified flag to indicate that the current
13574 * state is no more identical to the state in the
13575 * current snapshot */
13576 if (!mData->mCurrentSnapshot.isNull())
13577 {
13578 mData->mCurrentStateModified = TRUE;
13579 stsFlags |= SaveSTS_CurStateModified;
13580 }
13581 }
13582
13583 if (deleteSavedState)
13584 {
13585 if (mRemoveSavedState)
13586 {
13587 Assert(!mSSData->strStateFilePath.isEmpty());
13588
13589 // it is safe to delete the saved state file if ...
13590 if ( !mData->mFirstSnapshot // ... we have no snapshots or
13591 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
13592 // ... none of the snapshots share the saved state file
13593 )
13594 RTFileDelete(mSSData->strStateFilePath.c_str());
13595 }
13596
13597 mSSData->strStateFilePath.setNull();
13598 stsFlags |= SaveSTS_StateFilePath;
13599 }
13600
13601 /* redirect to the underlying peer machine */
13602 mPeer->setMachineState(aMachineState);
13603
13604 if ( aMachineState == MachineState_PoweredOff
13605 || aMachineState == MachineState_Teleported
13606 || aMachineState == MachineState_Aborted
13607 || aMachineState == MachineState_Saved)
13608 {
13609 /* the machine has stopped execution
13610 * (or the saved state file was adopted) */
13611 stsFlags |= SaveSTS_StateTimeStamp;
13612 }
13613
13614 if ( ( oldMachineState == MachineState_PoweredOff
13615 || oldMachineState == MachineState_Aborted
13616 || oldMachineState == MachineState_Teleported
13617 )
13618 && aMachineState == MachineState_Saved)
13619 {
13620 /* the saved state file was adopted */
13621 Assert(!mSSData->strStateFilePath.isEmpty());
13622 stsFlags |= SaveSTS_StateFilePath;
13623 }
13624
13625#ifdef VBOX_WITH_GUEST_PROPS
13626 if ( aMachineState == MachineState_PoweredOff
13627 || aMachineState == MachineState_Aborted
13628 || aMachineState == MachineState_Teleported)
13629 {
13630 /* Make sure any transient guest properties get removed from the
13631 * property store on shutdown. */
13632
13633 HWData::GuestPropertyList::iterator it;
13634 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
13635 if (!fNeedsSaving)
13636 for (it = mHWData->mGuestProperties.begin();
13637 it != mHWData->mGuestProperties.end(); ++it)
13638 if ( (it->mFlags & guestProp::TRANSIENT)
13639 || (it->mFlags & guestProp::TRANSRESET))
13640 {
13641 fNeedsSaving = true;
13642 break;
13643 }
13644 if (fNeedsSaving)
13645 {
13646 mData->mCurrentStateModified = TRUE;
13647 stsFlags |= SaveSTS_CurStateModified;
13648 }
13649 }
13650#endif
13651
13652 rc = saveStateSettings(stsFlags);
13653
13654 if ( ( oldMachineState != MachineState_PoweredOff
13655 && oldMachineState != MachineState_Aborted
13656 && oldMachineState != MachineState_Teleported
13657 )
13658 && ( aMachineState == MachineState_PoweredOff
13659 || aMachineState == MachineState_Aborted
13660 || aMachineState == MachineState_Teleported
13661 )
13662 )
13663 {
13664 /* we've been shut down for any reason */
13665 /* no special action so far */
13666 }
13667
13668 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
13669 LogFlowThisFuncLeave();
13670 return rc;
13671}
13672
13673/**
13674 * Sends the current machine state value to the VM process.
13675 *
13676 * @note Locks this object for reading, then calls a client process.
13677 */
13678HRESULT SessionMachine::updateMachineStateOnClient()
13679{
13680 AutoCaller autoCaller(this);
13681 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13682
13683 ComPtr<IInternalSessionControl> directControl;
13684 {
13685 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13686 AssertReturn(!!mData, E_FAIL);
13687 directControl = mData->mSession.mDirectControl;
13688
13689 /* directControl may be already set to NULL here in #OnSessionEnd()
13690 * called too early by the direct session process while there is still
13691 * some operation (like deleting the snapshot) in progress. The client
13692 * process in this case is waiting inside Session::close() for the
13693 * "end session" process object to complete, while #uninit() called by
13694 * #checkForDeath() on the Watcher thread is waiting for the pending
13695 * operation to complete. For now, we accept this inconsistent behavior
13696 * and simply do nothing here. */
13697
13698 if (mData->mSession.mState == SessionState_Unlocking)
13699 return S_OK;
13700
13701 AssertReturn(!directControl.isNull(), E_FAIL);
13702 }
13703
13704 return directControl->UpdateMachineState(mData->mMachineState);
13705}
Note: See TracBrowser for help on using the repository browser.

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