VirtualBox

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

Last change on this file since 44528 was 44528, checked in by vboxsync, 12 years ago

header (C) fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 470.6 KB
Line 
1/* $Id: MachineImpl.cpp 44528 2013-02-04 14:27:54Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2013 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 mVideoCaptureFile = "Test.webm";
169 mVideoCaptureWidth = 640;
170 mVideoCaptureHeight = 480;
171 mVideoCaptureEnabled = false;
172
173 mHWVirtExEnabled = true;
174 mHWVirtExNestedPagingEnabled = true;
175#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
176 mHWVirtExLargePagesEnabled = true;
177#else
178 /* Not supported on 32 bits hosts. */
179 mHWVirtExLargePagesEnabled = false;
180#endif
181 mHWVirtExVPIDEnabled = true;
182 mHWVirtExForceEnabled = false;
183#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
184 mHWVirtExExclusive = false;
185#else
186 mHWVirtExExclusive = true;
187#endif
188#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
189 mPAEEnabled = true;
190#else
191 mPAEEnabled = false;
192#endif
193 mSyntheticCpu = false;
194 mHPETEnabled = false;
195
196 /* default boot order: floppy - DVD - HDD */
197 mBootOrder[0] = DeviceType_Floppy;
198 mBootOrder[1] = DeviceType_DVD;
199 mBootOrder[2] = DeviceType_HardDisk;
200 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
201 mBootOrder[i] = DeviceType_Null;
202
203 mClipboardMode = ClipboardMode_Disabled;
204 mDragAndDropMode = DragAndDropMode_Disabled;
205 mGuestPropertyNotificationPatterns = "";
206
207 mFirmwareType = FirmwareType_BIOS;
208 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
209 mPointingHIDType = PointingHIDType_PS2Mouse;
210 mChipsetType = ChipsetType_PIIX3;
211 mEmulatedUSBWebcamEnabled = FALSE;
212 mEmulatedUSBCardReaderEnabled = FALSE;
213
214 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
215 mCPUAttached[i] = false;
216
217 mIOCacheEnabled = true;
218 mIOCacheSize = 5; /* 5MB */
219
220 /* Maximum CPU execution cap by default. */
221 mCpuExecutionCap = 100;
222}
223
224Machine::HWData::~HWData()
225{
226}
227
228/////////////////////////////////////////////////////////////////////////////
229// Machine::HDData structure
230/////////////////////////////////////////////////////////////////////////////
231
232Machine::MediaData::MediaData()
233{
234}
235
236Machine::MediaData::~MediaData()
237{
238}
239
240/////////////////////////////////////////////////////////////////////////////
241// Machine class
242/////////////////////////////////////////////////////////////////////////////
243
244// constructor / destructor
245/////////////////////////////////////////////////////////////////////////////
246
247Machine::Machine()
248 : mCollectorGuest(NULL),
249 mPeer(NULL),
250 mParent(NULL),
251 mSerialPorts(),
252 mParallelPorts(),
253 uRegistryNeedsSaving(0)
254{}
255
256Machine::~Machine()
257{}
258
259HRESULT Machine::FinalConstruct()
260{
261 LogFlowThisFunc(("\n"));
262 return BaseFinalConstruct();
263}
264
265void Machine::FinalRelease()
266{
267 LogFlowThisFunc(("\n"));
268 uninit();
269 BaseFinalRelease();
270}
271
272/**
273 * Initializes a new machine instance; this init() variant creates a new, empty machine.
274 * This gets called from VirtualBox::CreateMachine().
275 *
276 * @param aParent Associated parent object
277 * @param strConfigFile Local file system path to the VM settings file (can
278 * be relative to the VirtualBox config directory).
279 * @param strName name for the machine
280 * @param llGroups list of groups for the machine
281 * @param aOsType OS Type of this machine or NULL.
282 * @param aId UUID for the new machine.
283 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
284 *
285 * @return Success indicator. if not S_OK, the machine object is invalid
286 */
287HRESULT Machine::init(VirtualBox *aParent,
288 const Utf8Str &strConfigFile,
289 const Utf8Str &strName,
290 const StringsList &llGroups,
291 GuestOSType *aOsType,
292 const Guid &aId,
293 bool fForceOverwrite,
294 bool fDirectoryIncludesUUID)
295{
296 LogFlowThisFuncEnter();
297 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
298
299 /* Enclose the state transition NotReady->InInit->Ready */
300 AutoInitSpan autoInitSpan(this);
301 AssertReturn(autoInitSpan.isOk(), E_FAIL);
302
303 HRESULT rc = initImpl(aParent, strConfigFile);
304 if (FAILED(rc)) return rc;
305
306 rc = tryCreateMachineConfigFile(fForceOverwrite);
307 if (FAILED(rc)) return rc;
308
309 if (SUCCEEDED(rc))
310 {
311 // create an empty machine config
312 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
313
314 rc = initDataAndChildObjects();
315 }
316
317 if (SUCCEEDED(rc))
318 {
319 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
320 mData->mAccessible = TRUE;
321
322 unconst(mData->mUuid) = aId;
323
324 mUserData->s.strName = strName;
325
326 mUserData->s.llGroups = llGroups;
327
328 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
329 // the "name sync" flag determines whether the machine directory gets renamed along
330 // with the machine file; say so if the settings file name is the same as the
331 // settings file parent directory (machine directory)
332 mUserData->s.fNameSync = isInOwnDir();
333
334 // initialize the default snapshots folder
335 rc = COMSETTER(SnapshotFolder)(NULL);
336 AssertComRC(rc);
337
338 if (aOsType)
339 {
340 /* Store OS type */
341 mUserData->s.strOsType = aOsType->id();
342
343 /* Apply BIOS defaults */
344 mBIOSSettings->applyDefaults(aOsType);
345
346 /* Apply network adapters defaults */
347 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
348 mNetworkAdapters[slot]->applyDefaults(aOsType);
349
350 /* Apply serial port defaults */
351 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
352 mSerialPorts[slot]->applyDefaults(aOsType);
353 }
354
355 /* At this point the changing of the current state modification
356 * flag is allowed. */
357 allowStateModification();
358
359 /* commit all changes made during the initialization */
360 commit();
361 }
362
363 /* Confirm a successful initialization when it's the case */
364 if (SUCCEEDED(rc))
365 {
366 if (mData->mAccessible)
367 autoInitSpan.setSucceeded();
368 else
369 autoInitSpan.setLimited();
370 }
371
372 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
373 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
374 mData->mRegistered,
375 mData->mAccessible,
376 rc));
377
378 LogFlowThisFuncLeave();
379
380 return rc;
381}
382
383/**
384 * Initializes a new instance with data from machine XML (formerly Init_Registered).
385 * Gets called in two modes:
386 *
387 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
388 * UUID is specified and we mark the machine as "registered";
389 *
390 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
391 * and the machine remains unregistered until RegisterMachine() is called.
392 *
393 * @param aParent Associated parent object
394 * @param aConfigFile Local file system path to the VM settings file (can
395 * be relative to the VirtualBox config directory).
396 * @param aId UUID of the machine or NULL (see above).
397 *
398 * @return Success indicator. if not S_OK, the machine object is invalid
399 */
400HRESULT Machine::initFromSettings(VirtualBox *aParent,
401 const Utf8Str &strConfigFile,
402 const Guid *aId)
403{
404 LogFlowThisFuncEnter();
405 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
406
407 /* Enclose the state transition NotReady->InInit->Ready */
408 AutoInitSpan autoInitSpan(this);
409 AssertReturn(autoInitSpan.isOk(), E_FAIL);
410
411 HRESULT rc = initImpl(aParent, strConfigFile);
412 if (FAILED(rc)) return rc;
413
414 if (aId)
415 {
416 // loading a registered VM:
417 unconst(mData->mUuid) = *aId;
418 mData->mRegistered = TRUE;
419 // now load the settings from XML:
420 rc = registeredInit();
421 // this calls initDataAndChildObjects() and loadSettings()
422 }
423 else
424 {
425 // opening an unregistered VM (VirtualBox::OpenMachine()):
426 rc = initDataAndChildObjects();
427
428 if (SUCCEEDED(rc))
429 {
430 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
431 mData->mAccessible = TRUE;
432
433 try
434 {
435 // load and parse machine XML; this will throw on XML or logic errors
436 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
437
438 // reject VM UUID duplicates, they can happen if someone
439 // tries to register an already known VM config again
440 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
441 true /* fPermitInaccessible */,
442 false /* aDoSetError */,
443 NULL) != VBOX_E_OBJECT_NOT_FOUND)
444 {
445 throw setError(E_FAIL,
446 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
447 mData->m_strConfigFile.c_str());
448 }
449
450 // use UUID from machine config
451 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
452
453 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
454 NULL /* puuidRegistry */);
455 if (FAILED(rc)) throw rc;
456
457 /* At this point the changing of the current state modification
458 * flag is allowed. */
459 allowStateModification();
460
461 commit();
462 }
463 catch (HRESULT err)
464 {
465 /* we assume that error info is set by the thrower */
466 rc = err;
467 }
468 catch (...)
469 {
470 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
471 }
472 }
473 }
474
475 /* Confirm a successful initialization when it's the case */
476 if (SUCCEEDED(rc))
477 {
478 if (mData->mAccessible)
479 autoInitSpan.setSucceeded();
480 else
481 {
482 autoInitSpan.setLimited();
483
484 // uninit media from this machine's media registry, or else
485 // reloading the settings will fail
486 mParent->unregisterMachineMedia(getId());
487 }
488 }
489
490 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
491 "rc=%08X\n",
492 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
493 mData->mRegistered, mData->mAccessible, rc));
494
495 LogFlowThisFuncLeave();
496
497 return rc;
498}
499
500/**
501 * Initializes a new instance from a machine config that is already in memory
502 * (import OVF case). Since we are importing, the UUID in the machine
503 * config is ignored and we always generate a fresh one.
504 *
505 * @param strName Name for the new machine; this overrides what is specified in config and is used
506 * for the settings file as well.
507 * @param config Machine configuration loaded and parsed from XML.
508 *
509 * @return Success indicator. if not S_OK, the machine object is invalid
510 */
511HRESULT Machine::init(VirtualBox *aParent,
512 const Utf8Str &strName,
513 const settings::MachineConfigFile &config)
514{
515 LogFlowThisFuncEnter();
516
517 /* Enclose the state transition NotReady->InInit->Ready */
518 AutoInitSpan autoInitSpan(this);
519 AssertReturn(autoInitSpan.isOk(), E_FAIL);
520
521 Utf8Str strConfigFile;
522 aParent->getDefaultMachineFolder(strConfigFile);
523 strConfigFile.append(RTPATH_DELIMITER);
524 strConfigFile.append(strName);
525 strConfigFile.append(RTPATH_DELIMITER);
526 strConfigFile.append(strName);
527 strConfigFile.append(".vbox");
528
529 HRESULT rc = initImpl(aParent, strConfigFile);
530 if (FAILED(rc)) return rc;
531
532 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
533 if (FAILED(rc)) return rc;
534
535 rc = initDataAndChildObjects();
536
537 if (SUCCEEDED(rc))
538 {
539 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
540 mData->mAccessible = TRUE;
541
542 // create empty machine config for instance data
543 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
544
545 // generate fresh UUID, ignore machine config
546 unconst(mData->mUuid).create();
547
548 rc = loadMachineDataFromSettings(config,
549 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
550
551 // override VM name as well, it may be different
552 mUserData->s.strName = strName;
553
554 if (SUCCEEDED(rc))
555 {
556 /* At this point the changing of the current state modification
557 * flag is allowed. */
558 allowStateModification();
559
560 /* commit all changes made during the initialization */
561 commit();
562 }
563 }
564
565 /* Confirm a successful initialization when it's the case */
566 if (SUCCEEDED(rc))
567 {
568 if (mData->mAccessible)
569 autoInitSpan.setSucceeded();
570 else
571 {
572 autoInitSpan.setLimited();
573
574 // uninit media from this machine's media registry, or else
575 // reloading the settings will fail
576 mParent->unregisterMachineMedia(getId());
577 }
578 }
579
580 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
581 "rc=%08X\n",
582 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
583 mData->mRegistered, mData->mAccessible, rc));
584
585 LogFlowThisFuncLeave();
586
587 return rc;
588}
589
590/**
591 * Shared code between the various init() implementations.
592 * @param aParent
593 * @return
594 */
595HRESULT Machine::initImpl(VirtualBox *aParent,
596 const Utf8Str &strConfigFile)
597{
598 LogFlowThisFuncEnter();
599
600 AssertReturn(aParent, E_INVALIDARG);
601 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
602
603 HRESULT rc = S_OK;
604
605 /* share the parent weakly */
606 unconst(mParent) = aParent;
607
608 /* allocate the essential machine data structure (the rest will be
609 * allocated later by initDataAndChildObjects() */
610 mData.allocate();
611
612 /* memorize the config file name (as provided) */
613 mData->m_strConfigFile = strConfigFile;
614
615 /* get the full file name */
616 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
617 if (RT_FAILURE(vrc1))
618 return setError(VBOX_E_FILE_ERROR,
619 tr("Invalid machine settings file name '%s' (%Rrc)"),
620 strConfigFile.c_str(),
621 vrc1);
622
623 LogFlowThisFuncLeave();
624
625 return rc;
626}
627
628/**
629 * Tries to create a machine settings file in the path stored in the machine
630 * instance data. Used when a new machine is created to fail gracefully if
631 * the settings file could not be written (e.g. because machine dir is read-only).
632 * @return
633 */
634HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
635{
636 HRESULT rc = S_OK;
637
638 // when we create a new machine, we must be able to create the settings file
639 RTFILE f = NIL_RTFILE;
640 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
641 if ( RT_SUCCESS(vrc)
642 || vrc == VERR_SHARING_VIOLATION
643 )
644 {
645 if (RT_SUCCESS(vrc))
646 RTFileClose(f);
647 if (!fForceOverwrite)
648 rc = setError(VBOX_E_FILE_ERROR,
649 tr("Machine settings file '%s' already exists"),
650 mData->m_strConfigFileFull.c_str());
651 else
652 {
653 /* try to delete the config file, as otherwise the creation
654 * of a new settings file will fail. */
655 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
656 if (RT_FAILURE(vrc2))
657 rc = setError(VBOX_E_FILE_ERROR,
658 tr("Could not delete the existing settings file '%s' (%Rrc)"),
659 mData->m_strConfigFileFull.c_str(), vrc2);
660 }
661 }
662 else if ( vrc != VERR_FILE_NOT_FOUND
663 && vrc != VERR_PATH_NOT_FOUND
664 )
665 rc = setError(VBOX_E_FILE_ERROR,
666 tr("Invalid machine settings file name '%s' (%Rrc)"),
667 mData->m_strConfigFileFull.c_str(),
668 vrc);
669 return rc;
670}
671
672/**
673 * Initializes the registered machine by loading the settings file.
674 * This method is separated from #init() in order to make it possible to
675 * retry the operation after VirtualBox startup instead of refusing to
676 * startup the whole VirtualBox server in case if the settings file of some
677 * registered VM is invalid or inaccessible.
678 *
679 * @note Must be always called from this object's write lock
680 * (unless called from #init() that doesn't need any locking).
681 * @note Locks the mUSBController method for writing.
682 * @note Subclasses must not call this method.
683 */
684HRESULT Machine::registeredInit()
685{
686 AssertReturn(!isSessionMachine(), E_FAIL);
687 AssertReturn(!isSnapshotMachine(), E_FAIL);
688 AssertReturn(mData->mUuid.isValid(), E_FAIL);
689 AssertReturn(!mData->mAccessible, E_FAIL);
690
691 HRESULT rc = initDataAndChildObjects();
692
693 if (SUCCEEDED(rc))
694 {
695 /* Temporarily reset the registered flag in order to let setters
696 * potentially called from loadSettings() succeed (isMutable() used in
697 * all setters will return FALSE for a Machine instance if mRegistered
698 * is TRUE). */
699 mData->mRegistered = FALSE;
700
701 try
702 {
703 // load and parse machine XML; this will throw on XML or logic errors
704 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
705
706 if (mData->mUuid != mData->pMachineConfigFile->uuid)
707 throw setError(E_FAIL,
708 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
709 mData->pMachineConfigFile->uuid.raw(),
710 mData->m_strConfigFileFull.c_str(),
711 mData->mUuid.toString().c_str(),
712 mParent->settingsFilePath().c_str());
713
714 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
715 NULL /* const Guid *puuidRegistry */);
716 if (FAILED(rc)) throw rc;
717 }
718 catch (HRESULT err)
719 {
720 /* we assume that error info is set by the thrower */
721 rc = err;
722 }
723 catch (...)
724 {
725 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
726 }
727
728 /* Restore the registered flag (even on failure) */
729 mData->mRegistered = TRUE;
730 }
731
732 if (SUCCEEDED(rc))
733 {
734 /* Set mAccessible to TRUE only if we successfully locked and loaded
735 * the settings file */
736 mData->mAccessible = TRUE;
737
738 /* commit all changes made during loading the settings file */
739 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
740 /// @todo r=klaus for some reason the settings loading logic backs up
741 // the settings, and therefore a commit is needed. Should probably be changed.
742 }
743 else
744 {
745 /* If the machine is registered, then, instead of returning a
746 * failure, we mark it as inaccessible and set the result to
747 * success to give it a try later */
748
749 /* fetch the current error info */
750 mData->mAccessError = com::ErrorInfo();
751 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
752 mData->mUuid.raw(),
753 mData->mAccessError.getText().raw()));
754
755 /* rollback all changes */
756 rollback(false /* aNotify */);
757
758 // uninit media from this machine's media registry, or else
759 // reloading the settings will fail
760 mParent->unregisterMachineMedia(getId());
761
762 /* uninitialize the common part to make sure all data is reset to
763 * default (null) values */
764 uninitDataAndChildObjects();
765
766 rc = S_OK;
767 }
768
769 return rc;
770}
771
772/**
773 * Uninitializes the instance.
774 * Called either from FinalRelease() or by the parent when it gets destroyed.
775 *
776 * @note The caller of this method must make sure that this object
777 * a) doesn't have active callers on the current thread and b) is not locked
778 * by the current thread; otherwise uninit() will hang either a) due to
779 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
780 * a dead-lock caused by this thread waiting for all callers on the other
781 * threads are done but preventing them from doing so by holding a lock.
782 */
783void Machine::uninit()
784{
785 LogFlowThisFuncEnter();
786
787 Assert(!isWriteLockOnCurrentThread());
788
789 Assert(!uRegistryNeedsSaving);
790 if (uRegistryNeedsSaving)
791 {
792 AutoCaller autoCaller(this);
793 if (SUCCEEDED(autoCaller.rc()))
794 {
795 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
796 saveSettings(NULL, Machine::SaveS_Force);
797 }
798 }
799
800 /* Enclose the state transition Ready->InUninit->NotReady */
801 AutoUninitSpan autoUninitSpan(this);
802 if (autoUninitSpan.uninitDone())
803 return;
804
805 Assert(!isSnapshotMachine());
806 Assert(!isSessionMachine());
807 Assert(!!mData);
808
809 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
810 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
811
812 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
813
814 if (!mData->mSession.mMachine.isNull())
815 {
816 /* Theoretically, this can only happen if the VirtualBox server has been
817 * terminated while there were clients running that owned open direct
818 * sessions. Since in this case we are definitely called by
819 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
820 * won't happen on the client watcher thread (because it does
821 * VirtualBox::addCaller() for the duration of the
822 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
823 * cannot happen until the VirtualBox caller is released). This is
824 * important, because SessionMachine::uninit() cannot correctly operate
825 * after we return from this method (it expects the Machine instance is
826 * still valid). We'll call it ourselves below.
827 */
828 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
829 (SessionMachine*)mData->mSession.mMachine));
830
831 if (Global::IsOnlineOrTransient(mData->mMachineState))
832 {
833 LogWarningThisFunc(("Setting state to Aborted!\n"));
834 /* set machine state using SessionMachine reimplementation */
835 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
836 }
837
838 /*
839 * Uninitialize SessionMachine using public uninit() to indicate
840 * an unexpected uninitialization.
841 */
842 mData->mSession.mMachine->uninit();
843 /* SessionMachine::uninit() must set mSession.mMachine to null */
844 Assert(mData->mSession.mMachine.isNull());
845 }
846
847 // uninit media from this machine's media registry, if they're still there
848 Guid uuidMachine(getId());
849
850 /* the lock is no more necessary (SessionMachine is uninitialized) */
851 alock.release();
852
853 /* XXX This will fail with
854 * "cannot be closed because it is still attached to 1 virtual machines"
855 * because at this point we did not call uninitDataAndChildObjects() yet
856 * and therefore also removeBackReference() for all these mediums was not called! */
857
858 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
859 mParent->unregisterMachineMedia(uuidMachine);
860
861 // has machine been modified?
862 if (mData->flModifications)
863 {
864 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
865 rollback(false /* aNotify */);
866 }
867
868 if (mData->mAccessible)
869 uninitDataAndChildObjects();
870
871 /* free the essential data structure last */
872 mData.free();
873
874 LogFlowThisFuncLeave();
875}
876
877// IMachine properties
878/////////////////////////////////////////////////////////////////////////////
879
880STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
881{
882 CheckComArgOutPointerValid(aParent);
883
884 AutoLimitedCaller autoCaller(this);
885 if (FAILED(autoCaller.rc())) return autoCaller.rc();
886
887 /* mParent is constant during life time, no need to lock */
888 ComObjPtr<VirtualBox> pVirtualBox(mParent);
889 pVirtualBox.queryInterfaceTo(aParent);
890
891 return S_OK;
892}
893
894STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
895{
896 CheckComArgOutPointerValid(aAccessible);
897
898 AutoLimitedCaller autoCaller(this);
899 if (FAILED(autoCaller.rc())) return autoCaller.rc();
900
901 LogFlowThisFunc(("ENTER\n"));
902
903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
904
905 HRESULT rc = S_OK;
906
907 if (!mData->mAccessible)
908 {
909 /* try to initialize the VM once more if not accessible */
910
911 AutoReinitSpan autoReinitSpan(this);
912 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
913
914#ifdef DEBUG
915 LogFlowThisFunc(("Dumping media backreferences\n"));
916 mParent->dumpAllBackRefs();
917#endif
918
919 if (mData->pMachineConfigFile)
920 {
921 // reset the XML file to force loadSettings() (called from registeredInit())
922 // to parse it again; the file might have changed
923 delete mData->pMachineConfigFile;
924 mData->pMachineConfigFile = NULL;
925 }
926
927 rc = registeredInit();
928
929 if (SUCCEEDED(rc) && mData->mAccessible)
930 {
931 autoReinitSpan.setSucceeded();
932
933 /* make sure interesting parties will notice the accessibility
934 * state change */
935 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
936 mParent->onMachineDataChange(mData->mUuid);
937 }
938 }
939
940 if (SUCCEEDED(rc))
941 *aAccessible = mData->mAccessible;
942
943 LogFlowThisFuncLeave();
944
945 return rc;
946}
947
948STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
949{
950 CheckComArgOutPointerValid(aAccessError);
951
952 AutoLimitedCaller autoCaller(this);
953 if (FAILED(autoCaller.rc())) return autoCaller.rc();
954
955 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
956
957 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
958 {
959 /* return shortly */
960 aAccessError = NULL;
961 return S_OK;
962 }
963
964 HRESULT rc = S_OK;
965
966 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
967 rc = errorInfo.createObject();
968 if (SUCCEEDED(rc))
969 {
970 errorInfo->init(mData->mAccessError.getResultCode(),
971 mData->mAccessError.getInterfaceID().ref(),
972 Utf8Str(mData->mAccessError.getComponent()).c_str(),
973 Utf8Str(mData->mAccessError.getText()));
974 rc = errorInfo.queryInterfaceTo(aAccessError);
975 }
976
977 return rc;
978}
979
980STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
981{
982 CheckComArgOutPointerValid(aName);
983
984 AutoCaller autoCaller(this);
985 if (FAILED(autoCaller.rc())) return autoCaller.rc();
986
987 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
988
989 mUserData->s.strName.cloneTo(aName);
990
991 return S_OK;
992}
993
994STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
995{
996 CheckComArgStrNotEmptyOrNull(aName);
997
998 AutoCaller autoCaller(this);
999 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1000
1001 // prohibit setting a UUID only as the machine name, or else it can
1002 // never be found by findMachine()
1003 Guid test(aName);
1004
1005 if (test.isValid())
1006 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1007
1008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1009
1010 HRESULT rc = checkStateDependency(MutableStateDep);
1011 if (FAILED(rc)) return rc;
1012
1013 setModified(IsModified_MachineData);
1014 mUserData.backup();
1015 mUserData->s.strName = aName;
1016
1017 return S_OK;
1018}
1019
1020STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1021{
1022 CheckComArgOutPointerValid(aDescription);
1023
1024 AutoCaller autoCaller(this);
1025 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1026
1027 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1028
1029 mUserData->s.strDescription.cloneTo(aDescription);
1030
1031 return S_OK;
1032}
1033
1034STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1035{
1036 AutoCaller autoCaller(this);
1037 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1038
1039 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1040
1041 // this can be done in principle in any state as it doesn't affect the VM
1042 // significantly, but play safe by not messing around while complex
1043 // activities are going on
1044 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1045 if (FAILED(rc)) return rc;
1046
1047 setModified(IsModified_MachineData);
1048 mUserData.backup();
1049 mUserData->s.strDescription = aDescription;
1050
1051 return S_OK;
1052}
1053
1054STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1055{
1056 CheckComArgOutPointerValid(aId);
1057
1058 AutoLimitedCaller autoCaller(this);
1059 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1060
1061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1062
1063 mData->mUuid.toUtf16().cloneTo(aId);
1064
1065 return S_OK;
1066}
1067
1068STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1069{
1070 CheckComArgOutSafeArrayPointerValid(aGroups);
1071
1072 AutoCaller autoCaller(this);
1073 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1074
1075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1076 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1077 size_t i = 0;
1078 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1079 it != mUserData->s.llGroups.end();
1080 ++it, i++)
1081 {
1082 Bstr tmp = *it;
1083 tmp.cloneTo(&groups[i]);
1084 }
1085 groups.detachTo(ComSafeArrayOutArg(aGroups));
1086
1087 return S_OK;
1088}
1089
1090STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1091{
1092 AutoCaller autoCaller(this);
1093 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1094
1095 StringsList llGroups;
1096 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1097 if (FAILED(rc))
1098 return rc;
1099
1100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1101
1102 // changing machine groups is possible while the VM is offline
1103 rc = checkStateDependency(OfflineStateDep);
1104 if (FAILED(rc)) return rc;
1105
1106 setModified(IsModified_MachineData);
1107 mUserData.backup();
1108 mUserData->s.llGroups = llGroups;
1109
1110 return S_OK;
1111}
1112
1113STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1114{
1115 CheckComArgOutPointerValid(aOSTypeId);
1116
1117 AutoCaller autoCaller(this);
1118 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1119
1120 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1121
1122 mUserData->s.strOsType.cloneTo(aOSTypeId);
1123
1124 return S_OK;
1125}
1126
1127STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1128{
1129 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1130
1131 AutoCaller autoCaller(this);
1132 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1133
1134 /* look up the object by Id to check it is valid */
1135 ComPtr<IGuestOSType> guestOSType;
1136 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1137 if (FAILED(rc)) return rc;
1138
1139 /* when setting, always use the "etalon" value for consistency -- lookup
1140 * by ID is case-insensitive and the input value may have different case */
1141 Bstr osTypeId;
1142 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1143 if (FAILED(rc)) return rc;
1144
1145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1146
1147 rc = checkStateDependency(MutableStateDep);
1148 if (FAILED(rc)) return rc;
1149
1150 setModified(IsModified_MachineData);
1151 mUserData.backup();
1152 mUserData->s.strOsType = osTypeId;
1153
1154 return S_OK;
1155}
1156
1157
1158STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1159{
1160 CheckComArgOutPointerValid(aFirmwareType);
1161
1162 AutoCaller autoCaller(this);
1163 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1164
1165 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1166
1167 *aFirmwareType = mHWData->mFirmwareType;
1168
1169 return S_OK;
1170}
1171
1172STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1173{
1174 AutoCaller autoCaller(this);
1175 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1176 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1177
1178 HRESULT rc = checkStateDependency(MutableStateDep);
1179 if (FAILED(rc)) return rc;
1180
1181 setModified(IsModified_MachineData);
1182 mHWData.backup();
1183 mHWData->mFirmwareType = aFirmwareType;
1184
1185 return S_OK;
1186}
1187
1188STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1189{
1190 CheckComArgOutPointerValid(aKeyboardHIDType);
1191
1192 AutoCaller autoCaller(this);
1193 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1194
1195 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1196
1197 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1198
1199 return S_OK;
1200}
1201
1202STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1203{
1204 AutoCaller autoCaller(this);
1205 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1206 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1207
1208 HRESULT rc = checkStateDependency(MutableStateDep);
1209 if (FAILED(rc)) return rc;
1210
1211 setModified(IsModified_MachineData);
1212 mHWData.backup();
1213 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1214
1215 return S_OK;
1216}
1217
1218STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1219{
1220 CheckComArgOutPointerValid(aPointingHIDType);
1221
1222 AutoCaller autoCaller(this);
1223 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1224
1225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1226
1227 *aPointingHIDType = mHWData->mPointingHIDType;
1228
1229 return S_OK;
1230}
1231
1232STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1233{
1234 AutoCaller autoCaller(this);
1235 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1236 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1237
1238 HRESULT rc = checkStateDependency(MutableStateDep);
1239 if (FAILED(rc)) return rc;
1240
1241 setModified(IsModified_MachineData);
1242 mHWData.backup();
1243 mHWData->mPointingHIDType = aPointingHIDType;
1244
1245 return S_OK;
1246}
1247
1248STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1249{
1250 CheckComArgOutPointerValid(aChipsetType);
1251
1252 AutoCaller autoCaller(this);
1253 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1254
1255 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1256
1257 *aChipsetType = mHWData->mChipsetType;
1258
1259 return S_OK;
1260}
1261
1262STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1263{
1264 AutoCaller autoCaller(this);
1265 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1266 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1267
1268 HRESULT rc = checkStateDependency(MutableStateDep);
1269 if (FAILED(rc)) return rc;
1270
1271 if (aChipsetType != mHWData->mChipsetType)
1272 {
1273 setModified(IsModified_MachineData);
1274 mHWData.backup();
1275 mHWData->mChipsetType = aChipsetType;
1276
1277 // Resize network adapter array, to be finalized on commit/rollback.
1278 // We must not throw away entries yet, otherwise settings are lost
1279 // without a way to roll back.
1280 uint32_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1281 uint32_t oldCount = mNetworkAdapters.size();
1282 if (newCount > oldCount)
1283 {
1284 mNetworkAdapters.resize(newCount);
1285 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1286 {
1287 unconst(mNetworkAdapters[slot]).createObject();
1288 mNetworkAdapters[slot]->init(this, slot);
1289 }
1290 }
1291 }
1292
1293 return S_OK;
1294}
1295
1296STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1297{
1298 CheckComArgOutPointerValid(aHWVersion);
1299
1300 AutoCaller autoCaller(this);
1301 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1302
1303 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1304
1305 mHWData->mHWVersion.cloneTo(aHWVersion);
1306
1307 return S_OK;
1308}
1309
1310STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1311{
1312 /* check known version */
1313 Utf8Str hwVersion = aHWVersion;
1314 if ( hwVersion.compare("1") != 0
1315 && hwVersion.compare("2") != 0)
1316 return setError(E_INVALIDARG,
1317 tr("Invalid hardware version: %ls\n"), aHWVersion);
1318
1319 AutoCaller autoCaller(this);
1320 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1321
1322 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1323
1324 HRESULT rc = checkStateDependency(MutableStateDep);
1325 if (FAILED(rc)) return rc;
1326
1327 setModified(IsModified_MachineData);
1328 mHWData.backup();
1329 mHWData->mHWVersion = hwVersion;
1330
1331 return S_OK;
1332}
1333
1334STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1335{
1336 CheckComArgOutPointerValid(aUUID);
1337
1338 AutoCaller autoCaller(this);
1339 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1340
1341 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1342
1343 if (mHWData->mHardwareUUID.isValid())
1344 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1345 else
1346 mData->mUuid.toUtf16().cloneTo(aUUID);
1347
1348 return S_OK;
1349}
1350
1351STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1352{
1353 Guid hardwareUUID(aUUID);
1354 if (!hardwareUUID.isValid())
1355 return E_INVALIDARG;
1356
1357 AutoCaller autoCaller(this);
1358 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1359
1360 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1361
1362 HRESULT rc = checkStateDependency(MutableStateDep);
1363 if (FAILED(rc)) return rc;
1364
1365 setModified(IsModified_MachineData);
1366 mHWData.backup();
1367 if (hardwareUUID == mData->mUuid)
1368 mHWData->mHardwareUUID.clear();
1369 else
1370 mHWData->mHardwareUUID = hardwareUUID;
1371
1372 return S_OK;
1373}
1374
1375STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1376{
1377 CheckComArgOutPointerValid(memorySize);
1378
1379 AutoCaller autoCaller(this);
1380 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1381
1382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1383
1384 *memorySize = mHWData->mMemorySize;
1385
1386 return S_OK;
1387}
1388
1389STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1390{
1391 /* check RAM limits */
1392 if ( memorySize < MM_RAM_MIN_IN_MB
1393 || memorySize > MM_RAM_MAX_IN_MB
1394 )
1395 return setError(E_INVALIDARG,
1396 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1397 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1398
1399 AutoCaller autoCaller(this);
1400 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1401
1402 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1403
1404 HRESULT rc = checkStateDependency(MutableStateDep);
1405 if (FAILED(rc)) return rc;
1406
1407 setModified(IsModified_MachineData);
1408 mHWData.backup();
1409 mHWData->mMemorySize = memorySize;
1410
1411 return S_OK;
1412}
1413
1414STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1415{
1416 CheckComArgOutPointerValid(CPUCount);
1417
1418 AutoCaller autoCaller(this);
1419 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1420
1421 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1422
1423 *CPUCount = mHWData->mCPUCount;
1424
1425 return S_OK;
1426}
1427
1428STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1429{
1430 /* check CPU limits */
1431 if ( CPUCount < SchemaDefs::MinCPUCount
1432 || CPUCount > SchemaDefs::MaxCPUCount
1433 )
1434 return setError(E_INVALIDARG,
1435 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1436 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1437
1438 AutoCaller autoCaller(this);
1439 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1440
1441 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1442
1443 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1444 if (mHWData->mCPUHotPlugEnabled)
1445 {
1446 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1447 {
1448 if (mHWData->mCPUAttached[idx])
1449 return setError(E_INVALIDARG,
1450 tr("There is still a CPU attached to socket %lu."
1451 "Detach the CPU before removing the socket"),
1452 CPUCount, idx+1);
1453 }
1454 }
1455
1456 HRESULT rc = checkStateDependency(MutableStateDep);
1457 if (FAILED(rc)) return rc;
1458
1459 setModified(IsModified_MachineData);
1460 mHWData.backup();
1461 mHWData->mCPUCount = CPUCount;
1462
1463 return S_OK;
1464}
1465
1466STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1467{
1468 CheckComArgOutPointerValid(aExecutionCap);
1469
1470 AutoCaller autoCaller(this);
1471 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1472
1473 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1474
1475 *aExecutionCap = mHWData->mCpuExecutionCap;
1476
1477 return S_OK;
1478}
1479
1480STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1481{
1482 HRESULT rc = S_OK;
1483
1484 /* check throttle limits */
1485 if ( aExecutionCap < 1
1486 || aExecutionCap > 100
1487 )
1488 return setError(E_INVALIDARG,
1489 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1490 aExecutionCap, 1, 100);
1491
1492 AutoCaller autoCaller(this);
1493 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1494
1495 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1496
1497 alock.release();
1498 rc = onCPUExecutionCapChange(aExecutionCap);
1499 alock.acquire();
1500 if (FAILED(rc)) return rc;
1501
1502 setModified(IsModified_MachineData);
1503 mHWData.backup();
1504 mHWData->mCpuExecutionCap = aExecutionCap;
1505
1506 /* Save settings if online - todo why is this required?? */
1507 if (Global::IsOnline(mData->mMachineState))
1508 saveSettings(NULL);
1509
1510 return S_OK;
1511}
1512
1513
1514STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *enabled)
1515{
1516 CheckComArgOutPointerValid(enabled);
1517
1518 AutoCaller autoCaller(this);
1519 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1520
1521 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1522
1523 *enabled = mHWData->mCPUHotPlugEnabled;
1524
1525 return S_OK;
1526}
1527
1528STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled)
1529{
1530 HRESULT rc = S_OK;
1531
1532 AutoCaller autoCaller(this);
1533 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1534
1535 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1536
1537 rc = checkStateDependency(MutableStateDep);
1538 if (FAILED(rc)) return rc;
1539
1540 if (mHWData->mCPUHotPlugEnabled != enabled)
1541 {
1542 if (enabled)
1543 {
1544 setModified(IsModified_MachineData);
1545 mHWData.backup();
1546
1547 /* Add the amount of CPUs currently attached */
1548 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1549 {
1550 mHWData->mCPUAttached[i] = true;
1551 }
1552 }
1553 else
1554 {
1555 /*
1556 * We can disable hotplug only if the amount of maximum CPUs is equal
1557 * to the amount of attached CPUs
1558 */
1559 unsigned cCpusAttached = 0;
1560 unsigned iHighestId = 0;
1561
1562 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1563 {
1564 if (mHWData->mCPUAttached[i])
1565 {
1566 cCpusAttached++;
1567 iHighestId = i;
1568 }
1569 }
1570
1571 if ( (cCpusAttached != mHWData->mCPUCount)
1572 || (iHighestId >= mHWData->mCPUCount))
1573 return setError(E_INVALIDARG,
1574 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1575
1576 setModified(IsModified_MachineData);
1577 mHWData.backup();
1578 }
1579 }
1580
1581 mHWData->mCPUHotPlugEnabled = enabled;
1582
1583 return rc;
1584}
1585
1586STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *enabled)
1587{
1588#ifdef VBOX_WITH_USB_CARDREADER
1589 CheckComArgOutPointerValid(enabled);
1590
1591 AutoCaller autoCaller(this);
1592 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1593
1594 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1595
1596 *enabled = mHWData->mEmulatedUSBCardReaderEnabled;
1597
1598 return S_OK;
1599#else
1600 NOREF(enabled);
1601 return E_NOTIMPL;
1602#endif
1603}
1604
1605STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL enabled)
1606{
1607#ifdef VBOX_WITH_USB_CARDREADER
1608 AutoCaller autoCaller(this);
1609 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1610 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1611
1612 HRESULT rc = checkStateDependency(MutableStateDep);
1613 if (FAILED(rc)) return rc;
1614
1615 setModified(IsModified_MachineData);
1616 mHWData.backup();
1617 mHWData->mEmulatedUSBCardReaderEnabled = enabled;
1618
1619 return S_OK;
1620#else
1621 NOREF(enabled);
1622 return E_NOTIMPL;
1623#endif
1624}
1625
1626STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *enabled)
1627{
1628#ifdef VBOX_WITH_USB_VIDEO
1629 CheckComArgOutPointerValid(enabled);
1630
1631 AutoCaller autoCaller(this);
1632 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1633
1634 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1635
1636 *enabled = mHWData->mEmulatedUSBWebcamEnabled;
1637
1638 return S_OK;
1639#else
1640 NOREF(enabled);
1641 return E_NOTIMPL;
1642#endif
1643}
1644
1645STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL enabled)
1646{
1647#ifdef VBOX_WITH_USB_VIDEO
1648 AutoCaller autoCaller(this);
1649 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1650 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1651
1652 HRESULT rc = checkStateDependency(MutableStateDep);
1653 if (FAILED(rc)) return rc;
1654
1655 setModified(IsModified_MachineData);
1656 mHWData.backup();
1657 mHWData->mEmulatedUSBWebcamEnabled = enabled;
1658
1659 return S_OK;
1660#else
1661 NOREF(enabled);
1662 return E_NOTIMPL;
1663#endif
1664}
1665
1666STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *enabled)
1667{
1668 CheckComArgOutPointerValid(enabled);
1669
1670 AutoCaller autoCaller(this);
1671 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1672 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1673
1674 *enabled = mHWData->mHPETEnabled;
1675
1676 return S_OK;
1677}
1678
1679STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL enabled)
1680{
1681 HRESULT rc = S_OK;
1682
1683 AutoCaller autoCaller(this);
1684 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1685 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1686
1687 rc = checkStateDependency(MutableStateDep);
1688 if (FAILED(rc)) return rc;
1689
1690 setModified(IsModified_MachineData);
1691 mHWData.backup();
1692
1693 mHWData->mHPETEnabled = enabled;
1694
1695 return rc;
1696}
1697
1698STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
1699{
1700 AutoCaller autoCaller(this);
1701 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1702
1703 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1704
1705 *fEnabled = mHWData->mVideoCaptureEnabled;
1706 return S_OK;
1707}
1708
1709STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1710{
1711 AutoCaller autoCaller(this);
1712 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1713
1714 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1715 mHWData->mVideoCaptureEnabled = fEnabled;
1716 return S_OK;
1717}
1718
1719STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR * apFile)
1720{
1721 AutoCaller autoCaller(this);
1722 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1723
1724 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1725 mHWData->mVideoCaptureFile.cloneTo(apFile);
1726 return S_OK;
1727}
1728
1729STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1730{
1731 Utf8Str strFile(aFile);
1732 AutoCaller autoCaller(this);
1733 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1734
1735 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1736 if (strFile.isEmpty())
1737 strFile = "VideoCap.webm";
1738 mHWData->mVideoCaptureFile = strFile;
1739 return S_OK;
1740}
1741
1742
1743STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *ulHorzRes)
1744{
1745 AutoCaller autoCaller(this);
1746 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1747
1748 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1749 *ulHorzRes = mHWData->mVideoCaptureWidth;
1750 return S_OK;
1751}
1752
1753STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG ulHorzRes)
1754{
1755 AutoCaller autoCaller(this);
1756 if (FAILED(autoCaller.rc()))
1757 {
1758 LogFlow(("Autolocked failed\n"));
1759 return autoCaller.rc();
1760 }
1761
1762 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1763 mHWData->mVideoCaptureWidth = ulHorzRes;
1764 return S_OK;
1765}
1766
1767STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *ulVertRes)
1768{
1769 AutoCaller autoCaller(this);
1770 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1771
1772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1773 *ulVertRes = mHWData->mVideoCaptureHeight;
1774 return S_OK;
1775}
1776
1777STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG ulVertRes)
1778{
1779 AutoCaller autoCaller(this);
1780 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1781
1782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1783 mHWData->mVideoCaptureHeight = ulVertRes;
1784 return S_OK;
1785}
1786
1787STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1788{
1789 CheckComArgOutPointerValid(memorySize);
1790
1791 AutoCaller autoCaller(this);
1792 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1793
1794 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1795
1796 *memorySize = mHWData->mVRAMSize;
1797
1798 return S_OK;
1799}
1800
1801STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1802{
1803 /* check VRAM limits */
1804 if (memorySize < SchemaDefs::MinGuestVRAM ||
1805 memorySize > SchemaDefs::MaxGuestVRAM)
1806 return setError(E_INVALIDARG,
1807 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1808 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1809
1810 AutoCaller autoCaller(this);
1811 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1812
1813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1814
1815 HRESULT rc = checkStateDependency(MutableStateDep);
1816 if (FAILED(rc)) return rc;
1817
1818 setModified(IsModified_MachineData);
1819 mHWData.backup();
1820 mHWData->mVRAMSize = memorySize;
1821
1822 return S_OK;
1823}
1824
1825/** @todo this method should not be public */
1826STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1827{
1828 CheckComArgOutPointerValid(memoryBalloonSize);
1829
1830 AutoCaller autoCaller(this);
1831 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1832
1833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1834
1835 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
1836
1837 return S_OK;
1838}
1839
1840/**
1841 * Set the memory balloon size.
1842 *
1843 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1844 * we have to make sure that we never call IGuest from here.
1845 */
1846STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
1847{
1848 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1849#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1850 /* check limits */
1851 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1852 return setError(E_INVALIDARG,
1853 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1854 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1855
1856 AutoCaller autoCaller(this);
1857 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1858
1859 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1860
1861 setModified(IsModified_MachineData);
1862 mHWData.backup();
1863 mHWData->mMemoryBalloonSize = memoryBalloonSize;
1864
1865 return S_OK;
1866#else
1867 NOREF(memoryBalloonSize);
1868 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1869#endif
1870}
1871
1872STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *enabled)
1873{
1874 CheckComArgOutPointerValid(enabled);
1875
1876 AutoCaller autoCaller(this);
1877 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1878
1879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1880
1881 *enabled = mHWData->mPageFusionEnabled;
1882 return S_OK;
1883}
1884
1885STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL enabled)
1886{
1887#ifdef VBOX_WITH_PAGE_SHARING
1888 AutoCaller autoCaller(this);
1889 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1890
1891 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1892
1893 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1894 setModified(IsModified_MachineData);
1895 mHWData.backup();
1896 mHWData->mPageFusionEnabled = enabled;
1897 return S_OK;
1898#else
1899 NOREF(enabled);
1900 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1901#endif
1902}
1903
1904STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *enabled)
1905{
1906 CheckComArgOutPointerValid(enabled);
1907
1908 AutoCaller autoCaller(this);
1909 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1910
1911 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1912
1913 *enabled = mHWData->mAccelerate3DEnabled;
1914
1915 return S_OK;
1916}
1917
1918STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
1919{
1920 AutoCaller autoCaller(this);
1921 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1922
1923 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1924
1925 HRESULT rc = checkStateDependency(MutableStateDep);
1926 if (FAILED(rc)) return rc;
1927
1928 /** @todo check validity! */
1929
1930 setModified(IsModified_MachineData);
1931 mHWData.backup();
1932 mHWData->mAccelerate3DEnabled = enable;
1933
1934 return S_OK;
1935}
1936
1937
1938STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *enabled)
1939{
1940 CheckComArgOutPointerValid(enabled);
1941
1942 AutoCaller autoCaller(this);
1943 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1944
1945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1946
1947 *enabled = mHWData->mAccelerate2DVideoEnabled;
1948
1949 return S_OK;
1950}
1951
1952STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
1953{
1954 AutoCaller autoCaller(this);
1955 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1956
1957 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1958
1959 HRESULT rc = checkStateDependency(MutableStateDep);
1960 if (FAILED(rc)) return rc;
1961
1962 /** @todo check validity! */
1963
1964 setModified(IsModified_MachineData);
1965 mHWData.backup();
1966 mHWData->mAccelerate2DVideoEnabled = enable;
1967
1968 return S_OK;
1969}
1970
1971STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
1972{
1973 CheckComArgOutPointerValid(monitorCount);
1974
1975 AutoCaller autoCaller(this);
1976 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1977
1978 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1979
1980 *monitorCount = mHWData->mMonitorCount;
1981
1982 return S_OK;
1983}
1984
1985STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
1986{
1987 /* make sure monitor count is a sensible number */
1988 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
1989 return setError(E_INVALIDARG,
1990 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1991 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
1992
1993 AutoCaller autoCaller(this);
1994 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1995
1996 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1997
1998 HRESULT rc = checkStateDependency(MutableStateDep);
1999 if (FAILED(rc)) return rc;
2000
2001 setModified(IsModified_MachineData);
2002 mHWData.backup();
2003 mHWData->mMonitorCount = monitorCount;
2004
2005 return S_OK;
2006}
2007
2008STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2009{
2010 CheckComArgOutPointerValid(biosSettings);
2011
2012 AutoCaller autoCaller(this);
2013 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2014
2015 /* mBIOSSettings is constant during life time, no need to lock */
2016 mBIOSSettings.queryInterfaceTo(biosSettings);
2017
2018 return S_OK;
2019}
2020
2021STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2022{
2023 CheckComArgOutPointerValid(aVal);
2024
2025 AutoCaller autoCaller(this);
2026 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2027
2028 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2029
2030 switch(property)
2031 {
2032 case CPUPropertyType_PAE:
2033 *aVal = mHWData->mPAEEnabled;
2034 break;
2035
2036 case CPUPropertyType_Synthetic:
2037 *aVal = mHWData->mSyntheticCpu;
2038 break;
2039
2040 default:
2041 return E_INVALIDARG;
2042 }
2043 return S_OK;
2044}
2045
2046STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2047{
2048 AutoCaller autoCaller(this);
2049 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2050
2051 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2052
2053 HRESULT rc = checkStateDependency(MutableStateDep);
2054 if (FAILED(rc)) return rc;
2055
2056 switch(property)
2057 {
2058 case CPUPropertyType_PAE:
2059 setModified(IsModified_MachineData);
2060 mHWData.backup();
2061 mHWData->mPAEEnabled = !!aVal;
2062 break;
2063
2064 case CPUPropertyType_Synthetic:
2065 setModified(IsModified_MachineData);
2066 mHWData.backup();
2067 mHWData->mSyntheticCpu = !!aVal;
2068 break;
2069
2070 default:
2071 return E_INVALIDARG;
2072 }
2073 return S_OK;
2074}
2075
2076STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2077{
2078 CheckComArgOutPointerValid(aValEax);
2079 CheckComArgOutPointerValid(aValEbx);
2080 CheckComArgOutPointerValid(aValEcx);
2081 CheckComArgOutPointerValid(aValEdx);
2082
2083 AutoCaller autoCaller(this);
2084 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2085
2086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2087
2088 switch(aId)
2089 {
2090 case 0x0:
2091 case 0x1:
2092 case 0x2:
2093 case 0x3:
2094 case 0x4:
2095 case 0x5:
2096 case 0x6:
2097 case 0x7:
2098 case 0x8:
2099 case 0x9:
2100 case 0xA:
2101 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2102 return E_INVALIDARG;
2103
2104 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2105 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2106 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2107 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2108 break;
2109
2110 case 0x80000000:
2111 case 0x80000001:
2112 case 0x80000002:
2113 case 0x80000003:
2114 case 0x80000004:
2115 case 0x80000005:
2116 case 0x80000006:
2117 case 0x80000007:
2118 case 0x80000008:
2119 case 0x80000009:
2120 case 0x8000000A:
2121 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2122 return E_INVALIDARG;
2123
2124 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2125 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2126 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2127 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2128 break;
2129
2130 default:
2131 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2132 }
2133 return S_OK;
2134}
2135
2136STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2137{
2138 AutoCaller autoCaller(this);
2139 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2140
2141 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2142
2143 HRESULT rc = checkStateDependency(MutableStateDep);
2144 if (FAILED(rc)) return rc;
2145
2146 switch(aId)
2147 {
2148 case 0x0:
2149 case 0x1:
2150 case 0x2:
2151 case 0x3:
2152 case 0x4:
2153 case 0x5:
2154 case 0x6:
2155 case 0x7:
2156 case 0x8:
2157 case 0x9:
2158 case 0xA:
2159 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2160 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2161 setModified(IsModified_MachineData);
2162 mHWData.backup();
2163 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2164 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2165 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2166 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2167 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2168 break;
2169
2170 case 0x80000000:
2171 case 0x80000001:
2172 case 0x80000002:
2173 case 0x80000003:
2174 case 0x80000004:
2175 case 0x80000005:
2176 case 0x80000006:
2177 case 0x80000007:
2178 case 0x80000008:
2179 case 0x80000009:
2180 case 0x8000000A:
2181 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2182 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2183 setModified(IsModified_MachineData);
2184 mHWData.backup();
2185 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2186 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2187 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2188 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2189 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2190 break;
2191
2192 default:
2193 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2194 }
2195 return S_OK;
2196}
2197
2198STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2199{
2200 AutoCaller autoCaller(this);
2201 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2202
2203 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2204
2205 HRESULT rc = checkStateDependency(MutableStateDep);
2206 if (FAILED(rc)) return rc;
2207
2208 switch(aId)
2209 {
2210 case 0x0:
2211 case 0x1:
2212 case 0x2:
2213 case 0x3:
2214 case 0x4:
2215 case 0x5:
2216 case 0x6:
2217 case 0x7:
2218 case 0x8:
2219 case 0x9:
2220 case 0xA:
2221 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2222 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2223 setModified(IsModified_MachineData);
2224 mHWData.backup();
2225 /* Invalidate leaf. */
2226 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2227 break;
2228
2229 case 0x80000000:
2230 case 0x80000001:
2231 case 0x80000002:
2232 case 0x80000003:
2233 case 0x80000004:
2234 case 0x80000005:
2235 case 0x80000006:
2236 case 0x80000007:
2237 case 0x80000008:
2238 case 0x80000009:
2239 case 0x8000000A:
2240 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2241 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2242 setModified(IsModified_MachineData);
2243 mHWData.backup();
2244 /* Invalidate leaf. */
2245 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2246 break;
2247
2248 default:
2249 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2250 }
2251 return S_OK;
2252}
2253
2254STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2255{
2256 AutoCaller autoCaller(this);
2257 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2258
2259 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2260
2261 HRESULT rc = checkStateDependency(MutableStateDep);
2262 if (FAILED(rc)) return rc;
2263
2264 setModified(IsModified_MachineData);
2265 mHWData.backup();
2266
2267 /* Invalidate all standard leafs. */
2268 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2269 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2270
2271 /* Invalidate all extended leafs. */
2272 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2273 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2274
2275 return S_OK;
2276}
2277
2278STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2279{
2280 CheckComArgOutPointerValid(aVal);
2281
2282 AutoCaller autoCaller(this);
2283 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2284
2285 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2286
2287 switch(property)
2288 {
2289 case HWVirtExPropertyType_Enabled:
2290 *aVal = mHWData->mHWVirtExEnabled;
2291 break;
2292
2293 case HWVirtExPropertyType_Exclusive:
2294 *aVal = mHWData->mHWVirtExExclusive;
2295 break;
2296
2297 case HWVirtExPropertyType_VPID:
2298 *aVal = mHWData->mHWVirtExVPIDEnabled;
2299 break;
2300
2301 case HWVirtExPropertyType_NestedPaging:
2302 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2303 break;
2304
2305 case HWVirtExPropertyType_LargePages:
2306 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2307#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2308 *aVal = FALSE;
2309#endif
2310 break;
2311
2312 case HWVirtExPropertyType_Force:
2313 *aVal = mHWData->mHWVirtExForceEnabled;
2314 break;
2315
2316 default:
2317 return E_INVALIDARG;
2318 }
2319 return S_OK;
2320}
2321
2322STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2323{
2324 AutoCaller autoCaller(this);
2325 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2326
2327 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2328
2329 HRESULT rc = checkStateDependency(MutableStateDep);
2330 if (FAILED(rc)) return rc;
2331
2332 switch(property)
2333 {
2334 case HWVirtExPropertyType_Enabled:
2335 setModified(IsModified_MachineData);
2336 mHWData.backup();
2337 mHWData->mHWVirtExEnabled = !!aVal;
2338 break;
2339
2340 case HWVirtExPropertyType_Exclusive:
2341 setModified(IsModified_MachineData);
2342 mHWData.backup();
2343 mHWData->mHWVirtExExclusive = !!aVal;
2344 break;
2345
2346 case HWVirtExPropertyType_VPID:
2347 setModified(IsModified_MachineData);
2348 mHWData.backup();
2349 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2350 break;
2351
2352 case HWVirtExPropertyType_NestedPaging:
2353 setModified(IsModified_MachineData);
2354 mHWData.backup();
2355 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2356 break;
2357
2358 case HWVirtExPropertyType_LargePages:
2359 setModified(IsModified_MachineData);
2360 mHWData.backup();
2361 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2362 break;
2363
2364 case HWVirtExPropertyType_Force:
2365 setModified(IsModified_MachineData);
2366 mHWData.backup();
2367 mHWData->mHWVirtExForceEnabled = !!aVal;
2368 break;
2369
2370 default:
2371 return E_INVALIDARG;
2372 }
2373
2374 return S_OK;
2375}
2376
2377STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2378{
2379 CheckComArgOutPointerValid(aSnapshotFolder);
2380
2381 AutoCaller autoCaller(this);
2382 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2383
2384 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2385
2386 Utf8Str strFullSnapshotFolder;
2387 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2388 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2389
2390 return S_OK;
2391}
2392
2393STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2394{
2395 /* @todo (r=dmik):
2396 * 1. Allow to change the name of the snapshot folder containing snapshots
2397 * 2. Rename the folder on disk instead of just changing the property
2398 * value (to be smart and not to leave garbage). Note that it cannot be
2399 * done here because the change may be rolled back. Thus, the right
2400 * place is #saveSettings().
2401 */
2402
2403 AutoCaller autoCaller(this);
2404 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2405
2406 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2407
2408 HRESULT rc = checkStateDependency(MutableStateDep);
2409 if (FAILED(rc)) return rc;
2410
2411 if (!mData->mCurrentSnapshot.isNull())
2412 return setError(E_FAIL,
2413 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2414
2415 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2416
2417 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2418 if (strSnapshotFolder.isEmpty())
2419 strSnapshotFolder = "Snapshots";
2420 int vrc = calculateFullPath(strSnapshotFolder,
2421 strSnapshotFolder);
2422 if (RT_FAILURE(vrc))
2423 return setError(E_FAIL,
2424 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2425 aSnapshotFolder, vrc);
2426
2427 setModified(IsModified_MachineData);
2428 mUserData.backup();
2429
2430 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2431
2432 return S_OK;
2433}
2434
2435STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2436{
2437 CheckComArgOutSafeArrayPointerValid(aAttachments);
2438
2439 AutoCaller autoCaller(this);
2440 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2441
2442 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2443
2444 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2445 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2446
2447 return S_OK;
2448}
2449
2450STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2451{
2452 CheckComArgOutPointerValid(vrdeServer);
2453
2454 AutoCaller autoCaller(this);
2455 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2456
2457 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2458
2459 Assert(!!mVRDEServer);
2460 mVRDEServer.queryInterfaceTo(vrdeServer);
2461
2462 return S_OK;
2463}
2464
2465STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2466{
2467 CheckComArgOutPointerValid(audioAdapter);
2468
2469 AutoCaller autoCaller(this);
2470 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2471
2472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2473
2474 mAudioAdapter.queryInterfaceTo(audioAdapter);
2475 return S_OK;
2476}
2477
2478STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2479{
2480#ifdef VBOX_WITH_VUSB
2481 CheckComArgOutPointerValid(aUSBController);
2482
2483 AutoCaller autoCaller(this);
2484 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2485
2486 clearError();
2487 MultiResult rc(S_OK);
2488
2489# ifdef VBOX_WITH_USB
2490 rc = mParent->host()->checkUSBProxyService();
2491 if (FAILED(rc)) return rc;
2492# endif
2493
2494 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2495
2496 return rc = mUSBController.queryInterfaceTo(aUSBController);
2497#else
2498 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2499 * extended error info to indicate that USB is simply not available
2500 * (w/o treating it as a failure), for example, as in OSE */
2501 NOREF(aUSBController);
2502 ReturnComNotImplemented();
2503#endif /* VBOX_WITH_VUSB */
2504}
2505
2506STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2507{
2508 CheckComArgOutPointerValid(aFilePath);
2509
2510 AutoLimitedCaller autoCaller(this);
2511 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2512
2513 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2514
2515 mData->m_strConfigFileFull.cloneTo(aFilePath);
2516 return S_OK;
2517}
2518
2519STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2520{
2521 CheckComArgOutPointerValid(aModified);
2522
2523 AutoCaller autoCaller(this);
2524 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2525
2526 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2527
2528 HRESULT rc = checkStateDependency(MutableStateDep);
2529 if (FAILED(rc)) return rc;
2530
2531 if (!mData->pMachineConfigFile->fileExists())
2532 // this is a new machine, and no config file exists yet:
2533 *aModified = TRUE;
2534 else
2535 *aModified = (mData->flModifications != 0);
2536
2537 return S_OK;
2538}
2539
2540STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2541{
2542 CheckComArgOutPointerValid(aSessionState);
2543
2544 AutoCaller autoCaller(this);
2545 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2546
2547 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2548
2549 *aSessionState = mData->mSession.mState;
2550
2551 return S_OK;
2552}
2553
2554STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2555{
2556 CheckComArgOutPointerValid(aSessionType);
2557
2558 AutoCaller autoCaller(this);
2559 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2560
2561 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2562
2563 mData->mSession.mType.cloneTo(aSessionType);
2564
2565 return S_OK;
2566}
2567
2568STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2569{
2570 CheckComArgOutPointerValid(aSessionPID);
2571
2572 AutoCaller autoCaller(this);
2573 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2574
2575 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2576
2577 *aSessionPID = mData->mSession.mPID;
2578
2579 return S_OK;
2580}
2581
2582STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2583{
2584 CheckComArgOutPointerValid(machineState);
2585
2586 AutoCaller autoCaller(this);
2587 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2588
2589 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2590
2591 *machineState = mData->mMachineState;
2592
2593 return S_OK;
2594}
2595
2596STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2597{
2598 CheckComArgOutPointerValid(aLastStateChange);
2599
2600 AutoCaller autoCaller(this);
2601 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2602
2603 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2604
2605 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2606
2607 return S_OK;
2608}
2609
2610STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2611{
2612 CheckComArgOutPointerValid(aStateFilePath);
2613
2614 AutoCaller autoCaller(this);
2615 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2616
2617 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2618
2619 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2620
2621 return S_OK;
2622}
2623
2624STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2625{
2626 CheckComArgOutPointerValid(aLogFolder);
2627
2628 AutoCaller autoCaller(this);
2629 AssertComRCReturnRC(autoCaller.rc());
2630
2631 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2632
2633 Utf8Str logFolder;
2634 getLogFolder(logFolder);
2635 logFolder.cloneTo(aLogFolder);
2636
2637 return S_OK;
2638}
2639
2640STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2641{
2642 CheckComArgOutPointerValid(aCurrentSnapshot);
2643
2644 AutoCaller autoCaller(this);
2645 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2646
2647 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2648
2649 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2650
2651 return S_OK;
2652}
2653
2654STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2655{
2656 CheckComArgOutPointerValid(aSnapshotCount);
2657
2658 AutoCaller autoCaller(this);
2659 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2660
2661 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2662
2663 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2664 ? 0
2665 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2666
2667 return S_OK;
2668}
2669
2670STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2671{
2672 CheckComArgOutPointerValid(aCurrentStateModified);
2673
2674 AutoCaller autoCaller(this);
2675 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2676
2677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2678
2679 /* Note: for machines with no snapshots, we always return FALSE
2680 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2681 * reasons :) */
2682
2683 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2684 ? FALSE
2685 : mData->mCurrentStateModified;
2686
2687 return S_OK;
2688}
2689
2690STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2691{
2692 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2693
2694 AutoCaller autoCaller(this);
2695 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2696
2697 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2698
2699 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2700 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2701
2702 return S_OK;
2703}
2704
2705STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2706{
2707 CheckComArgOutPointerValid(aClipboardMode);
2708
2709 AutoCaller autoCaller(this);
2710 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2711
2712 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2713
2714 *aClipboardMode = mHWData->mClipboardMode;
2715
2716 return S_OK;
2717}
2718
2719STDMETHODIMP
2720Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2721{
2722 HRESULT rc = S_OK;
2723
2724 AutoCaller autoCaller(this);
2725 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2726
2727 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2728
2729 alock.release();
2730 rc = onClipboardModeChange(aClipboardMode);
2731 alock.acquire();
2732 if (FAILED(rc)) return rc;
2733
2734 setModified(IsModified_MachineData);
2735 mHWData.backup();
2736 mHWData->mClipboardMode = aClipboardMode;
2737
2738 /* Save settings if online - todo why is this required?? */
2739 if (Global::IsOnline(mData->mMachineState))
2740 saveSettings(NULL);
2741
2742 return S_OK;
2743}
2744
2745STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
2746{
2747 CheckComArgOutPointerValid(aDragAndDropMode);
2748
2749 AutoCaller autoCaller(this);
2750 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2751
2752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2753
2754 *aDragAndDropMode = mHWData->mDragAndDropMode;
2755
2756 return S_OK;
2757}
2758
2759STDMETHODIMP
2760Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
2761{
2762 HRESULT rc = S_OK;
2763
2764 AutoCaller autoCaller(this);
2765 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2766
2767 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2768
2769 alock.release();
2770 rc = onDragAndDropModeChange(aDragAndDropMode);
2771 alock.acquire();
2772 if (FAILED(rc)) return rc;
2773
2774 setModified(IsModified_MachineData);
2775 mHWData.backup();
2776 mHWData->mDragAndDropMode = aDragAndDropMode;
2777
2778 /* Save settings if online - todo why is this required?? */
2779 if (Global::IsOnline(mData->mMachineState))
2780 saveSettings(NULL);
2781
2782 return S_OK;
2783}
2784
2785STDMETHODIMP
2786Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
2787{
2788 CheckComArgOutPointerValid(aPatterns);
2789
2790 AutoCaller autoCaller(this);
2791 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2792
2793 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2794
2795 try
2796 {
2797 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
2798 }
2799 catch (...)
2800 {
2801 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2802 }
2803
2804 return S_OK;
2805}
2806
2807STDMETHODIMP
2808Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
2809{
2810 AutoCaller autoCaller(this);
2811 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2812
2813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2814
2815 HRESULT rc = checkStateDependency(MutableStateDep);
2816 if (FAILED(rc)) return rc;
2817
2818 setModified(IsModified_MachineData);
2819 mHWData.backup();
2820 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
2821 return rc;
2822}
2823
2824STDMETHODIMP
2825Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
2826{
2827 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
2828
2829 AutoCaller autoCaller(this);
2830 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2831
2832 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2833
2834 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
2835 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
2836
2837 return S_OK;
2838}
2839
2840STDMETHODIMP
2841Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
2842{
2843 CheckComArgOutPointerValid(aEnabled);
2844
2845 AutoCaller autoCaller(this);
2846 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2847
2848 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2849
2850 *aEnabled = mUserData->s.fTeleporterEnabled;
2851
2852 return S_OK;
2853}
2854
2855STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
2856{
2857 AutoCaller autoCaller(this);
2858 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2859
2860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2861
2862 /* Only allow it to be set to true when PoweredOff or Aborted.
2863 (Clearing it is always permitted.) */
2864 if ( aEnabled
2865 && mData->mRegistered
2866 && ( !isSessionMachine()
2867 || ( mData->mMachineState != MachineState_PoweredOff
2868 && mData->mMachineState != MachineState_Teleported
2869 && mData->mMachineState != MachineState_Aborted
2870 )
2871 )
2872 )
2873 return setError(VBOX_E_INVALID_VM_STATE,
2874 tr("The machine is not powered off (state is %s)"),
2875 Global::stringifyMachineState(mData->mMachineState));
2876
2877 setModified(IsModified_MachineData);
2878 mUserData.backup();
2879 mUserData->s.fTeleporterEnabled = !!aEnabled;
2880
2881 return S_OK;
2882}
2883
2884STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
2885{
2886 CheckComArgOutPointerValid(aPort);
2887
2888 AutoCaller autoCaller(this);
2889 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2890
2891 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2892
2893 *aPort = (ULONG)mUserData->s.uTeleporterPort;
2894
2895 return S_OK;
2896}
2897
2898STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
2899{
2900 if (aPort >= _64K)
2901 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
2902
2903 AutoCaller autoCaller(this);
2904 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2905
2906 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2907
2908 HRESULT rc = checkStateDependency(MutableStateDep);
2909 if (FAILED(rc)) return rc;
2910
2911 setModified(IsModified_MachineData);
2912 mUserData.backup();
2913 mUserData->s.uTeleporterPort = (uint32_t)aPort;
2914
2915 return S_OK;
2916}
2917
2918STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
2919{
2920 CheckComArgOutPointerValid(aAddress);
2921
2922 AutoCaller autoCaller(this);
2923 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2924
2925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2926
2927 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
2928
2929 return S_OK;
2930}
2931
2932STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
2933{
2934 AutoCaller autoCaller(this);
2935 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2936
2937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2938
2939 HRESULT rc = checkStateDependency(MutableStateDep);
2940 if (FAILED(rc)) return rc;
2941
2942 setModified(IsModified_MachineData);
2943 mUserData.backup();
2944 mUserData->s.strTeleporterAddress = aAddress;
2945
2946 return S_OK;
2947}
2948
2949STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
2950{
2951 CheckComArgOutPointerValid(aPassword);
2952
2953 AutoCaller autoCaller(this);
2954 HRESULT hrc = autoCaller.rc();
2955 if (SUCCEEDED(hrc))
2956 {
2957 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2958 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
2959 }
2960
2961 return hrc;
2962}
2963
2964STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
2965{
2966 /*
2967 * Hash the password first.
2968 */
2969 Utf8Str strPassword(aPassword);
2970 if (!strPassword.isEmpty())
2971 {
2972 if (VBoxIsPasswordHashed(&strPassword))
2973 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2974 VBoxHashPassword(&strPassword);
2975 }
2976
2977 /*
2978 * Do the update.
2979 */
2980 AutoCaller autoCaller(this);
2981 HRESULT hrc = autoCaller.rc();
2982 if (SUCCEEDED(hrc))
2983 {
2984 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2985 hrc = checkStateDependency(MutableStateDep);
2986 if (SUCCEEDED(hrc))
2987 {
2988 setModified(IsModified_MachineData);
2989 mUserData.backup();
2990 mUserData->s.strTeleporterPassword = strPassword;
2991 }
2992 }
2993
2994 return hrc;
2995}
2996
2997STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
2998{
2999 CheckComArgOutPointerValid(aState);
3000
3001 AutoCaller autoCaller(this);
3002 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3003
3004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3005
3006 *aState = mUserData->s.enmFaultToleranceState;
3007 return S_OK;
3008}
3009
3010STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
3011{
3012 AutoCaller autoCaller(this);
3013 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3014
3015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3016
3017 /* @todo deal with running state change. */
3018 HRESULT rc = checkStateDependency(MutableStateDep);
3019 if (FAILED(rc)) return rc;
3020
3021 setModified(IsModified_MachineData);
3022 mUserData.backup();
3023 mUserData->s.enmFaultToleranceState = aState;
3024 return S_OK;
3025}
3026
3027STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3028{
3029 CheckComArgOutPointerValid(aAddress);
3030
3031 AutoCaller autoCaller(this);
3032 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3033
3034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3035
3036 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3037 return S_OK;
3038}
3039
3040STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3041{
3042 AutoCaller autoCaller(this);
3043 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3044
3045 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3046
3047 /* @todo deal with running state change. */
3048 HRESULT rc = checkStateDependency(MutableStateDep);
3049 if (FAILED(rc)) return rc;
3050
3051 setModified(IsModified_MachineData);
3052 mUserData.backup();
3053 mUserData->s.strFaultToleranceAddress = aAddress;
3054 return S_OK;
3055}
3056
3057STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3058{
3059 CheckComArgOutPointerValid(aPort);
3060
3061 AutoCaller autoCaller(this);
3062 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3063
3064 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3065
3066 *aPort = mUserData->s.uFaultTolerancePort;
3067 return S_OK;
3068}
3069
3070STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3071{
3072 AutoCaller autoCaller(this);
3073 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3074
3075 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3076
3077 /* @todo deal with running state change. */
3078 HRESULT rc = checkStateDependency(MutableStateDep);
3079 if (FAILED(rc)) return rc;
3080
3081 setModified(IsModified_MachineData);
3082 mUserData.backup();
3083 mUserData->s.uFaultTolerancePort = aPort;
3084 return S_OK;
3085}
3086
3087STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3088{
3089 CheckComArgOutPointerValid(aPassword);
3090
3091 AutoCaller autoCaller(this);
3092 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3093
3094 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3095
3096 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3097
3098 return S_OK;
3099}
3100
3101STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3102{
3103 AutoCaller autoCaller(this);
3104 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3105
3106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3107
3108 /* @todo deal with running state change. */
3109 HRESULT rc = checkStateDependency(MutableStateDep);
3110 if (FAILED(rc)) return rc;
3111
3112 setModified(IsModified_MachineData);
3113 mUserData.backup();
3114 mUserData->s.strFaultTolerancePassword = aPassword;
3115
3116 return S_OK;
3117}
3118
3119STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3120{
3121 CheckComArgOutPointerValid(aInterval);
3122
3123 AutoCaller autoCaller(this);
3124 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3125
3126 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3127
3128 *aInterval = mUserData->s.uFaultToleranceInterval;
3129 return S_OK;
3130}
3131
3132STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3133{
3134 AutoCaller autoCaller(this);
3135 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3136
3137 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3138
3139 /* @todo deal with running state change. */
3140 HRESULT rc = checkStateDependency(MutableStateDep);
3141 if (FAILED(rc)) return rc;
3142
3143 setModified(IsModified_MachineData);
3144 mUserData.backup();
3145 mUserData->s.uFaultToleranceInterval = aInterval;
3146 return S_OK;
3147}
3148
3149STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3150{
3151 CheckComArgOutPointerValid(aEnabled);
3152
3153 AutoCaller autoCaller(this);
3154 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3155
3156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3157
3158 *aEnabled = mUserData->s.fRTCUseUTC;
3159
3160 return S_OK;
3161}
3162
3163STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3164{
3165 AutoCaller autoCaller(this);
3166 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3167
3168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3169
3170 /* Only allow it to be set to true when PoweredOff or Aborted.
3171 (Clearing it is always permitted.) */
3172 if ( aEnabled
3173 && mData->mRegistered
3174 && ( !isSessionMachine()
3175 || ( mData->mMachineState != MachineState_PoweredOff
3176 && mData->mMachineState != MachineState_Teleported
3177 && mData->mMachineState != MachineState_Aborted
3178 )
3179 )
3180 )
3181 return setError(VBOX_E_INVALID_VM_STATE,
3182 tr("The machine is not powered off (state is %s)"),
3183 Global::stringifyMachineState(mData->mMachineState));
3184
3185 setModified(IsModified_MachineData);
3186 mUserData.backup();
3187 mUserData->s.fRTCUseUTC = !!aEnabled;
3188
3189 return S_OK;
3190}
3191
3192STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3193{
3194 CheckComArgOutPointerValid(aEnabled);
3195
3196 AutoCaller autoCaller(this);
3197 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3198
3199 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3200
3201 *aEnabled = mHWData->mIOCacheEnabled;
3202
3203 return S_OK;
3204}
3205
3206STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3207{
3208 AutoCaller autoCaller(this);
3209 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3210
3211 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3212
3213 HRESULT rc = checkStateDependency(MutableStateDep);
3214 if (FAILED(rc)) return rc;
3215
3216 setModified(IsModified_MachineData);
3217 mHWData.backup();
3218 mHWData->mIOCacheEnabled = aEnabled;
3219
3220 return S_OK;
3221}
3222
3223STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3224{
3225 CheckComArgOutPointerValid(aIOCacheSize);
3226
3227 AutoCaller autoCaller(this);
3228 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3229
3230 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3231
3232 *aIOCacheSize = mHWData->mIOCacheSize;
3233
3234 return S_OK;
3235}
3236
3237STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3238{
3239 AutoCaller autoCaller(this);
3240 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3241
3242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3243
3244 HRESULT rc = checkStateDependency(MutableStateDep);
3245 if (FAILED(rc)) return rc;
3246
3247 setModified(IsModified_MachineData);
3248 mHWData.backup();
3249 mHWData->mIOCacheSize = aIOCacheSize;
3250
3251 return S_OK;
3252}
3253
3254
3255/**
3256 * @note Locks objects!
3257 */
3258STDMETHODIMP Machine::LockMachine(ISession *aSession,
3259 LockType_T lockType)
3260{
3261 CheckComArgNotNull(aSession);
3262
3263 AutoCaller autoCaller(this);
3264 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3265
3266 /* check the session state */
3267 SessionState_T state;
3268 HRESULT rc = aSession->COMGETTER(State)(&state);
3269 if (FAILED(rc)) return rc;
3270
3271 if (state != SessionState_Unlocked)
3272 return setError(VBOX_E_INVALID_OBJECT_STATE,
3273 tr("The given session is busy"));
3274
3275 // get the client's IInternalSessionControl interface
3276 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3277 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3278 E_INVALIDARG);
3279
3280 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3281
3282 if (!mData->mRegistered)
3283 return setError(E_UNEXPECTED,
3284 tr("The machine '%s' is not registered"),
3285 mUserData->s.strName.c_str());
3286
3287 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3288
3289 SessionState_T oldState = mData->mSession.mState;
3290 /* Hack: in case the session is closing and there is a progress object
3291 * which allows waiting for the session to be closed, take the opportunity
3292 * and do a limited wait (max. 1 second). This helps a lot when the system
3293 * is busy and thus session closing can take a little while. */
3294 if ( mData->mSession.mState == SessionState_Unlocking
3295 && mData->mSession.mProgress)
3296 {
3297 alock.release();
3298 mData->mSession.mProgress->WaitForCompletion(1000);
3299 alock.acquire();
3300 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3301 }
3302
3303 // try again now
3304 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3305 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3306 )
3307 {
3308 // OK, share the session... we are now dealing with three processes:
3309 // 1) VBoxSVC (where this code runs);
3310 // 2) process C: the caller's client process (who wants a shared session);
3311 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3312
3313 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3314 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3315 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3316 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3317 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3318
3319 /*
3320 * Release the lock before calling the client process. It's safe here
3321 * since the only thing to do after we get the lock again is to add
3322 * the remote control to the list (which doesn't directly influence
3323 * anything).
3324 */
3325 alock.release();
3326
3327 // get the console of the session holding the write lock (this is a remote call)
3328 ComPtr<IConsole> pConsoleW;
3329 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3330 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3331 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3332 if (FAILED(rc))
3333 // the failure may occur w/o any error info (from RPC), so provide one
3334 return setError(VBOX_E_VM_ERROR,
3335 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3336
3337 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3338
3339 // share the session machine and W's console with the caller's session
3340 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3341 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3342 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3343
3344 if (FAILED(rc))
3345 // the failure may occur w/o any error info (from RPC), so provide one
3346 return setError(VBOX_E_VM_ERROR,
3347 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3348 alock.acquire();
3349
3350 // need to revalidate the state after acquiring the lock again
3351 if (mData->mSession.mState != SessionState_Locked)
3352 {
3353 pSessionControl->Uninitialize();
3354 return setError(VBOX_E_INVALID_SESSION_STATE,
3355 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3356 mUserData->s.strName.c_str());
3357 }
3358
3359 // add the caller's session to the list
3360 mData->mSession.mRemoteControls.push_back(pSessionControl);
3361 }
3362 else if ( mData->mSession.mState == SessionState_Locked
3363 || mData->mSession.mState == SessionState_Unlocking
3364 )
3365 {
3366 // sharing not permitted, or machine still unlocking:
3367 return setError(VBOX_E_INVALID_OBJECT_STATE,
3368 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3369 mUserData->s.strName.c_str());
3370 }
3371 else
3372 {
3373 // machine is not locked: then write-lock the machine (create the session machine)
3374
3375 // must not be busy
3376 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3377
3378 // get the caller's session PID
3379 RTPROCESS pid = NIL_RTPROCESS;
3380 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3381 pSessionControl->GetPID((ULONG*)&pid);
3382 Assert(pid != NIL_RTPROCESS);
3383
3384 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3385
3386 if (fLaunchingVMProcess)
3387 {
3388 // this machine is awaiting for a spawning session to be opened:
3389 // then the calling process must be the one that got started by
3390 // LaunchVMProcess()
3391
3392 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3393 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3394
3395 if (mData->mSession.mPID != pid)
3396 return setError(E_ACCESSDENIED,
3397 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3398 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3399 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3400 }
3401
3402 // create the mutable SessionMachine from the current machine
3403 ComObjPtr<SessionMachine> sessionMachine;
3404 sessionMachine.createObject();
3405 rc = sessionMachine->init(this);
3406 AssertComRC(rc);
3407
3408 /* NOTE: doing return from this function after this point but
3409 * before the end is forbidden since it may call SessionMachine::uninit()
3410 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3411 * lock while still holding the Machine lock in alock so that a deadlock
3412 * is possible due to the wrong lock order. */
3413
3414 if (SUCCEEDED(rc))
3415 {
3416 /*
3417 * Set the session state to Spawning to protect against subsequent
3418 * attempts to open a session and to unregister the machine after
3419 * we release the lock.
3420 */
3421 SessionState_T origState = mData->mSession.mState;
3422 mData->mSession.mState = SessionState_Spawning;
3423
3424 /*
3425 * Release the lock before calling the client process -- it will call
3426 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3427 * because the state is Spawning, so that LaunchVMProcess() and
3428 * LockMachine() calls will fail. This method, called before we
3429 * acquire the lock again, will fail because of the wrong PID.
3430 *
3431 * Note that mData->mSession.mRemoteControls accessed outside
3432 * the lock may not be modified when state is Spawning, so it's safe.
3433 */
3434 alock.release();
3435
3436 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3437 rc = pSessionControl->AssignMachine(sessionMachine, lockType);
3438 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3439
3440 /* The failure may occur w/o any error info (from RPC), so provide one */
3441 if (FAILED(rc))
3442 setError(VBOX_E_VM_ERROR,
3443 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3444
3445 if ( SUCCEEDED(rc)
3446 && fLaunchingVMProcess
3447 )
3448 {
3449 /* complete the remote session initialization */
3450
3451 /* get the console from the direct session */
3452 ComPtr<IConsole> console;
3453 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3454 ComAssertComRC(rc);
3455
3456 if (SUCCEEDED(rc) && !console)
3457 {
3458 ComAssert(!!console);
3459 rc = E_FAIL;
3460 }
3461
3462 /* assign machine & console to the remote session */
3463 if (SUCCEEDED(rc))
3464 {
3465 /*
3466 * after LaunchVMProcess(), the first and the only
3467 * entry in remoteControls is that remote session
3468 */
3469 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3470 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3471 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3472
3473 /* The failure may occur w/o any error info (from RPC), so provide one */
3474 if (FAILED(rc))
3475 setError(VBOX_E_VM_ERROR,
3476 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3477 }
3478
3479 if (FAILED(rc))
3480 pSessionControl->Uninitialize();
3481 }
3482
3483 /* acquire the lock again */
3484 alock.acquire();
3485
3486 /* Restore the session state */
3487 mData->mSession.mState = origState;
3488 }
3489
3490 // finalize spawning anyway (this is why we don't return on errors above)
3491 if (fLaunchingVMProcess)
3492 {
3493 /* Note that the progress object is finalized later */
3494 /** @todo Consider checking mData->mSession.mProgress for cancellation
3495 * around here. */
3496
3497 /* We don't reset mSession.mPID here because it is necessary for
3498 * SessionMachine::uninit() to reap the child process later. */
3499
3500 if (FAILED(rc))
3501 {
3502 /* Close the remote session, remove the remote control from the list
3503 * and reset session state to Closed (@note keep the code in sync
3504 * with the relevant part in openSession()). */
3505
3506 Assert(mData->mSession.mRemoteControls.size() == 1);
3507 if (mData->mSession.mRemoteControls.size() == 1)
3508 {
3509 ErrorInfoKeeper eik;
3510 mData->mSession.mRemoteControls.front()->Uninitialize();
3511 }
3512
3513 mData->mSession.mRemoteControls.clear();
3514 mData->mSession.mState = SessionState_Unlocked;
3515 }
3516 }
3517 else
3518 {
3519 /* memorize PID of the directly opened session */
3520 if (SUCCEEDED(rc))
3521 mData->mSession.mPID = pid;
3522 }
3523
3524 if (SUCCEEDED(rc))
3525 {
3526 /* memorize the direct session control and cache IUnknown for it */
3527 mData->mSession.mDirectControl = pSessionControl;
3528 mData->mSession.mState = SessionState_Locked;
3529 /* associate the SessionMachine with this Machine */
3530 mData->mSession.mMachine = sessionMachine;
3531
3532 /* request an IUnknown pointer early from the remote party for later
3533 * identity checks (it will be internally cached within mDirectControl
3534 * at least on XPCOM) */
3535 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3536 NOREF(unk);
3537 }
3538
3539 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3540 * would break the lock order */
3541 alock.release();
3542
3543 /* uninitialize the created session machine on failure */
3544 if (FAILED(rc))
3545 sessionMachine->uninit();
3546
3547 }
3548
3549 if (SUCCEEDED(rc))
3550 {
3551 /*
3552 * tell the client watcher thread to update the set of
3553 * machines that have open sessions
3554 */
3555 mParent->updateClientWatcher();
3556
3557 if (oldState != SessionState_Locked)
3558 /* fire an event */
3559 mParent->onSessionStateChange(getId(), SessionState_Locked);
3560 }
3561
3562 return rc;
3563}
3564
3565/**
3566 * @note Locks objects!
3567 */
3568STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3569 IN_BSTR aType,
3570 IN_BSTR aEnvironment,
3571 IProgress **aProgress)
3572{
3573 CheckComArgStrNotEmptyOrNull(aType);
3574 Utf8Str strType(aType);
3575 Utf8Str strEnvironment(aEnvironment);
3576 /* "emergencystop" doesn't need the session, so skip the checks/interface
3577 * retrieval. This code doesn't quite fit in here, but introducing a
3578 * special API method would be even more effort, and would require explicit
3579 * support by every API client. It's better to hide the feature a bit. */
3580 if (strType != "emergencystop")
3581 CheckComArgNotNull(aSession);
3582 CheckComArgOutPointerValid(aProgress);
3583
3584 AutoCaller autoCaller(this);
3585 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3586
3587 ComPtr<IInternalSessionControl> control;
3588 HRESULT rc = S_OK;
3589
3590 if (strType != "emergencystop")
3591 {
3592 /* check the session state */
3593 SessionState_T state;
3594 rc = aSession->COMGETTER(State)(&state);
3595 if (FAILED(rc))
3596 return rc;
3597
3598 if (state != SessionState_Unlocked)
3599 return setError(VBOX_E_INVALID_OBJECT_STATE,
3600 tr("The given session is busy"));
3601
3602 /* get the IInternalSessionControl interface */
3603 control = aSession;
3604 ComAssertMsgRet(!control.isNull(),
3605 ("No IInternalSessionControl interface"),
3606 E_INVALIDARG);
3607 }
3608
3609 /* get the teleporter enable state for the progress object init. */
3610 BOOL fTeleporterEnabled;
3611 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3612 if (FAILED(rc))
3613 return rc;
3614
3615 /* create a progress object */
3616 if (strType != "emergencystop")
3617 {
3618 ComObjPtr<ProgressProxy> progress;
3619 progress.createObject();
3620 rc = progress->init(mParent,
3621 static_cast<IMachine*>(this),
3622 Bstr(tr("Starting VM")).raw(),
3623 TRUE /* aCancelable */,
3624 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3625 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strType.c_str()).raw(),
3626 2 /* uFirstOperationWeight */,
3627 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3628
3629 if (SUCCEEDED(rc))
3630 {
3631 rc = launchVMProcess(control, strType, strEnvironment, progress);
3632 if (SUCCEEDED(rc))
3633 {
3634 progress.queryInterfaceTo(aProgress);
3635
3636 /* signal the client watcher thread */
3637 mParent->updateClientWatcher();
3638
3639 /* fire an event */
3640 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3641 }
3642 }
3643 }
3644 else
3645 {
3646 /* no progress object - either instant success or failure */
3647 *aProgress = NULL;
3648
3649 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3650
3651 if (mData->mSession.mState != SessionState_Locked)
3652 return setError(VBOX_E_INVALID_OBJECT_STATE,
3653 tr("The machine '%s' is not locked by a session"),
3654 mUserData->s.strName.c_str());
3655
3656 /* must have a VM process associated - do not kill normal API clients
3657 * with an open session */
3658 if (!Global::IsOnline(mData->mMachineState))
3659 return setError(VBOX_E_INVALID_OBJECT_STATE,
3660 tr("The machine '%s' does not have a VM process"),
3661 mUserData->s.strName.c_str());
3662
3663 /* forcibly terminate the VM process */
3664 if (mData->mSession.mPID != NIL_RTPROCESS)
3665 RTProcTerminate(mData->mSession.mPID);
3666
3667 /* signal the client watcher thread, as most likely the client has
3668 * been terminated */
3669 mParent->updateClientWatcher();
3670 }
3671
3672 return rc;
3673}
3674
3675STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3676{
3677 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3678 return setError(E_INVALIDARG,
3679 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3680 aPosition, SchemaDefs::MaxBootPosition);
3681
3682 if (aDevice == DeviceType_USB)
3683 return setError(E_NOTIMPL,
3684 tr("Booting from USB device is currently not supported"));
3685
3686 AutoCaller autoCaller(this);
3687 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3688
3689 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3690
3691 HRESULT rc = checkStateDependency(MutableStateDep);
3692 if (FAILED(rc)) return rc;
3693
3694 setModified(IsModified_MachineData);
3695 mHWData.backup();
3696 mHWData->mBootOrder[aPosition - 1] = aDevice;
3697
3698 return S_OK;
3699}
3700
3701STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3702{
3703 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3704 return setError(E_INVALIDARG,
3705 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3706 aPosition, SchemaDefs::MaxBootPosition);
3707
3708 AutoCaller autoCaller(this);
3709 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3710
3711 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3712
3713 *aDevice = mHWData->mBootOrder[aPosition - 1];
3714
3715 return S_OK;
3716}
3717
3718STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
3719 LONG aControllerPort,
3720 LONG aDevice,
3721 DeviceType_T aType,
3722 IMedium *aMedium)
3723{
3724 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3725 aControllerName, aControllerPort, aDevice, aType, aMedium));
3726
3727 CheckComArgStrNotEmptyOrNull(aControllerName);
3728
3729 AutoCaller autoCaller(this);
3730 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3731
3732 // request the host lock first, since might be calling Host methods for getting host drives;
3733 // next, protect the media tree all the while we're in here, as well as our member variables
3734 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
3735 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3736
3737 HRESULT rc = checkStateDependency(MutableStateDep);
3738 if (FAILED(rc)) return rc;
3739
3740 /// @todo NEWMEDIA implicit machine registration
3741 if (!mData->mRegistered)
3742 return setError(VBOX_E_INVALID_OBJECT_STATE,
3743 tr("Cannot attach storage devices to an unregistered machine"));
3744
3745 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3746
3747 /* Check for an existing controller. */
3748 ComObjPtr<StorageController> ctl;
3749 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
3750 if (FAILED(rc)) return rc;
3751
3752 StorageControllerType_T ctrlType;
3753 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3754 if (FAILED(rc))
3755 return setError(E_FAIL,
3756 tr("Could not get type of controller '%ls'"),
3757 aControllerName);
3758
3759 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3760 bool fHotplug = false;
3761 if (Global::IsOnlineOrTransient(mData->mMachineState))
3762 fHotplug = true;
3763
3764 if (fHotplug && !isControllerHotplugCapable(ctrlType))
3765 return setError(VBOX_E_INVALID_VM_STATE,
3766 tr("Controller '%ls' does not support hotplugging"),
3767 aControllerName);
3768
3769 // check that the port and device are not out of range
3770 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
3771 if (FAILED(rc)) return rc;
3772
3773 /* check if the device slot is already busy */
3774 MediumAttachment *pAttachTemp;
3775 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
3776 aControllerName,
3777 aControllerPort,
3778 aDevice)))
3779 {
3780 Medium *pMedium = pAttachTemp->getMedium();
3781 if (pMedium)
3782 {
3783 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3784 return setError(VBOX_E_OBJECT_IN_USE,
3785 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3786 pMedium->getLocationFull().c_str(),
3787 aControllerPort,
3788 aDevice,
3789 aControllerName);
3790 }
3791 else
3792 return setError(VBOX_E_OBJECT_IN_USE,
3793 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3794 aControllerPort, aDevice, aControllerName);
3795 }
3796
3797 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
3798 if (aMedium && medium.isNull())
3799 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3800
3801 AutoCaller mediumCaller(medium);
3802 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3803
3804 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3805
3806 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
3807 && !medium.isNull()
3808 )
3809 return setError(VBOX_E_OBJECT_IN_USE,
3810 tr("Medium '%s' is already attached to this virtual machine"),
3811 medium->getLocationFull().c_str());
3812
3813 if (!medium.isNull())
3814 {
3815 MediumType_T mtype = medium->getType();
3816 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3817 // For DVDs it's not written to the config file, so needs no global config
3818 // version bump. For floppies it's a new attribute "type", which is ignored
3819 // by older VirtualBox version, so needs no global config version bump either.
3820 // For hard disks this type is not accepted.
3821 if (mtype == MediumType_MultiAttach)
3822 {
3823 // This type is new with VirtualBox 4.0 and therefore requires settings
3824 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3825 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3826 // two reasons: The medium type is a property of the media registry tree, which
3827 // can reside in the global config file (for pre-4.0 media); we would therefore
3828 // possibly need to bump the global config version. We don't want to do that though
3829 // because that might make downgrading to pre-4.0 impossible.
3830 // As a result, we can only use these two new types if the medium is NOT in the
3831 // global registry:
3832 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
3833 if ( medium->isInRegistry(uuidGlobalRegistry)
3834 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3835 )
3836 return setError(VBOX_E_INVALID_OBJECT_STATE,
3837 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3838 "to machines that were created with VirtualBox 4.0 or later"),
3839 medium->getLocationFull().c_str());
3840 }
3841 }
3842
3843 bool fIndirect = false;
3844 if (!medium.isNull())
3845 fIndirect = medium->isReadOnly();
3846 bool associate = true;
3847
3848 do
3849 {
3850 if ( aType == DeviceType_HardDisk
3851 && mMediaData.isBackedUp())
3852 {
3853 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3854
3855 /* check if the medium was attached to the VM before we started
3856 * changing attachments in which case the attachment just needs to
3857 * be restored */
3858 if ((pAttachTemp = findAttachment(oldAtts, medium)))
3859 {
3860 AssertReturn(!fIndirect, E_FAIL);
3861
3862 /* see if it's the same bus/channel/device */
3863 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
3864 {
3865 /* the simplest case: restore the whole attachment
3866 * and return, nothing else to do */
3867 mMediaData->mAttachments.push_back(pAttachTemp);
3868 return S_OK;
3869 }
3870
3871 /* bus/channel/device differ; we need a new attachment object,
3872 * but don't try to associate it again */
3873 associate = false;
3874 break;
3875 }
3876 }
3877
3878 /* go further only if the attachment is to be indirect */
3879 if (!fIndirect)
3880 break;
3881
3882 /* perform the so called smart attachment logic for indirect
3883 * attachments. Note that smart attachment is only applicable to base
3884 * hard disks. */
3885
3886 if (medium->getParent().isNull())
3887 {
3888 /* first, investigate the backup copy of the current hard disk
3889 * attachments to make it possible to re-attach existing diffs to
3890 * another device slot w/o losing their contents */
3891 if (mMediaData.isBackedUp())
3892 {
3893 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3894
3895 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3896 uint32_t foundLevel = 0;
3897
3898 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
3899 it != oldAtts.end();
3900 ++it)
3901 {
3902 uint32_t level = 0;
3903 MediumAttachment *pAttach = *it;
3904 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3905 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3906 if (pMedium.isNull())
3907 continue;
3908
3909 if (pMedium->getBase(&level) == medium)
3910 {
3911 /* skip the hard disk if its currently attached (we
3912 * cannot attach the same hard disk twice) */
3913 if (findAttachment(mMediaData->mAttachments,
3914 pMedium))
3915 continue;
3916
3917 /* matched device, channel and bus (i.e. attached to the
3918 * same place) will win and immediately stop the search;
3919 * otherwise the attachment that has the youngest
3920 * descendant of medium will be used
3921 */
3922 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
3923 {
3924 /* the simplest case: restore the whole attachment
3925 * and return, nothing else to do */
3926 mMediaData->mAttachments.push_back(*it);
3927 return S_OK;
3928 }
3929 else if ( foundIt == oldAtts.end()
3930 || level > foundLevel /* prefer younger */
3931 )
3932 {
3933 foundIt = it;
3934 foundLevel = level;
3935 }
3936 }
3937 }
3938
3939 if (foundIt != oldAtts.end())
3940 {
3941 /* use the previously attached hard disk */
3942 medium = (*foundIt)->getMedium();
3943 mediumCaller.attach(medium);
3944 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3945 mediumLock.attach(medium);
3946 /* not implicit, doesn't require association with this VM */
3947 fIndirect = false;
3948 associate = false;
3949 /* go right to the MediumAttachment creation */
3950 break;
3951 }
3952 }
3953
3954 /* must give up the medium lock and medium tree lock as below we
3955 * go over snapshots, which needs a lock with higher lock order. */
3956 mediumLock.release();
3957 treeLock.release();
3958
3959 /* then, search through snapshots for the best diff in the given
3960 * hard disk's chain to base the new diff on */
3961
3962 ComObjPtr<Medium> base;
3963 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3964 while (snap)
3965 {
3966 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3967
3968 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
3969
3970 MediumAttachment *pAttachFound = NULL;
3971 uint32_t foundLevel = 0;
3972
3973 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
3974 it != snapAtts.end();
3975 ++it)
3976 {
3977 MediumAttachment *pAttach = *it;
3978 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3979 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3980 if (pMedium.isNull())
3981 continue;
3982
3983 uint32_t level = 0;
3984 if (pMedium->getBase(&level) == medium)
3985 {
3986 /* matched device, channel and bus (i.e. attached to the
3987 * same place) will win and immediately stop the search;
3988 * otherwise the attachment that has the youngest
3989 * descendant of medium will be used
3990 */
3991 if ( pAttach->getDevice() == aDevice
3992 && pAttach->getPort() == aControllerPort
3993 && pAttach->getControllerName() == aControllerName
3994 )
3995 {
3996 pAttachFound = pAttach;
3997 break;
3998 }
3999 else if ( !pAttachFound
4000 || level > foundLevel /* prefer younger */
4001 )
4002 {
4003 pAttachFound = pAttach;
4004 foundLevel = level;
4005 }
4006 }
4007 }
4008
4009 if (pAttachFound)
4010 {
4011 base = pAttachFound->getMedium();
4012 break;
4013 }
4014
4015 snap = snap->getParent();
4016 }
4017
4018 /* re-lock medium tree and the medium, as we need it below */
4019 treeLock.acquire();
4020 mediumLock.acquire();
4021
4022 /* found a suitable diff, use it as a base */
4023 if (!base.isNull())
4024 {
4025 medium = base;
4026 mediumCaller.attach(medium);
4027 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4028 mediumLock.attach(medium);
4029 }
4030 }
4031
4032 Utf8Str strFullSnapshotFolder;
4033 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4034
4035 ComObjPtr<Medium> diff;
4036 diff.createObject();
4037 // store this diff in the same registry as the parent
4038 Guid uuidRegistryParent;
4039 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4040 {
4041 // parent image has no registry: this can happen if we're attaching a new immutable
4042 // image that has not yet been attached (medium then points to the base and we're
4043 // creating the diff image for the immutable, and the parent is not yet registered);
4044 // put the parent in the machine registry then
4045 mediumLock.release();
4046 treeLock.release();
4047 alock.release();
4048 addMediumToRegistry(medium);
4049 alock.acquire();
4050 treeLock.acquire();
4051 mediumLock.acquire();
4052 medium->getFirstRegistryMachineId(uuidRegistryParent);
4053 }
4054 rc = diff->init(mParent,
4055 medium->getPreferredDiffFormat(),
4056 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4057 uuidRegistryParent);
4058 if (FAILED(rc)) return rc;
4059
4060 /* Apply the normal locking logic to the entire chain. */
4061 MediumLockList *pMediumLockList(new MediumLockList());
4062 mediumLock.release();
4063 treeLock.release();
4064 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4065 true /* fMediumLockWrite */,
4066 medium,
4067 *pMediumLockList);
4068 treeLock.acquire();
4069 mediumLock.acquire();
4070 if (SUCCEEDED(rc))
4071 {
4072 mediumLock.release();
4073 treeLock.release();
4074 rc = pMediumLockList->Lock();
4075 treeLock.acquire();
4076 mediumLock.acquire();
4077 if (FAILED(rc))
4078 setError(rc,
4079 tr("Could not lock medium when creating diff '%s'"),
4080 diff->getLocationFull().c_str());
4081 else
4082 {
4083 /* will release the lock before the potentially lengthy
4084 * operation, so protect with the special state */
4085 MachineState_T oldState = mData->mMachineState;
4086 setMachineState(MachineState_SettingUp);
4087
4088 mediumLock.release();
4089 treeLock.release();
4090 alock.release();
4091
4092 rc = medium->createDiffStorage(diff,
4093 MediumVariant_Standard,
4094 pMediumLockList,
4095 NULL /* aProgress */,
4096 true /* aWait */);
4097
4098 alock.acquire();
4099 treeLock.acquire();
4100 mediumLock.acquire();
4101
4102 setMachineState(oldState);
4103 }
4104 }
4105
4106 /* Unlock the media and free the associated memory. */
4107 delete pMediumLockList;
4108
4109 if (FAILED(rc)) return rc;
4110
4111 /* use the created diff for the actual attachment */
4112 medium = diff;
4113 mediumCaller.attach(medium);
4114 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4115 mediumLock.attach(medium);
4116 }
4117 while (0);
4118
4119 ComObjPtr<MediumAttachment> attachment;
4120 attachment.createObject();
4121 rc = attachment->init(this,
4122 medium,
4123 aControllerName,
4124 aControllerPort,
4125 aDevice,
4126 aType,
4127 fIndirect,
4128 false /* fPassthrough */,
4129 false /* fTempEject */,
4130 false /* fNonRotational */,
4131 false /* fDiscard */,
4132 Utf8Str::Empty);
4133 if (FAILED(rc)) return rc;
4134
4135 if (associate && !medium.isNull())
4136 {
4137 // as the last step, associate the medium to the VM
4138 rc = medium->addBackReference(mData->mUuid);
4139 // here we can fail because of Deleting, or being in process of creating a Diff
4140 if (FAILED(rc)) return rc;
4141
4142 mediumLock.release();
4143 treeLock.release();
4144 alock.release();
4145 addMediumToRegistry(medium);
4146 alock.acquire();
4147 treeLock.acquire();
4148 mediumLock.acquire();
4149 }
4150
4151 /* success: finally remember the attachment */
4152 setModified(IsModified_Storage);
4153 mMediaData.backup();
4154 mMediaData->mAttachments.push_back(attachment);
4155
4156 mediumLock.release();
4157 treeLock.release();
4158 alock.release();
4159
4160 if (fHotplug)
4161 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */);
4162
4163 mParent->saveModifiedRegistries();
4164
4165 return rc;
4166}
4167
4168STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4169 LONG aDevice)
4170{
4171 CheckComArgStrNotEmptyOrNull(aControllerName);
4172
4173 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4174 aControllerName, aControllerPort, aDevice));
4175
4176 AutoCaller autoCaller(this);
4177 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4178
4179 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4180
4181 HRESULT rc = checkStateDependency(MutableStateDep);
4182 if (FAILED(rc)) return rc;
4183
4184 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4185
4186 /* Check for an existing controller. */
4187 ComObjPtr<StorageController> ctl;
4188 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4189 if (FAILED(rc)) return rc;
4190
4191 StorageControllerType_T ctrlType;
4192 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4193 if (FAILED(rc))
4194 return setError(E_FAIL,
4195 tr("Could not get type of controller '%ls'"),
4196 aControllerName);
4197
4198 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4199 bool fHotplug = false;
4200 if (Global::IsOnlineOrTransient(mData->mMachineState))
4201 fHotplug = true;
4202
4203 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4204 return setError(VBOX_E_INVALID_VM_STATE,
4205 tr("Controller '%ls' does not support hotplugging"),
4206 aControllerName);
4207
4208 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4209 aControllerName,
4210 aControllerPort,
4211 aDevice);
4212 if (!pAttach)
4213 return setError(VBOX_E_OBJECT_NOT_FOUND,
4214 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4215 aDevice, aControllerPort, aControllerName);
4216
4217 /*
4218 * The VM has to detach the device before we delete any implicit diffs.
4219 * If this fails we can roll back without loosing data.
4220 */
4221 if (fHotplug)
4222 {
4223 alock.release();
4224 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */);
4225 alock.acquire();
4226 }
4227 if (FAILED(rc)) return rc;
4228
4229 /* If we are here everything went well and we can delete the implicit now. */
4230 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4231
4232 alock.release();
4233
4234 mParent->saveModifiedRegistries();
4235
4236 return rc;
4237}
4238
4239STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4240 LONG aDevice, BOOL aPassthrough)
4241{
4242 CheckComArgStrNotEmptyOrNull(aControllerName);
4243
4244 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4245 aControllerName, aControllerPort, aDevice, aPassthrough));
4246
4247 AutoCaller autoCaller(this);
4248 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4249
4250 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4251
4252 HRESULT rc = checkStateDependency(MutableStateDep);
4253 if (FAILED(rc)) return rc;
4254
4255 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4256
4257 if (Global::IsOnlineOrTransient(mData->mMachineState))
4258 return setError(VBOX_E_INVALID_VM_STATE,
4259 tr("Invalid machine state: %s"),
4260 Global::stringifyMachineState(mData->mMachineState));
4261
4262 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4263 aControllerName,
4264 aControllerPort,
4265 aDevice);
4266 if (!pAttach)
4267 return setError(VBOX_E_OBJECT_NOT_FOUND,
4268 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4269 aDevice, aControllerPort, aControllerName);
4270
4271
4272 setModified(IsModified_Storage);
4273 mMediaData.backup();
4274
4275 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4276
4277 if (pAttach->getType() != DeviceType_DVD)
4278 return setError(E_INVALIDARG,
4279 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4280 aDevice, aControllerPort, aControllerName);
4281 pAttach->updatePassthrough(!!aPassthrough);
4282
4283 return S_OK;
4284}
4285
4286STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4287 LONG aDevice, BOOL aTemporaryEject)
4288{
4289 CheckComArgStrNotEmptyOrNull(aControllerName);
4290
4291 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4292 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4293
4294 AutoCaller autoCaller(this);
4295 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4296
4297 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4298
4299 HRESULT rc = checkStateDependency(MutableStateDep);
4300 if (FAILED(rc)) return rc;
4301
4302 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4303 aControllerName,
4304 aControllerPort,
4305 aDevice);
4306 if (!pAttach)
4307 return setError(VBOX_E_OBJECT_NOT_FOUND,
4308 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4309 aDevice, aControllerPort, aControllerName);
4310
4311
4312 setModified(IsModified_Storage);
4313 mMediaData.backup();
4314
4315 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4316
4317 if (pAttach->getType() != DeviceType_DVD)
4318 return setError(E_INVALIDARG,
4319 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4320 aDevice, aControllerPort, aControllerName);
4321 pAttach->updateTempEject(!!aTemporaryEject);
4322
4323 return S_OK;
4324}
4325
4326STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4327 LONG aDevice, BOOL aNonRotational)
4328{
4329 CheckComArgStrNotEmptyOrNull(aControllerName);
4330
4331 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4332 aControllerName, aControllerPort, aDevice, aNonRotational));
4333
4334 AutoCaller autoCaller(this);
4335 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4336
4337 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4338
4339 HRESULT rc = checkStateDependency(MutableStateDep);
4340 if (FAILED(rc)) return rc;
4341
4342 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4343
4344 if (Global::IsOnlineOrTransient(mData->mMachineState))
4345 return setError(VBOX_E_INVALID_VM_STATE,
4346 tr("Invalid machine state: %s"),
4347 Global::stringifyMachineState(mData->mMachineState));
4348
4349 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4350 aControllerName,
4351 aControllerPort,
4352 aDevice);
4353 if (!pAttach)
4354 return setError(VBOX_E_OBJECT_NOT_FOUND,
4355 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4356 aDevice, aControllerPort, aControllerName);
4357
4358
4359 setModified(IsModified_Storage);
4360 mMediaData.backup();
4361
4362 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4363
4364 if (pAttach->getType() != DeviceType_HardDisk)
4365 return setError(E_INVALIDARG,
4366 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"),
4367 aDevice, aControllerPort, aControllerName);
4368 pAttach->updateNonRotational(!!aNonRotational);
4369
4370 return S_OK;
4371}
4372
4373STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4374 LONG aDevice, BOOL aDiscard)
4375{
4376 CheckComArgStrNotEmptyOrNull(aControllerName);
4377
4378 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4379 aControllerName, aControllerPort, aDevice, aDiscard));
4380
4381 AutoCaller autoCaller(this);
4382 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4383
4384 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4385
4386 HRESULT rc = checkStateDependency(MutableStateDep);
4387 if (FAILED(rc)) return rc;
4388
4389 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4390
4391 if (Global::IsOnlineOrTransient(mData->mMachineState))
4392 return setError(VBOX_E_INVALID_VM_STATE,
4393 tr("Invalid machine state: %s"),
4394 Global::stringifyMachineState(mData->mMachineState));
4395
4396 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4397 aControllerName,
4398 aControllerPort,
4399 aDevice);
4400 if (!pAttach)
4401 return setError(VBOX_E_OBJECT_NOT_FOUND,
4402 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4403 aDevice, aControllerPort, aControllerName);
4404
4405
4406 setModified(IsModified_Storage);
4407 mMediaData.backup();
4408
4409 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4410
4411 if (pAttach->getType() != DeviceType_HardDisk)
4412 return setError(E_INVALIDARG,
4413 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"),
4414 aDevice, aControllerPort, aControllerName);
4415 pAttach->updateDiscard(!!aDiscard);
4416
4417 return S_OK;
4418}
4419
4420STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4421 LONG aDevice)
4422{
4423 int rc = S_OK;
4424 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4425 aControllerName, aControllerPort, aDevice));
4426
4427 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4428
4429 return rc;
4430}
4431
4432STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4433 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4434{
4435 CheckComArgStrNotEmptyOrNull(aControllerName);
4436
4437 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4438 aControllerName, aControllerPort, aDevice));
4439
4440 AutoCaller autoCaller(this);
4441 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4442
4443 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4444
4445 HRESULT rc = checkStateDependency(MutableStateDep);
4446 if (FAILED(rc)) return rc;
4447
4448 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4449
4450 if (Global::IsOnlineOrTransient(mData->mMachineState))
4451 return setError(VBOX_E_INVALID_VM_STATE,
4452 tr("Invalid machine state: %s"),
4453 Global::stringifyMachineState(mData->mMachineState));
4454
4455 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4456 aControllerName,
4457 aControllerPort,
4458 aDevice);
4459 if (!pAttach)
4460 return setError(VBOX_E_OBJECT_NOT_FOUND,
4461 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4462 aDevice, aControllerPort, aControllerName);
4463
4464
4465 setModified(IsModified_Storage);
4466 mMediaData.backup();
4467
4468 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4469 if (aBandwidthGroup && group.isNull())
4470 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4471
4472 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4473
4474 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4475 if (strBandwidthGroupOld.isNotEmpty())
4476 {
4477 /* Get the bandwidth group object and release it - this must not fail. */
4478 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4479 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4480 Assert(SUCCEEDED(rc));
4481
4482 pBandwidthGroupOld->release();
4483 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4484 }
4485
4486 if (!group.isNull())
4487 {
4488 group->reference();
4489 pAttach->updateBandwidthGroup(group->getName());
4490 }
4491
4492 return S_OK;
4493}
4494
4495STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4496 LONG aControllerPort,
4497 LONG aDevice,
4498 DeviceType_T aType)
4499{
4500 HRESULT rc = S_OK;
4501
4502 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4503 aControllerName, aControllerPort, aDevice, aType));
4504
4505 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4506
4507 return rc;
4508}
4509
4510
4511
4512STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4513 LONG aControllerPort,
4514 LONG aDevice,
4515 BOOL aForce)
4516{
4517 int rc = S_OK;
4518 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4519 aControllerName, aControllerPort, aForce));
4520
4521 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4522
4523 return rc;
4524}
4525
4526STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4527 LONG aControllerPort,
4528 LONG aDevice,
4529 IMedium *aMedium,
4530 BOOL aForce)
4531{
4532 int rc = S_OK;
4533 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4534 aControllerName, aControllerPort, aDevice, aForce));
4535
4536 CheckComArgStrNotEmptyOrNull(aControllerName);
4537
4538 AutoCaller autoCaller(this);
4539 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4540
4541 // request the host lock first, since might be calling Host methods for getting host drives;
4542 // next, protect the media tree all the while we're in here, as well as our member variables
4543 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4544 this->lockHandle(),
4545 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4546
4547 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4548 aControllerName,
4549 aControllerPort,
4550 aDevice);
4551 if (pAttach.isNull())
4552 return setError(VBOX_E_OBJECT_NOT_FOUND,
4553 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4554 aDevice, aControllerPort, aControllerName);
4555
4556 /* Remember previously mounted medium. The medium before taking the
4557 * backup is not necessarily the same thing. */
4558 ComObjPtr<Medium> oldmedium;
4559 oldmedium = pAttach->getMedium();
4560
4561 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4562 if (aMedium && pMedium.isNull())
4563 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4564
4565 AutoCaller mediumCaller(pMedium);
4566 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4567
4568 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4569 if (pMedium)
4570 {
4571 DeviceType_T mediumType = pAttach->getType();
4572 switch (mediumType)
4573 {
4574 case DeviceType_DVD:
4575 case DeviceType_Floppy:
4576 break;
4577
4578 default:
4579 return setError(VBOX_E_INVALID_OBJECT_STATE,
4580 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
4581 aControllerPort,
4582 aDevice,
4583 aControllerName);
4584 }
4585 }
4586
4587 setModified(IsModified_Storage);
4588 mMediaData.backup();
4589
4590 {
4591 // The backup operation makes the pAttach reference point to the
4592 // old settings. Re-get the correct reference.
4593 pAttach = findAttachment(mMediaData->mAttachments,
4594 aControllerName,
4595 aControllerPort,
4596 aDevice);
4597 if (!oldmedium.isNull())
4598 oldmedium->removeBackReference(mData->mUuid);
4599 if (!pMedium.isNull())
4600 {
4601 pMedium->addBackReference(mData->mUuid);
4602
4603 mediumLock.release();
4604 multiLock.release();
4605 addMediumToRegistry(pMedium);
4606 multiLock.acquire();
4607 mediumLock.acquire();
4608 }
4609
4610 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4611 pAttach->updateMedium(pMedium);
4612 }
4613
4614 setModified(IsModified_Storage);
4615
4616 mediumLock.release();
4617 multiLock.release();
4618 rc = onMediumChange(pAttach, aForce);
4619 multiLock.acquire();
4620 mediumLock.acquire();
4621
4622 /* On error roll back this change only. */
4623 if (FAILED(rc))
4624 {
4625 if (!pMedium.isNull())
4626 pMedium->removeBackReference(mData->mUuid);
4627 pAttach = findAttachment(mMediaData->mAttachments,
4628 aControllerName,
4629 aControllerPort,
4630 aDevice);
4631 /* If the attachment is gone in the meantime, bail out. */
4632 if (pAttach.isNull())
4633 return rc;
4634 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4635 if (!oldmedium.isNull())
4636 oldmedium->addBackReference(mData->mUuid);
4637 pAttach->updateMedium(oldmedium);
4638 }
4639
4640 mediumLock.release();
4641 multiLock.release();
4642
4643 mParent->saveModifiedRegistries();
4644
4645 return rc;
4646}
4647
4648STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
4649 LONG aControllerPort,
4650 LONG aDevice,
4651 IMedium **aMedium)
4652{
4653 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4654 aControllerName, aControllerPort, aDevice));
4655
4656 CheckComArgStrNotEmptyOrNull(aControllerName);
4657 CheckComArgOutPointerValid(aMedium);
4658
4659 AutoCaller autoCaller(this);
4660 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4661
4662 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4663
4664 *aMedium = NULL;
4665
4666 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4667 aControllerName,
4668 aControllerPort,
4669 aDevice);
4670 if (pAttach.isNull())
4671 return setError(VBOX_E_OBJECT_NOT_FOUND,
4672 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4673 aDevice, aControllerPort, aControllerName);
4674
4675 pAttach->getMedium().queryInterfaceTo(aMedium);
4676
4677 return S_OK;
4678}
4679
4680STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
4681{
4682 CheckComArgOutPointerValid(port);
4683 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
4684
4685 AutoCaller autoCaller(this);
4686 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4687
4688 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4689
4690 mSerialPorts[slot].queryInterfaceTo(port);
4691
4692 return S_OK;
4693}
4694
4695STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
4696{
4697 CheckComArgOutPointerValid(port);
4698 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
4699
4700 AutoCaller autoCaller(this);
4701 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4702
4703 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4704
4705 mParallelPorts[slot].queryInterfaceTo(port);
4706
4707 return S_OK;
4708}
4709
4710STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
4711{
4712 CheckComArgOutPointerValid(adapter);
4713 /* Do not assert if slot is out of range, just return the advertised
4714 status. testdriver/vbox.py triggers this in logVmInfo. */
4715 if (slot >= mNetworkAdapters.size())
4716 return setError(E_INVALIDARG,
4717 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4718 slot, mNetworkAdapters.size());
4719
4720 AutoCaller autoCaller(this);
4721 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4722
4723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4724
4725 mNetworkAdapters[slot].queryInterfaceTo(adapter);
4726
4727 return S_OK;
4728}
4729
4730STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
4731{
4732 CheckComArgOutSafeArrayPointerValid(aKeys);
4733
4734 AutoCaller autoCaller(this);
4735 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4736
4737 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4738
4739 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4740 int i = 0;
4741 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4742 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4743 ++it, ++i)
4744 {
4745 const Utf8Str &strKey = it->first;
4746 strKey.cloneTo(&saKeys[i]);
4747 }
4748 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
4749
4750 return S_OK;
4751 }
4752
4753 /**
4754 * @note Locks this object for reading.
4755 */
4756STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
4757 BSTR *aValue)
4758{
4759 CheckComArgStrNotEmptyOrNull(aKey);
4760 CheckComArgOutPointerValid(aValue);
4761
4762 AutoCaller autoCaller(this);
4763 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4764
4765 /* start with nothing found */
4766 Bstr bstrResult("");
4767
4768 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4769
4770 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
4771 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4772 // found:
4773 bstrResult = it->second; // source is a Utf8Str
4774
4775 /* return the result to caller (may be empty) */
4776 bstrResult.cloneTo(aValue);
4777
4778 return S_OK;
4779}
4780
4781 /**
4782 * @note Locks mParent for writing + this object for writing.
4783 */
4784STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
4785{
4786 CheckComArgStrNotEmptyOrNull(aKey);
4787
4788 AutoCaller autoCaller(this);
4789 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4790
4791 Utf8Str strKey(aKey);
4792 Utf8Str strValue(aValue);
4793 Utf8Str strOldValue; // empty
4794
4795 // locking note: we only hold the read lock briefly to look up the old value,
4796 // then release it and call the onExtraCanChange callbacks. There is a small
4797 // chance of a race insofar as the callback might be called twice if two callers
4798 // change the same key at the same time, but that's a much better solution
4799 // than the deadlock we had here before. The actual changing of the extradata
4800 // is then performed under the write lock and race-free.
4801
4802 // look up the old value first; if nothing has changed then we need not do anything
4803 {
4804 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4805 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
4806 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4807 strOldValue = it->second;
4808 }
4809
4810 bool fChanged;
4811 if ((fChanged = (strOldValue != strValue)))
4812 {
4813 // ask for permission from all listeners outside the locks;
4814 // onExtraDataCanChange() only briefly requests the VirtualBox
4815 // lock to copy the list of callbacks to invoke
4816 Bstr error;
4817 Bstr bstrValue(aValue);
4818
4819 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
4820 {
4821 const char *sep = error.isEmpty() ? "" : ": ";
4822 CBSTR err = error.raw();
4823 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4824 sep, err));
4825 return setError(E_ACCESSDENIED,
4826 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
4827 aKey,
4828 bstrValue.raw(),
4829 sep,
4830 err);
4831 }
4832
4833 // data is changing and change not vetoed: then write it out under the lock
4834 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4835
4836 if (isSnapshotMachine())
4837 {
4838 HRESULT rc = checkStateDependency(MutableStateDep);
4839 if (FAILED(rc)) return rc;
4840 }
4841
4842 if (strValue.isEmpty())
4843 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
4844 else
4845 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
4846 // creates a new key if needed
4847
4848 bool fNeedsGlobalSaveSettings = false;
4849 saveSettings(&fNeedsGlobalSaveSettings);
4850
4851 if (fNeedsGlobalSaveSettings)
4852 {
4853 // save the global settings; for that we should hold only the VirtualBox lock
4854 alock.release();
4855 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4856 mParent->saveSettings();
4857 }
4858 }
4859
4860 // fire notification outside the lock
4861 if (fChanged)
4862 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
4863
4864 return S_OK;
4865}
4866
4867STDMETHODIMP Machine::SaveSettings()
4868{
4869 AutoCaller autoCaller(this);
4870 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4871
4872 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4873
4874 /* when there was auto-conversion, we want to save the file even if
4875 * the VM is saved */
4876 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
4877 if (FAILED(rc)) return rc;
4878
4879 /* the settings file path may never be null */
4880 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4881
4882 /* save all VM data excluding snapshots */
4883 bool fNeedsGlobalSaveSettings = false;
4884 rc = saveSettings(&fNeedsGlobalSaveSettings);
4885 mlock.release();
4886
4887 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4888 {
4889 // save the global settings; for that we should hold only the VirtualBox lock
4890 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4891 rc = mParent->saveSettings();
4892 }
4893
4894 return rc;
4895}
4896
4897STDMETHODIMP Machine::DiscardSettings()
4898{
4899 AutoCaller autoCaller(this);
4900 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4901
4902 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4903
4904 HRESULT rc = checkStateDependency(MutableStateDep);
4905 if (FAILED(rc)) return rc;
4906
4907 /*
4908 * during this rollback, the session will be notified if data has
4909 * been actually changed
4910 */
4911 rollback(true /* aNotify */);
4912
4913 return S_OK;
4914}
4915
4916/** @note Locks objects! */
4917STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
4918 ComSafeArrayOut(IMedium*, aMedia))
4919{
4920 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
4921 AutoLimitedCaller autoCaller(this);
4922 AssertComRCReturnRC(autoCaller.rc());
4923
4924 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4925
4926 Guid id(getId());
4927
4928 if (mData->mSession.mState != SessionState_Unlocked)
4929 return setError(VBOX_E_INVALID_OBJECT_STATE,
4930 tr("Cannot unregister the machine '%s' while it is locked"),
4931 mUserData->s.strName.c_str());
4932
4933 // wait for state dependents to drop to zero
4934 ensureNoStateDependencies();
4935
4936 if (!mData->mAccessible)
4937 {
4938 // inaccessible maschines can only be unregistered; uninitialize ourselves
4939 // here because currently there may be no unregistered that are inaccessible
4940 // (this state combination is not supported). Note releasing the caller and
4941 // leaving the lock before calling uninit()
4942 alock.release();
4943 autoCaller.release();
4944
4945 uninit();
4946
4947 mParent->unregisterMachine(this, id);
4948 // calls VirtualBox::saveSettings()
4949
4950 return S_OK;
4951 }
4952
4953 HRESULT rc = S_OK;
4954
4955 // discard saved state
4956 if (mData->mMachineState == MachineState_Saved)
4957 {
4958 // add the saved state file to the list of files the caller should delete
4959 Assert(!mSSData->strStateFilePath.isEmpty());
4960 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4961
4962 mSSData->strStateFilePath.setNull();
4963
4964 // unconditionally set the machine state to powered off, we now
4965 // know no session has locked the machine
4966 mData->mMachineState = MachineState_PoweredOff;
4967 }
4968
4969 size_t cSnapshots = 0;
4970 if (mData->mFirstSnapshot)
4971 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
4972 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
4973 // fail now before we start detaching media
4974 return setError(VBOX_E_INVALID_OBJECT_STATE,
4975 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
4976 mUserData->s.strName.c_str(), cSnapshots);
4977
4978 // This list collects the medium objects from all medium attachments
4979 // which we will detach from the machine and its snapshots, in a specific
4980 // order which allows for closing all media without getting "media in use"
4981 // errors, simply by going through the list from the front to the back:
4982 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4983 // and must be closed before the parent media from the snapshots, or closing the parents
4984 // will fail because they still have children);
4985 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4986 // the root ("first") snapshot of the machine.
4987 MediaList llMedia;
4988
4989 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
4990 && mMediaData->mAttachments.size()
4991 )
4992 {
4993 // we have media attachments: detach them all and add the Medium objects to our list
4994 if (cleanupMode != CleanupMode_UnregisterOnly)
4995 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
4996 else
4997 return setError(VBOX_E_INVALID_OBJECT_STATE,
4998 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
4999 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5000 }
5001
5002 if (cSnapshots)
5003 {
5004 // autoCleanup must be true here, or we would have failed above
5005
5006 // add the media from the medium attachments of the snapshots to llMedia
5007 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5008 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5009 // into the children first
5010
5011 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5012 MachineState_T oldState = mData->mMachineState;
5013 mData->mMachineState = MachineState_DeletingSnapshot;
5014
5015 // make a copy of the first snapshot so the refcount does not drop to 0
5016 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5017 // because of the AutoCaller voodoo)
5018 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5019
5020 // GO!
5021 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5022
5023 mData->mMachineState = oldState;
5024 }
5025
5026 if (FAILED(rc))
5027 {
5028 rollbackMedia();
5029 return rc;
5030 }
5031
5032 // commit all the media changes made above
5033 commitMedia();
5034
5035 mData->mRegistered = false;
5036
5037 // machine lock no longer needed
5038 alock.release();
5039
5040 // return media to caller
5041 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5042 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5043
5044 mParent->unregisterMachine(this, id);
5045 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5046
5047 return S_OK;
5048}
5049
5050struct Machine::DeleteTask
5051{
5052 ComObjPtr<Machine> pMachine;
5053 RTCList<ComPtr<IMedium> > llMediums;
5054 StringsList llFilesToDelete;
5055 ComObjPtr<Progress> pProgress;
5056};
5057
5058STDMETHODIMP Machine::Delete(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5059{
5060 LogFlowFuncEnter();
5061
5062 AutoCaller autoCaller(this);
5063 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5064
5065 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5066
5067 HRESULT rc = checkStateDependency(MutableStateDep);
5068 if (FAILED(rc)) return rc;
5069
5070 if (mData->mRegistered)
5071 return setError(VBOX_E_INVALID_VM_STATE,
5072 tr("Cannot delete settings of a registered machine"));
5073
5074 DeleteTask *pTask = new DeleteTask;
5075 pTask->pMachine = this;
5076 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5077
5078 // collect files to delete
5079 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5080
5081 for (size_t i = 0; i < sfaMedia.size(); ++i)
5082 {
5083 IMedium *pIMedium(sfaMedia[i]);
5084 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5085 if (pMedium.isNull())
5086 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5087 SafeArray<BSTR> ids;
5088 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5089 if (FAILED(rc)) return rc;
5090 /* At this point the medium should not have any back references
5091 * anymore. If it has it is attached to another VM and *must* not
5092 * deleted. */
5093 if (ids.size() < 1)
5094 pTask->llMediums.append(pMedium);
5095 }
5096 if (mData->pMachineConfigFile->fileExists())
5097 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5098
5099 pTask->pProgress.createObject();
5100 pTask->pProgress->init(getVirtualBox(),
5101 static_cast<IMachine*>(this) /* aInitiator */,
5102 Bstr(tr("Deleting files")).raw(),
5103 true /* fCancellable */,
5104 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5105 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5106
5107 int vrc = RTThreadCreate(NULL,
5108 Machine::deleteThread,
5109 (void*)pTask,
5110 0,
5111 RTTHREADTYPE_MAIN_WORKER,
5112 0,
5113 "MachineDelete");
5114
5115 pTask->pProgress.queryInterfaceTo(aProgress);
5116
5117 if (RT_FAILURE(vrc))
5118 {
5119 delete pTask;
5120 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5121 }
5122
5123 LogFlowFuncLeave();
5124
5125 return S_OK;
5126}
5127
5128/**
5129 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5130 * calls Machine::deleteTaskWorker() on the actual machine object.
5131 * @param Thread
5132 * @param pvUser
5133 * @return
5134 */
5135/*static*/
5136DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5137{
5138 LogFlowFuncEnter();
5139
5140 DeleteTask *pTask = (DeleteTask*)pvUser;
5141 Assert(pTask);
5142 Assert(pTask->pMachine);
5143 Assert(pTask->pProgress);
5144
5145 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5146 pTask->pProgress->notifyComplete(rc);
5147
5148 delete pTask;
5149
5150 LogFlowFuncLeave();
5151
5152 NOREF(Thread);
5153
5154 return VINF_SUCCESS;
5155}
5156
5157/**
5158 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5159 * @param task
5160 * @return
5161 */
5162HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5163{
5164 AutoCaller autoCaller(this);
5165 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5166
5167 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5168
5169 HRESULT rc = S_OK;
5170
5171 try
5172 {
5173 ULONG uLogHistoryCount = 3;
5174 ComPtr<ISystemProperties> systemProperties;
5175 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5176 if (FAILED(rc)) throw rc;
5177
5178 if (!systemProperties.isNull())
5179 {
5180 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5181 if (FAILED(rc)) throw rc;
5182 }
5183
5184 MachineState_T oldState = mData->mMachineState;
5185 setMachineState(MachineState_SettingUp);
5186 alock.release();
5187 for (size_t i = 0; i < task.llMediums.size(); ++i)
5188 {
5189 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5190 {
5191 AutoCaller mac(pMedium);
5192 if (FAILED(mac.rc())) throw mac.rc();
5193 Utf8Str strLocation = pMedium->getLocationFull();
5194 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5195 if (FAILED(rc)) throw rc;
5196 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5197 }
5198 ComPtr<IProgress> pProgress2;
5199 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5200 if (FAILED(rc)) throw rc;
5201 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5202 if (FAILED(rc)) throw rc;
5203 /* Check the result of the asynchrony process. */
5204 LONG iRc;
5205 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5206 if (FAILED(rc)) throw rc;
5207 /* If the thread of the progress object has an error, then
5208 * retrieve the error info from there, or it'll be lost. */
5209 if (FAILED(iRc))
5210 throw setError(ProgressErrorInfo(pProgress2));
5211 }
5212 setMachineState(oldState);
5213 alock.acquire();
5214
5215 // delete the files pushed on the task list by Machine::Delete()
5216 // (this includes saved states of the machine and snapshots and
5217 // medium storage files from the IMedium list passed in, and the
5218 // machine XML file)
5219 StringsList::const_iterator it = task.llFilesToDelete.begin();
5220 while (it != task.llFilesToDelete.end())
5221 {
5222 const Utf8Str &strFile = *it;
5223 LogFunc(("Deleting file %s\n", strFile.c_str()));
5224 int vrc = RTFileDelete(strFile.c_str());
5225 if (RT_FAILURE(vrc))
5226 throw setError(VBOX_E_IPRT_ERROR,
5227 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5228
5229 ++it;
5230 if (it == task.llFilesToDelete.end())
5231 {
5232 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5233 if (FAILED(rc)) throw rc;
5234 break;
5235 }
5236
5237 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5238 if (FAILED(rc)) throw rc;
5239 }
5240
5241 /* delete the settings only when the file actually exists */
5242 if (mData->pMachineConfigFile->fileExists())
5243 {
5244 /* Delete any backup or uncommitted XML files. Ignore failures.
5245 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5246 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5247 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5248 RTFileDelete(otherXml.c_str());
5249 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5250 RTFileDelete(otherXml.c_str());
5251
5252 /* delete the Logs folder, nothing important should be left
5253 * there (we don't check for errors because the user might have
5254 * some private files there that we don't want to delete) */
5255 Utf8Str logFolder;
5256 getLogFolder(logFolder);
5257 Assert(logFolder.length());
5258 if (RTDirExists(logFolder.c_str()))
5259 {
5260 /* Delete all VBox.log[.N] files from the Logs folder
5261 * (this must be in sync with the rotation logic in
5262 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5263 * files that may have been created by the GUI. */
5264 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5265 logFolder.c_str(), RTPATH_DELIMITER);
5266 RTFileDelete(log.c_str());
5267 log = Utf8StrFmt("%s%cVBox.png",
5268 logFolder.c_str(), RTPATH_DELIMITER);
5269 RTFileDelete(log.c_str());
5270 for (int i = uLogHistoryCount; i > 0; i--)
5271 {
5272 log = Utf8StrFmt("%s%cVBox.log.%d",
5273 logFolder.c_str(), RTPATH_DELIMITER, i);
5274 RTFileDelete(log.c_str());
5275 log = Utf8StrFmt("%s%cVBox.png.%d",
5276 logFolder.c_str(), RTPATH_DELIMITER, i);
5277 RTFileDelete(log.c_str());
5278 }
5279
5280 RTDirRemove(logFolder.c_str());
5281 }
5282
5283 /* delete the Snapshots folder, nothing important should be left
5284 * there (we don't check for errors because the user might have
5285 * some private files there that we don't want to delete) */
5286 Utf8Str strFullSnapshotFolder;
5287 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5288 Assert(!strFullSnapshotFolder.isEmpty());
5289 if (RTDirExists(strFullSnapshotFolder.c_str()))
5290 RTDirRemove(strFullSnapshotFolder.c_str());
5291
5292 // delete the directory that contains the settings file, but only
5293 // if it matches the VM name
5294 Utf8Str settingsDir;
5295 if (isInOwnDir(&settingsDir))
5296 RTDirRemove(settingsDir.c_str());
5297 }
5298
5299 alock.release();
5300
5301 mParent->saveModifiedRegistries();
5302 }
5303 catch (HRESULT aRC) { rc = aRC; }
5304
5305 return rc;
5306}
5307
5308STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5309{
5310 CheckComArgOutPointerValid(aSnapshot);
5311
5312 AutoCaller autoCaller(this);
5313 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5314
5315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5316
5317 ComObjPtr<Snapshot> pSnapshot;
5318 HRESULT rc;
5319
5320 if (!aNameOrId || !*aNameOrId)
5321 // null case (caller wants root snapshot): findSnapshotById() handles this
5322 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5323 else
5324 {
5325 Guid uuid(aNameOrId);
5326 if (uuid.isValid())
5327 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5328 else
5329 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5330 }
5331 pSnapshot.queryInterfaceTo(aSnapshot);
5332
5333 return rc;
5334}
5335
5336STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5337{
5338 CheckComArgStrNotEmptyOrNull(aName);
5339 CheckComArgStrNotEmptyOrNull(aHostPath);
5340
5341 AutoCaller autoCaller(this);
5342 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5343
5344 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5345
5346 HRESULT rc = checkStateDependency(MutableStateDep);
5347 if (FAILED(rc)) return rc;
5348
5349 Utf8Str strName(aName);
5350
5351 ComObjPtr<SharedFolder> sharedFolder;
5352 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5353 if (SUCCEEDED(rc))
5354 return setError(VBOX_E_OBJECT_IN_USE,
5355 tr("Shared folder named '%s' already exists"),
5356 strName.c_str());
5357
5358 sharedFolder.createObject();
5359 rc = sharedFolder->init(getMachine(),
5360 strName,
5361 aHostPath,
5362 !!aWritable,
5363 !!aAutoMount,
5364 true /* fFailOnError */);
5365 if (FAILED(rc)) return rc;
5366
5367 setModified(IsModified_SharedFolders);
5368 mHWData.backup();
5369 mHWData->mSharedFolders.push_back(sharedFolder);
5370
5371 /* inform the direct session if any */
5372 alock.release();
5373 onSharedFolderChange();
5374
5375 return S_OK;
5376}
5377
5378STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5379{
5380 CheckComArgStrNotEmptyOrNull(aName);
5381
5382 AutoCaller autoCaller(this);
5383 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5384
5385 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5386
5387 HRESULT rc = checkStateDependency(MutableStateDep);
5388 if (FAILED(rc)) return rc;
5389
5390 ComObjPtr<SharedFolder> sharedFolder;
5391 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5392 if (FAILED(rc)) return rc;
5393
5394 setModified(IsModified_SharedFolders);
5395 mHWData.backup();
5396 mHWData->mSharedFolders.remove(sharedFolder);
5397
5398 /* inform the direct session if any */
5399 alock.release();
5400 onSharedFolderChange();
5401
5402 return S_OK;
5403}
5404
5405STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5406{
5407 CheckComArgOutPointerValid(aCanShow);
5408
5409 /* start with No */
5410 *aCanShow = FALSE;
5411
5412 AutoCaller autoCaller(this);
5413 AssertComRCReturnRC(autoCaller.rc());
5414
5415 ComPtr<IInternalSessionControl> directControl;
5416 {
5417 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5418
5419 if (mData->mSession.mState != SessionState_Locked)
5420 return setError(VBOX_E_INVALID_VM_STATE,
5421 tr("Machine is not locked for session (session state: %s)"),
5422 Global::stringifySessionState(mData->mSession.mState));
5423
5424 directControl = mData->mSession.mDirectControl;
5425 }
5426
5427 /* ignore calls made after #OnSessionEnd() is called */
5428 if (!directControl)
5429 return S_OK;
5430
5431 LONG64 dummy;
5432 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5433}
5434
5435STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5436{
5437 CheckComArgOutPointerValid(aWinId);
5438
5439 AutoCaller autoCaller(this);
5440 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5441
5442 ComPtr<IInternalSessionControl> directControl;
5443 {
5444 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5445
5446 if (mData->mSession.mState != SessionState_Locked)
5447 return setError(E_FAIL,
5448 tr("Machine is not locked for session (session state: %s)"),
5449 Global::stringifySessionState(mData->mSession.mState));
5450
5451 directControl = mData->mSession.mDirectControl;
5452 }
5453
5454 /* ignore calls made after #OnSessionEnd() is called */
5455 if (!directControl)
5456 return S_OK;
5457
5458 BOOL dummy;
5459 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5460}
5461
5462#ifdef VBOX_WITH_GUEST_PROPS
5463/**
5464 * Look up a guest property in VBoxSVC's internal structures.
5465 */
5466HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5467 BSTR *aValue,
5468 LONG64 *aTimestamp,
5469 BSTR *aFlags) const
5470{
5471 using namespace guestProp;
5472
5473 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5474 Utf8Str strName(aName);
5475 HWData::GuestPropertyMap::const_iterator it =
5476 mHWData->mGuestProperties.find(strName);
5477
5478 if (it != mHWData->mGuestProperties.end())
5479 {
5480 char szFlags[MAX_FLAGS_LEN + 1];
5481 it->second.strValue.cloneTo(aValue);
5482 *aTimestamp = it->second.mTimestamp;
5483 writeFlags(it->second.mFlags, szFlags);
5484 Bstr(szFlags).cloneTo(aFlags);
5485 }
5486
5487 return S_OK;
5488}
5489
5490/**
5491 * Query the VM that a guest property belongs to for the property.
5492 * @returns E_ACCESSDENIED if the VM process is not available or not
5493 * currently handling queries and the lookup should then be done in
5494 * VBoxSVC.
5495 */
5496HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5497 BSTR *aValue,
5498 LONG64 *aTimestamp,
5499 BSTR *aFlags) const
5500{
5501 HRESULT rc;
5502 ComPtr<IInternalSessionControl> directControl;
5503 directControl = mData->mSession.mDirectControl;
5504
5505 /* fail if we were called after #OnSessionEnd() is called. This is a
5506 * silly race condition. */
5507
5508 if (!directControl)
5509 rc = E_ACCESSDENIED;
5510 else
5511 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5512 false /* isSetter */,
5513 aValue, aTimestamp, aFlags);
5514 return rc;
5515}
5516#endif // VBOX_WITH_GUEST_PROPS
5517
5518STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5519 BSTR *aValue,
5520 LONG64 *aTimestamp,
5521 BSTR *aFlags)
5522{
5523#ifndef VBOX_WITH_GUEST_PROPS
5524 ReturnComNotImplemented();
5525#else // VBOX_WITH_GUEST_PROPS
5526 CheckComArgStrNotEmptyOrNull(aName);
5527 CheckComArgOutPointerValid(aValue);
5528 CheckComArgOutPointerValid(aTimestamp);
5529 CheckComArgOutPointerValid(aFlags);
5530
5531 AutoCaller autoCaller(this);
5532 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5533
5534 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5535 if (rc == E_ACCESSDENIED)
5536 /* The VM is not running or the service is not (yet) accessible */
5537 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5538 return rc;
5539#endif // VBOX_WITH_GUEST_PROPS
5540}
5541
5542STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5543{
5544 LONG64 dummyTimestamp;
5545 Bstr dummyFlags;
5546 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5547}
5548
5549STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5550{
5551 Bstr dummyValue;
5552 Bstr dummyFlags;
5553 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5554}
5555
5556#ifdef VBOX_WITH_GUEST_PROPS
5557/**
5558 * Set a guest property in VBoxSVC's internal structures.
5559 */
5560HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5561 IN_BSTR aFlags)
5562{
5563 using namespace guestProp;
5564
5565 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5566 HRESULT rc = S_OK;
5567 HWData::GuestProperty property;
5568 property.mFlags = NILFLAG;
5569
5570 rc = checkStateDependency(MutableStateDep);
5571 if (FAILED(rc)) return rc;
5572
5573 try
5574 {
5575 Utf8Str utf8Name(aName);
5576 Utf8Str utf8Flags(aFlags);
5577 uint32_t fFlags = NILFLAG;
5578 if ( (aFlags != NULL)
5579 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags))
5580 )
5581 return setError(E_INVALIDARG,
5582 tr("Invalid guest property flag values: '%ls'"),
5583 aFlags);
5584
5585 HWData::GuestPropertyMap::iterator it =
5586 mHWData->mGuestProperties.find(utf8Name);
5587
5588 if (it == mHWData->mGuestProperties.end())
5589 {
5590 setModified(IsModified_MachineData);
5591 mHWData.backupEx();
5592
5593 RTTIMESPEC time;
5594 HWData::GuestProperty prop;
5595 prop.strValue = aValue;
5596 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5597 prop.mFlags = fFlags;
5598
5599 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
5600 }
5601 else
5602 {
5603 if (it->second.mFlags & (RDONLYHOST))
5604 {
5605 rc = setError(E_ACCESSDENIED,
5606 tr("The property '%ls' cannot be changed by the host"),
5607 aName);
5608 }
5609 else
5610 {
5611 setModified(IsModified_MachineData);
5612 mHWData.backupEx();
5613
5614 /* The backupEx() operation invalidates our iterator,
5615 * so get a new one. */
5616 it = mHWData->mGuestProperties.find(utf8Name);
5617 Assert(it != mHWData->mGuestProperties.end());
5618
5619 if (RT_VALID_PTR(aValue) && *(aValue) != '\0')
5620 {
5621 RTTIMESPEC time;
5622 it->second.strValue = aValue;
5623 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5624 if (aFlags != NULL)
5625 it->second.mFlags = fFlags;
5626 }
5627 else
5628 {
5629 mHWData->mGuestProperties.erase(it);
5630 }
5631 }
5632 }
5633
5634 if ( SUCCEEDED(rc)
5635 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5636 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5637 RTSTR_MAX,
5638 utf8Name.c_str(),
5639 RTSTR_MAX,
5640 NULL)
5641 )
5642 )
5643 {
5644 alock.release();
5645
5646 mParent->onGuestPropertyChange(mData->mUuid, aName,
5647 aValue ? aValue : Bstr("").raw(),
5648 aFlags ? aFlags : Bstr("").raw());
5649 }
5650 }
5651 catch (std::bad_alloc &)
5652 {
5653 rc = E_OUTOFMEMORY;
5654 }
5655
5656 return rc;
5657}
5658
5659/**
5660 * Set a property on the VM that that property belongs to.
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::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
5666 IN_BSTR aFlags)
5667{
5668 HRESULT rc;
5669
5670 try
5671 {
5672 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5673
5674 BSTR dummy = NULL; /* will not be changed (setter) */
5675 LONG64 dummy64;
5676 if (!directControl)
5677 rc = E_ACCESSDENIED;
5678 else
5679 /** @todo Fix when adding DeleteGuestProperty(),
5680 see defect. */
5681 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
5682 true /* isSetter */,
5683 &dummy, &dummy64, &dummy);
5684 }
5685 catch (std::bad_alloc &)
5686 {
5687 rc = E_OUTOFMEMORY;
5688 }
5689
5690 return rc;
5691}
5692#endif // VBOX_WITH_GUEST_PROPS
5693
5694STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
5695 IN_BSTR aFlags)
5696{
5697#ifndef VBOX_WITH_GUEST_PROPS
5698 ReturnComNotImplemented();
5699#else // VBOX_WITH_GUEST_PROPS
5700 CheckComArgStrNotEmptyOrNull(aName);
5701 CheckComArgMaybeNull(aFlags);
5702 CheckComArgMaybeNull(aValue);
5703
5704 AutoCaller autoCaller(this);
5705 if (FAILED(autoCaller.rc()))
5706 return autoCaller.rc();
5707
5708 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
5709 if (rc == E_ACCESSDENIED)
5710 /* The VM is not running or the service is not (yet) accessible */
5711 rc = setGuestPropertyToService(aName, aValue, aFlags);
5712 return rc;
5713#endif // VBOX_WITH_GUEST_PROPS
5714}
5715
5716STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
5717{
5718 return SetGuestProperty(aName, aValue, NULL);
5719}
5720
5721STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
5722{
5723 return SetGuestProperty(aName, NULL, NULL);
5724}
5725
5726#ifdef VBOX_WITH_GUEST_PROPS
5727/**
5728 * Enumerate the guest properties in VBoxSVC's internal structures.
5729 */
5730HRESULT Machine::enumerateGuestPropertiesInService
5731 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5732 ComSafeArrayOut(BSTR, aValues),
5733 ComSafeArrayOut(LONG64, aTimestamps),
5734 ComSafeArrayOut(BSTR, aFlags))
5735{
5736 using namespace guestProp;
5737
5738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5739 Utf8Str strPatterns(aPatterns);
5740
5741 HWData::GuestPropertyMap propMap;
5742
5743 /*
5744 * Look for matching patterns and build up a list.
5745 */
5746 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5747 while (it != mHWData->mGuestProperties.end())
5748 {
5749 if ( strPatterns.isEmpty()
5750 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5751 RTSTR_MAX,
5752 it->first.c_str(),
5753 RTSTR_MAX,
5754 NULL)
5755 )
5756 {
5757 propMap.insert(*it);
5758 }
5759
5760 it++;
5761 }
5762
5763 alock.release();
5764
5765 /*
5766 * And build up the arrays for returning the property information.
5767 */
5768 size_t cEntries = propMap.size();
5769 SafeArray<BSTR> names(cEntries);
5770 SafeArray<BSTR> values(cEntries);
5771 SafeArray<LONG64> timestamps(cEntries);
5772 SafeArray<BSTR> flags(cEntries);
5773 size_t iProp = 0;
5774
5775 it = propMap.begin();
5776 while (it != propMap.end())
5777 {
5778 char szFlags[MAX_FLAGS_LEN + 1];
5779 it->first.cloneTo(&names[iProp]);
5780 it->second.strValue.cloneTo(&values[iProp]);
5781 timestamps[iProp] = it->second.mTimestamp;
5782 writeFlags(it->second.mFlags, szFlags);
5783 Bstr(szFlags).cloneTo(&flags[iProp++]);
5784 it++;
5785 }
5786 names.detachTo(ComSafeArrayOutArg(aNames));
5787 values.detachTo(ComSafeArrayOutArg(aValues));
5788 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
5789 flags.detachTo(ComSafeArrayOutArg(aFlags));
5790 return S_OK;
5791}
5792
5793/**
5794 * Enumerate the properties managed by a VM.
5795 * @returns E_ACCESSDENIED if the VM process is not available or not
5796 * currently handling queries and the setting should then be done in
5797 * VBoxSVC.
5798 */
5799HRESULT Machine::enumerateGuestPropertiesOnVM
5800 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5801 ComSafeArrayOut(BSTR, aValues),
5802 ComSafeArrayOut(LONG64, aTimestamps),
5803 ComSafeArrayOut(BSTR, aFlags))
5804{
5805 HRESULT rc;
5806 ComPtr<IInternalSessionControl> directControl;
5807 directControl = mData->mSession.mDirectControl;
5808
5809 if (!directControl)
5810 rc = E_ACCESSDENIED;
5811 else
5812 rc = directControl->EnumerateGuestProperties
5813 (aPatterns, ComSafeArrayOutArg(aNames),
5814 ComSafeArrayOutArg(aValues),
5815 ComSafeArrayOutArg(aTimestamps),
5816 ComSafeArrayOutArg(aFlags));
5817 return rc;
5818}
5819#endif // VBOX_WITH_GUEST_PROPS
5820
5821STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
5822 ComSafeArrayOut(BSTR, aNames),
5823 ComSafeArrayOut(BSTR, aValues),
5824 ComSafeArrayOut(LONG64, aTimestamps),
5825 ComSafeArrayOut(BSTR, aFlags))
5826{
5827#ifndef VBOX_WITH_GUEST_PROPS
5828 ReturnComNotImplemented();
5829#else // VBOX_WITH_GUEST_PROPS
5830 CheckComArgMaybeNull(aPatterns);
5831 CheckComArgOutSafeArrayPointerValid(aNames);
5832 CheckComArgOutSafeArrayPointerValid(aValues);
5833 CheckComArgOutSafeArrayPointerValid(aTimestamps);
5834 CheckComArgOutSafeArrayPointerValid(aFlags);
5835
5836 AutoCaller autoCaller(this);
5837 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5838
5839 HRESULT rc = enumerateGuestPropertiesOnVM
5840 (aPatterns, ComSafeArrayOutArg(aNames),
5841 ComSafeArrayOutArg(aValues),
5842 ComSafeArrayOutArg(aTimestamps),
5843 ComSafeArrayOutArg(aFlags));
5844 if (rc == E_ACCESSDENIED)
5845 /* The VM is not running or the service is not (yet) accessible */
5846 rc = enumerateGuestPropertiesInService
5847 (aPatterns, ComSafeArrayOutArg(aNames),
5848 ComSafeArrayOutArg(aValues),
5849 ComSafeArrayOutArg(aTimestamps),
5850 ComSafeArrayOutArg(aFlags));
5851 return rc;
5852#endif // VBOX_WITH_GUEST_PROPS
5853}
5854
5855STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
5856 ComSafeArrayOut(IMediumAttachment*, aAttachments))
5857{
5858 MediaData::AttachmentList atts;
5859
5860 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
5861 if (FAILED(rc)) return rc;
5862
5863 SafeIfaceArray<IMediumAttachment> attachments(atts);
5864 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
5865
5866 return S_OK;
5867}
5868
5869STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
5870 LONG aControllerPort,
5871 LONG aDevice,
5872 IMediumAttachment **aAttachment)
5873{
5874 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5875 aControllerName, aControllerPort, aDevice));
5876
5877 CheckComArgStrNotEmptyOrNull(aControllerName);
5878 CheckComArgOutPointerValid(aAttachment);
5879
5880 AutoCaller autoCaller(this);
5881 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5882
5883 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5884
5885 *aAttachment = NULL;
5886
5887 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5888 aControllerName,
5889 aControllerPort,
5890 aDevice);
5891 if (pAttach.isNull())
5892 return setError(VBOX_E_OBJECT_NOT_FOUND,
5893 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5894 aDevice, aControllerPort, aControllerName);
5895
5896 pAttach.queryInterfaceTo(aAttachment);
5897
5898 return S_OK;
5899}
5900
5901STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
5902 StorageBus_T aConnectionType,
5903 IStorageController **controller)
5904{
5905 CheckComArgStrNotEmptyOrNull(aName);
5906
5907 if ( (aConnectionType <= StorageBus_Null)
5908 || (aConnectionType > StorageBus_SAS))
5909 return setError(E_INVALIDARG,
5910 tr("Invalid connection type: %d"),
5911 aConnectionType);
5912
5913 AutoCaller autoCaller(this);
5914 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5915
5916 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5917
5918 HRESULT rc = checkStateDependency(MutableStateDep);
5919 if (FAILED(rc)) return rc;
5920
5921 /* try to find one with the name first. */
5922 ComObjPtr<StorageController> ctrl;
5923
5924 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
5925 if (SUCCEEDED(rc))
5926 return setError(VBOX_E_OBJECT_IN_USE,
5927 tr("Storage controller named '%ls' already exists"),
5928 aName);
5929
5930 ctrl.createObject();
5931
5932 /* get a new instance number for the storage controller */
5933 ULONG ulInstance = 0;
5934 bool fBootable = true;
5935 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5936 it != mStorageControllers->end();
5937 ++it)
5938 {
5939 if ((*it)->getStorageBus() == aConnectionType)
5940 {
5941 ULONG ulCurInst = (*it)->getInstance();
5942
5943 if (ulCurInst >= ulInstance)
5944 ulInstance = ulCurInst + 1;
5945
5946 /* Only one controller of each type can be marked as bootable. */
5947 if ((*it)->getBootable())
5948 fBootable = false;
5949 }
5950 }
5951
5952 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5953 if (FAILED(rc)) return rc;
5954
5955 setModified(IsModified_Storage);
5956 mStorageControllers.backup();
5957 mStorageControllers->push_back(ctrl);
5958
5959 ctrl.queryInterfaceTo(controller);
5960
5961 /* inform the direct session if any */
5962 alock.release();
5963 onStorageControllerChange();
5964
5965 return S_OK;
5966}
5967
5968STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
5969 IStorageController **aStorageController)
5970{
5971 CheckComArgStrNotEmptyOrNull(aName);
5972
5973 AutoCaller autoCaller(this);
5974 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5975
5976 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5977
5978 ComObjPtr<StorageController> ctrl;
5979
5980 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5981 if (SUCCEEDED(rc))
5982 ctrl.queryInterfaceTo(aStorageController);
5983
5984 return rc;
5985}
5986
5987STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
5988 IStorageController **aStorageController)
5989{
5990 AutoCaller autoCaller(this);
5991 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5992
5993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5994
5995 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5996 it != mStorageControllers->end();
5997 ++it)
5998 {
5999 if ((*it)->getInstance() == aInstance)
6000 {
6001 (*it).queryInterfaceTo(aStorageController);
6002 return S_OK;
6003 }
6004 }
6005
6006 return setError(VBOX_E_OBJECT_NOT_FOUND,
6007 tr("Could not find a storage controller with instance number '%lu'"),
6008 aInstance);
6009}
6010
6011STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6012{
6013 AutoCaller autoCaller(this);
6014 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6015
6016 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6017
6018 HRESULT rc = checkStateDependency(MutableStateDep);
6019 if (FAILED(rc)) return rc;
6020
6021 ComObjPtr<StorageController> ctrl;
6022
6023 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6024 if (SUCCEEDED(rc))
6025 {
6026 /* Ensure that only one controller of each type is marked as bootable. */
6027 if (fBootable == TRUE)
6028 {
6029 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6030 it != mStorageControllers->end();
6031 ++it)
6032 {
6033 ComObjPtr<StorageController> aCtrl = (*it);
6034
6035 if ( (aCtrl->getName() != Utf8Str(aName))
6036 && aCtrl->getBootable() == TRUE
6037 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6038 && aCtrl->getControllerType() == ctrl->getControllerType())
6039 {
6040 aCtrl->setBootable(FALSE);
6041 break;
6042 }
6043 }
6044 }
6045
6046 if (SUCCEEDED(rc))
6047 {
6048 ctrl->setBootable(fBootable);
6049 setModified(IsModified_Storage);
6050 }
6051 }
6052
6053 if (SUCCEEDED(rc))
6054 {
6055 /* inform the direct session if any */
6056 alock.release();
6057 onStorageControllerChange();
6058 }
6059
6060 return rc;
6061}
6062
6063STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6064{
6065 CheckComArgStrNotEmptyOrNull(aName);
6066
6067 AutoCaller autoCaller(this);
6068 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6069
6070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6071
6072 HRESULT rc = checkStateDependency(MutableStateDep);
6073 if (FAILED(rc)) return rc;
6074
6075 ComObjPtr<StorageController> ctrl;
6076 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6077 if (FAILED(rc)) return rc;
6078
6079 {
6080 /* find all attached devices to the appropriate storage controller and detach them all */
6081 // make a temporary list because detachDevice invalidates iterators into
6082 // mMediaData->mAttachments
6083 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6084
6085 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6086 it != llAttachments2.end();
6087 ++it)
6088 {
6089 MediumAttachment *pAttachTemp = *it;
6090
6091 AutoCaller localAutoCaller(pAttachTemp);
6092 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6093
6094 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6095
6096 if (pAttachTemp->getControllerName() == aName)
6097 {
6098 rc = detachDevice(pAttachTemp, alock, NULL);
6099 if (FAILED(rc)) return rc;
6100 }
6101 }
6102 }
6103
6104 /* We can remove it now. */
6105 setModified(IsModified_Storage);
6106 mStorageControllers.backup();
6107
6108 ctrl->unshare();
6109
6110 mStorageControllers->remove(ctrl);
6111
6112 /* inform the direct session if any */
6113 alock.release();
6114 onStorageControllerChange();
6115
6116 return S_OK;
6117}
6118
6119STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6120 ULONG *puOriginX,
6121 ULONG *puOriginY,
6122 ULONG *puWidth,
6123 ULONG *puHeight,
6124 BOOL *pfEnabled)
6125{
6126 LogFlowThisFunc(("\n"));
6127
6128 CheckComArgNotNull(puOriginX);
6129 CheckComArgNotNull(puOriginY);
6130 CheckComArgNotNull(puWidth);
6131 CheckComArgNotNull(puHeight);
6132 CheckComArgNotNull(pfEnabled);
6133
6134 uint32_t u32OriginX= 0;
6135 uint32_t u32OriginY= 0;
6136 uint32_t u32Width = 0;
6137 uint32_t u32Height = 0;
6138 uint16_t u16Flags = 0;
6139
6140 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6141 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6142 if (RT_FAILURE(vrc))
6143 {
6144#ifdef RT_OS_WINDOWS
6145 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6146 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6147 * So just assign fEnable to TRUE again.
6148 * The right fix would be to change GUI API wrappers to make sure that parameters
6149 * are changed only if API succeeds.
6150 */
6151 *pfEnabled = TRUE;
6152#endif
6153 return setError(VBOX_E_IPRT_ERROR,
6154 tr("Saved guest size is not available (%Rrc)"),
6155 vrc);
6156 }
6157
6158 *puOriginX = u32OriginX;
6159 *puOriginY = u32OriginY;
6160 *puWidth = u32Width;
6161 *puHeight = u32Height;
6162 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6163
6164 return S_OK;
6165}
6166
6167STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6168{
6169 LogFlowThisFunc(("\n"));
6170
6171 CheckComArgNotNull(aSize);
6172 CheckComArgNotNull(aWidth);
6173 CheckComArgNotNull(aHeight);
6174
6175 if (aScreenId != 0)
6176 return E_NOTIMPL;
6177
6178 AutoCaller autoCaller(this);
6179 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6180
6181 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6182
6183 uint8_t *pu8Data = NULL;
6184 uint32_t cbData = 0;
6185 uint32_t u32Width = 0;
6186 uint32_t u32Height = 0;
6187
6188 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6189
6190 if (RT_FAILURE(vrc))
6191 return setError(VBOX_E_IPRT_ERROR,
6192 tr("Saved screenshot data is not available (%Rrc)"),
6193 vrc);
6194
6195 *aSize = cbData;
6196 *aWidth = u32Width;
6197 *aHeight = u32Height;
6198
6199 freeSavedDisplayScreenshot(pu8Data);
6200
6201 return S_OK;
6202}
6203
6204STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6205{
6206 LogFlowThisFunc(("\n"));
6207
6208 CheckComArgNotNull(aWidth);
6209 CheckComArgNotNull(aHeight);
6210 CheckComArgOutSafeArrayPointerValid(aData);
6211
6212 if (aScreenId != 0)
6213 return E_NOTIMPL;
6214
6215 AutoCaller autoCaller(this);
6216 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6217
6218 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6219
6220 uint8_t *pu8Data = NULL;
6221 uint32_t cbData = 0;
6222 uint32_t u32Width = 0;
6223 uint32_t u32Height = 0;
6224
6225 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6226
6227 if (RT_FAILURE(vrc))
6228 return setError(VBOX_E_IPRT_ERROR,
6229 tr("Saved screenshot data is not available (%Rrc)"),
6230 vrc);
6231
6232 *aWidth = u32Width;
6233 *aHeight = u32Height;
6234
6235 com::SafeArray<BYTE> bitmap(cbData);
6236 /* Convert pixels to format expected by the API caller. */
6237 if (aBGR)
6238 {
6239 /* [0] B, [1] G, [2] R, [3] A. */
6240 for (unsigned i = 0; i < cbData; i += 4)
6241 {
6242 bitmap[i] = pu8Data[i];
6243 bitmap[i + 1] = pu8Data[i + 1];
6244 bitmap[i + 2] = pu8Data[i + 2];
6245 bitmap[i + 3] = 0xff;
6246 }
6247 }
6248 else
6249 {
6250 /* [0] R, [1] G, [2] B, [3] A. */
6251 for (unsigned i = 0; i < cbData; i += 4)
6252 {
6253 bitmap[i] = pu8Data[i + 2];
6254 bitmap[i + 1] = pu8Data[i + 1];
6255 bitmap[i + 2] = pu8Data[i];
6256 bitmap[i + 3] = 0xff;
6257 }
6258 }
6259 bitmap.detachTo(ComSafeArrayOutArg(aData));
6260
6261 freeSavedDisplayScreenshot(pu8Data);
6262
6263 return S_OK;
6264}
6265
6266
6267STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6268{
6269 LogFlowThisFunc(("\n"));
6270
6271 CheckComArgNotNull(aWidth);
6272 CheckComArgNotNull(aHeight);
6273 CheckComArgOutSafeArrayPointerValid(aData);
6274
6275 if (aScreenId != 0)
6276 return E_NOTIMPL;
6277
6278 AutoCaller autoCaller(this);
6279 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6280
6281 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6282
6283 uint8_t *pu8Data = NULL;
6284 uint32_t cbData = 0;
6285 uint32_t u32Width = 0;
6286 uint32_t u32Height = 0;
6287
6288 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6289
6290 if (RT_FAILURE(vrc))
6291 return setError(VBOX_E_IPRT_ERROR,
6292 tr("Saved screenshot data is not available (%Rrc)"),
6293 vrc);
6294
6295 *aWidth = u32Width;
6296 *aHeight = u32Height;
6297
6298 HRESULT rc = S_OK;
6299 uint8_t *pu8PNG = NULL;
6300 uint32_t cbPNG = 0;
6301 uint32_t cxPNG = 0;
6302 uint32_t cyPNG = 0;
6303
6304 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6305
6306 if (RT_SUCCESS(vrc))
6307 {
6308 com::SafeArray<BYTE> screenData(cbPNG);
6309 screenData.initFrom(pu8PNG, cbPNG);
6310 if (pu8PNG)
6311 RTMemFree(pu8PNG);
6312 screenData.detachTo(ComSafeArrayOutArg(aData));
6313 }
6314 else
6315 {
6316 if (pu8PNG)
6317 RTMemFree(pu8PNG);
6318 return setError(VBOX_E_IPRT_ERROR,
6319 tr("Could not convert screenshot to PNG (%Rrc)"),
6320 vrc);
6321 }
6322
6323 freeSavedDisplayScreenshot(pu8Data);
6324
6325 return rc;
6326}
6327
6328STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6329{
6330 LogFlowThisFunc(("\n"));
6331
6332 CheckComArgNotNull(aSize);
6333 CheckComArgNotNull(aWidth);
6334 CheckComArgNotNull(aHeight);
6335
6336 if (aScreenId != 0)
6337 return E_NOTIMPL;
6338
6339 AutoCaller autoCaller(this);
6340 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6341
6342 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6343
6344 uint8_t *pu8Data = NULL;
6345 uint32_t cbData = 0;
6346 uint32_t u32Width = 0;
6347 uint32_t u32Height = 0;
6348
6349 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6350
6351 if (RT_FAILURE(vrc))
6352 return setError(VBOX_E_IPRT_ERROR,
6353 tr("Saved screenshot data is not available (%Rrc)"),
6354 vrc);
6355
6356 *aSize = cbData;
6357 *aWidth = u32Width;
6358 *aHeight = u32Height;
6359
6360 freeSavedDisplayScreenshot(pu8Data);
6361
6362 return S_OK;
6363}
6364
6365STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6366{
6367 LogFlowThisFunc(("\n"));
6368
6369 CheckComArgNotNull(aWidth);
6370 CheckComArgNotNull(aHeight);
6371 CheckComArgOutSafeArrayPointerValid(aData);
6372
6373 if (aScreenId != 0)
6374 return E_NOTIMPL;
6375
6376 AutoCaller autoCaller(this);
6377 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6378
6379 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6380
6381 uint8_t *pu8Data = NULL;
6382 uint32_t cbData = 0;
6383 uint32_t u32Width = 0;
6384 uint32_t u32Height = 0;
6385
6386 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6387
6388 if (RT_FAILURE(vrc))
6389 return setError(VBOX_E_IPRT_ERROR,
6390 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6391 vrc);
6392
6393 *aWidth = u32Width;
6394 *aHeight = u32Height;
6395
6396 com::SafeArray<BYTE> png(cbData);
6397 png.initFrom(pu8Data, cbData);
6398 png.detachTo(ComSafeArrayOutArg(aData));
6399
6400 freeSavedDisplayScreenshot(pu8Data);
6401
6402 return S_OK;
6403}
6404
6405STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6406{
6407 HRESULT rc = S_OK;
6408 LogFlowThisFunc(("\n"));
6409
6410 AutoCaller autoCaller(this);
6411 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6412
6413 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6414
6415 if (!mHWData->mCPUHotPlugEnabled)
6416 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6417
6418 if (aCpu >= mHWData->mCPUCount)
6419 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6420
6421 if (mHWData->mCPUAttached[aCpu])
6422 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6423
6424 alock.release();
6425 rc = onCPUChange(aCpu, false);
6426 alock.acquire();
6427 if (FAILED(rc)) return rc;
6428
6429 setModified(IsModified_MachineData);
6430 mHWData.backup();
6431 mHWData->mCPUAttached[aCpu] = true;
6432
6433 /* Save settings if online */
6434 if (Global::IsOnline(mData->mMachineState))
6435 saveSettings(NULL);
6436
6437 return S_OK;
6438}
6439
6440STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6441{
6442 HRESULT rc = S_OK;
6443 LogFlowThisFunc(("\n"));
6444
6445 AutoCaller autoCaller(this);
6446 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6447
6448 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6449
6450 if (!mHWData->mCPUHotPlugEnabled)
6451 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6452
6453 if (aCpu >= SchemaDefs::MaxCPUCount)
6454 return setError(E_INVALIDARG,
6455 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6456 SchemaDefs::MaxCPUCount);
6457
6458 if (!mHWData->mCPUAttached[aCpu])
6459 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6460
6461 /* CPU 0 can't be detached */
6462 if (aCpu == 0)
6463 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6464
6465 alock.release();
6466 rc = onCPUChange(aCpu, true);
6467 alock.acquire();
6468 if (FAILED(rc)) return rc;
6469
6470 setModified(IsModified_MachineData);
6471 mHWData.backup();
6472 mHWData->mCPUAttached[aCpu] = false;
6473
6474 /* Save settings if online */
6475 if (Global::IsOnline(mData->mMachineState))
6476 saveSettings(NULL);
6477
6478 return S_OK;
6479}
6480
6481STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6482{
6483 LogFlowThisFunc(("\n"));
6484
6485 CheckComArgNotNull(aCpuAttached);
6486
6487 *aCpuAttached = false;
6488
6489 AutoCaller autoCaller(this);
6490 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6491
6492 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6493
6494 /* If hotplug is enabled the CPU is always enabled. */
6495 if (!mHWData->mCPUHotPlugEnabled)
6496 {
6497 if (aCpu < mHWData->mCPUCount)
6498 *aCpuAttached = true;
6499 }
6500 else
6501 {
6502 if (aCpu < SchemaDefs::MaxCPUCount)
6503 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6504 }
6505
6506 return S_OK;
6507}
6508
6509STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6510{
6511 CheckComArgOutPointerValid(aName);
6512
6513 AutoCaller autoCaller(this);
6514 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6515
6516 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6517
6518 Utf8Str log = queryLogFilename(aIdx);
6519 if (!RTFileExists(log.c_str()))
6520 log.setNull();
6521 log.cloneTo(aName);
6522
6523 return S_OK;
6524}
6525
6526STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6527{
6528 LogFlowThisFunc(("\n"));
6529 CheckComArgOutSafeArrayPointerValid(aData);
6530 if (aSize < 0)
6531 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6532
6533 AutoCaller autoCaller(this);
6534 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6535
6536 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6537
6538 HRESULT rc = S_OK;
6539 Utf8Str log = queryLogFilename(aIdx);
6540
6541 /* do not unnecessarily hold the lock while doing something which does
6542 * not need the lock and potentially takes a long time. */
6543 alock.release();
6544
6545 /* Limit the chunk size to 32K for now, as that gives better performance
6546 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6547 * One byte expands to approx. 25 bytes of breathtaking XML. */
6548 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6549 com::SafeArray<BYTE> logData(cbData);
6550
6551 RTFILE LogFile;
6552 int vrc = RTFileOpen(&LogFile, log.c_str(),
6553 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6554 if (RT_SUCCESS(vrc))
6555 {
6556 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6557 if (RT_SUCCESS(vrc))
6558 logData.resize(cbData);
6559 else
6560 rc = setError(VBOX_E_IPRT_ERROR,
6561 tr("Could not read log file '%s' (%Rrc)"),
6562 log.c_str(), vrc);
6563 RTFileClose(LogFile);
6564 }
6565 else
6566 rc = setError(VBOX_E_IPRT_ERROR,
6567 tr("Could not open log file '%s' (%Rrc)"),
6568 log.c_str(), vrc);
6569
6570 if (FAILED(rc))
6571 logData.resize(0);
6572 logData.detachTo(ComSafeArrayOutArg(aData));
6573
6574 return rc;
6575}
6576
6577
6578/**
6579 * Currently this method doesn't attach device to the running VM,
6580 * just makes sure it's plugged on next VM start.
6581 */
6582STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6583{
6584 AutoCaller autoCaller(this);
6585 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6586
6587 // lock scope
6588 {
6589 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6590
6591 HRESULT rc = checkStateDependency(MutableStateDep);
6592 if (FAILED(rc)) return rc;
6593
6594 ChipsetType_T aChipset = ChipsetType_PIIX3;
6595 COMGETTER(ChipsetType)(&aChipset);
6596
6597 if (aChipset != ChipsetType_ICH9)
6598 {
6599 return setError(E_INVALIDARG,
6600 tr("Host PCI attachment only supported with ICH9 chipset"));
6601 }
6602
6603 // check if device with this host PCI address already attached
6604 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6605 it != mHWData->mPCIDeviceAssignments.end();
6606 ++it)
6607 {
6608 LONG iHostAddress = -1;
6609 ComPtr<PCIDeviceAttachment> pAttach;
6610 pAttach = *it;
6611 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6612 if (iHostAddress == hostAddress)
6613 return setError(E_INVALIDARG,
6614 tr("Device with host PCI address already attached to this VM"));
6615 }
6616
6617 ComObjPtr<PCIDeviceAttachment> pda;
6618 char name[32];
6619
6620 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6621 Bstr bname(name);
6622 pda.createObject();
6623 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
6624 setModified(IsModified_MachineData);
6625 mHWData.backup();
6626 mHWData->mPCIDeviceAssignments.push_back(pda);
6627 }
6628
6629 return S_OK;
6630}
6631
6632/**
6633 * Currently this method doesn't detach device from the running VM,
6634 * just makes sure it's not plugged on next VM start.
6635 */
6636STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
6637{
6638 AutoCaller autoCaller(this);
6639 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6640
6641 ComObjPtr<PCIDeviceAttachment> pAttach;
6642 bool fRemoved = false;
6643 HRESULT rc;
6644
6645 // lock scope
6646 {
6647 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6648
6649 rc = checkStateDependency(MutableStateDep);
6650 if (FAILED(rc)) return rc;
6651
6652 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6653 it != mHWData->mPCIDeviceAssignments.end();
6654 ++it)
6655 {
6656 LONG iHostAddress = -1;
6657 pAttach = *it;
6658 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6659 if (iHostAddress != -1 && iHostAddress == hostAddress)
6660 {
6661 setModified(IsModified_MachineData);
6662 mHWData.backup();
6663 mHWData->mPCIDeviceAssignments.remove(pAttach);
6664 fRemoved = true;
6665 break;
6666 }
6667 }
6668 }
6669
6670
6671 /* Fire event outside of the lock */
6672 if (fRemoved)
6673 {
6674 Assert(!pAttach.isNull());
6675 ComPtr<IEventSource> es;
6676 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6677 Assert(SUCCEEDED(rc));
6678 Bstr mid;
6679 rc = this->COMGETTER(Id)(mid.asOutParam());
6680 Assert(SUCCEEDED(rc));
6681 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6682 }
6683
6684 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6685 tr("No host PCI device %08x attached"),
6686 hostAddress
6687 );
6688}
6689
6690STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
6691{
6692 CheckComArgOutSafeArrayPointerValid(aAssignments);
6693
6694 AutoCaller autoCaller(this);
6695 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6696
6697 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6698
6699 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
6700 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
6701
6702 return S_OK;
6703}
6704
6705STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
6706{
6707 CheckComArgOutPointerValid(aBandwidthControl);
6708
6709 AutoCaller autoCaller(this);
6710 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6711
6712 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
6713
6714 return S_OK;
6715}
6716
6717STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
6718{
6719 CheckComArgOutPointerValid(pfEnabled);
6720 AutoCaller autoCaller(this);
6721 HRESULT hrc = autoCaller.rc();
6722 if (SUCCEEDED(hrc))
6723 {
6724 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6725 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
6726 }
6727 return hrc;
6728}
6729
6730STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
6731{
6732 AutoCaller autoCaller(this);
6733 HRESULT hrc = autoCaller.rc();
6734 if (SUCCEEDED(hrc))
6735 {
6736 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6737 hrc = checkStateDependency(MutableStateDep);
6738 if (SUCCEEDED(hrc))
6739 {
6740 hrc = mHWData.backupEx();
6741 if (SUCCEEDED(hrc))
6742 {
6743 setModified(IsModified_MachineData);
6744 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
6745 }
6746 }
6747 }
6748 return hrc;
6749}
6750
6751STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
6752{
6753 CheckComArgOutPointerValid(pbstrConfig);
6754 AutoCaller autoCaller(this);
6755 HRESULT hrc = autoCaller.rc();
6756 if (SUCCEEDED(hrc))
6757 {
6758 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6759 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
6760 }
6761 return hrc;
6762}
6763
6764STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
6765{
6766 CheckComArgStr(bstrConfig);
6767 AutoCaller autoCaller(this);
6768 HRESULT hrc = autoCaller.rc();
6769 if (SUCCEEDED(hrc))
6770 {
6771 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6772 hrc = checkStateDependency(MutableStateDep);
6773 if (SUCCEEDED(hrc))
6774 {
6775 hrc = mHWData.backupEx();
6776 if (SUCCEEDED(hrc))
6777 {
6778 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
6779 if (SUCCEEDED(hrc))
6780 setModified(IsModified_MachineData);
6781 }
6782 }
6783 }
6784 return hrc;
6785
6786}
6787
6788STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
6789{
6790 CheckComArgOutPointerValid(pfAllow);
6791 AutoCaller autoCaller(this);
6792 HRESULT hrc = autoCaller.rc();
6793 if (SUCCEEDED(hrc))
6794 {
6795 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6796 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
6797 }
6798 return hrc;
6799}
6800
6801STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
6802{
6803 AutoCaller autoCaller(this);
6804 HRESULT hrc = autoCaller.rc();
6805 if (SUCCEEDED(hrc))
6806 {
6807 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6808 hrc = checkStateDependency(MutableStateDep);
6809 if (SUCCEEDED(hrc))
6810 {
6811 hrc = mHWData.backupEx();
6812 if (SUCCEEDED(hrc))
6813 {
6814 setModified(IsModified_MachineData);
6815 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
6816 }
6817 }
6818 }
6819 return hrc;
6820}
6821
6822STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
6823{
6824 CheckComArgOutPointerValid(pfEnabled);
6825 AutoCaller autoCaller(this);
6826 HRESULT hrc = autoCaller.rc();
6827 if (SUCCEEDED(hrc))
6828 {
6829 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6830 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
6831 }
6832 return hrc;
6833}
6834
6835STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
6836{
6837 AutoCaller autoCaller(this);
6838 HRESULT hrc = autoCaller.rc();
6839 if (SUCCEEDED(hrc))
6840 {
6841 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6842 hrc = checkStateDependency(MutableStateDep);
6843 if ( SUCCEEDED(hrc)
6844 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
6845 {
6846 AutostartDb *autostartDb = mParent->getAutostartDb();
6847 int vrc;
6848
6849 if (fEnabled)
6850 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6851 else
6852 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6853
6854 if (RT_SUCCESS(vrc))
6855 {
6856 hrc = mHWData.backupEx();
6857 if (SUCCEEDED(hrc))
6858 {
6859 setModified(IsModified_MachineData);
6860 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
6861 }
6862 }
6863 else if (vrc == VERR_NOT_SUPPORTED)
6864 hrc = setError(VBOX_E_NOT_SUPPORTED,
6865 tr("The VM autostart feature is not supported on this platform"));
6866 else if (vrc == VERR_PATH_NOT_FOUND)
6867 hrc = setError(E_FAIL,
6868 tr("The path to the autostart database is not set"));
6869 else
6870 hrc = setError(E_UNEXPECTED,
6871 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6872 fEnabled ? "Adding" : "Removing",
6873 mUserData->s.strName.c_str(), vrc);
6874 }
6875 }
6876 return hrc;
6877}
6878
6879STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
6880{
6881 CheckComArgOutPointerValid(puDelay);
6882 AutoCaller autoCaller(this);
6883 HRESULT hrc = autoCaller.rc();
6884 if (SUCCEEDED(hrc))
6885 {
6886 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6887 *puDelay = mHWData->mAutostart.uAutostartDelay;
6888 }
6889 return hrc;
6890}
6891
6892STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
6893{
6894 AutoCaller autoCaller(this);
6895 HRESULT hrc = autoCaller.rc();
6896 if (SUCCEEDED(hrc))
6897 {
6898 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6899 hrc = checkStateDependency(MutableStateDep);
6900 if (SUCCEEDED(hrc))
6901 {
6902 hrc = mHWData.backupEx();
6903 if (SUCCEEDED(hrc))
6904 {
6905 setModified(IsModified_MachineData);
6906 mHWData->mAutostart.uAutostartDelay = uDelay;
6907 }
6908 }
6909 }
6910 return hrc;
6911}
6912
6913STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
6914{
6915 CheckComArgOutPointerValid(penmAutostopType);
6916 AutoCaller autoCaller(this);
6917 HRESULT hrc = autoCaller.rc();
6918 if (SUCCEEDED(hrc))
6919 {
6920 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6921 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
6922 }
6923 return hrc;
6924}
6925
6926STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
6927{
6928 AutoCaller autoCaller(this);
6929 HRESULT hrc = autoCaller.rc();
6930 if (SUCCEEDED(hrc))
6931 {
6932 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6933 hrc = checkStateDependency(MutableStateDep);
6934 if ( SUCCEEDED(hrc)
6935 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
6936 {
6937 AutostartDb *autostartDb = mParent->getAutostartDb();
6938 int vrc;
6939
6940 if (enmAutostopType != AutostopType_Disabled)
6941 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6942 else
6943 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6944
6945 if (RT_SUCCESS(vrc))
6946 {
6947 hrc = mHWData.backupEx();
6948 if (SUCCEEDED(hrc))
6949 {
6950 setModified(IsModified_MachineData);
6951 mHWData->mAutostart.enmAutostopType = enmAutostopType;
6952 }
6953 }
6954 else if (vrc == VERR_NOT_SUPPORTED)
6955 hrc = setError(VBOX_E_NOT_SUPPORTED,
6956 tr("The VM autostop feature is not supported on this platform"));
6957 else if (vrc == VERR_PATH_NOT_FOUND)
6958 hrc = setError(E_FAIL,
6959 tr("The path to the autostart database is not set"));
6960 else
6961 hrc = setError(E_UNEXPECTED,
6962 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6963 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6964 mUserData->s.strName.c_str(), vrc);
6965 }
6966 }
6967 return hrc;
6968}
6969
6970
6971STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
6972{
6973 LogFlowFuncEnter();
6974
6975 CheckComArgNotNull(pTarget);
6976 CheckComArgOutPointerValid(pProgress);
6977
6978 /* Convert the options. */
6979 RTCList<CloneOptions_T> optList;
6980 if (options != NULL)
6981 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
6982
6983 if (optList.contains(CloneOptions_Link))
6984 {
6985 if (!isSnapshotMachine())
6986 return setError(E_INVALIDARG,
6987 tr("Linked clone can only be created from a snapshot"));
6988 if (mode != CloneMode_MachineState)
6989 return setError(E_INVALIDARG,
6990 tr("Linked clone can only be created for a single machine state"));
6991 }
6992 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6993
6994 AutoCaller autoCaller(this);
6995 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6996
6997
6998 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
6999
7000 HRESULT rc = pWorker->start(pProgress);
7001
7002 LogFlowFuncLeave();
7003
7004 return rc;
7005}
7006
7007// public methods for internal purposes
7008/////////////////////////////////////////////////////////////////////////////
7009
7010/**
7011 * Adds the given IsModified_* flag to the dirty flags of the machine.
7012 * This must be called either during loadSettings or under the machine write lock.
7013 * @param fl
7014 */
7015void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7016{
7017 mData->flModifications |= fl;
7018 if (fAllowStateModification && isStateModificationAllowed())
7019 mData->mCurrentStateModified = true;
7020}
7021
7022/**
7023 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7024 * care of the write locking.
7025 *
7026 * @param fModifications The flag to add.
7027 */
7028void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7029{
7030 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7031 setModified(fModification, fAllowStateModification);
7032}
7033
7034/**
7035 * Saves the registry entry of this machine to the given configuration node.
7036 *
7037 * @param aEntryNode Node to save the registry entry to.
7038 *
7039 * @note locks this object for reading.
7040 */
7041HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7042{
7043 AutoLimitedCaller autoCaller(this);
7044 AssertComRCReturnRC(autoCaller.rc());
7045
7046 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7047
7048 data.uuid = mData->mUuid;
7049 data.strSettingsFile = mData->m_strConfigFile;
7050
7051 return S_OK;
7052}
7053
7054/**
7055 * Calculates the absolute path of the given path taking the directory of the
7056 * machine settings file as the current directory.
7057 *
7058 * @param aPath Path to calculate the absolute path for.
7059 * @param aResult Where to put the result (used only on success, can be the
7060 * same Utf8Str instance as passed in @a aPath).
7061 * @return IPRT result.
7062 *
7063 * @note Locks this object for reading.
7064 */
7065int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7066{
7067 AutoCaller autoCaller(this);
7068 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7069
7070 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7071
7072 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7073
7074 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7075
7076 strSettingsDir.stripFilename();
7077 char folder[RTPATH_MAX];
7078 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7079 if (RT_SUCCESS(vrc))
7080 aResult = folder;
7081
7082 return vrc;
7083}
7084
7085/**
7086 * Copies strSource to strTarget, making it relative to the machine folder
7087 * if it is a subdirectory thereof, or simply copying it otherwise.
7088 *
7089 * @param strSource Path to evaluate and copy.
7090 * @param strTarget Buffer to receive target path.
7091 *
7092 * @note Locks this object for reading.
7093 */
7094void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7095 Utf8Str &strTarget)
7096{
7097 AutoCaller autoCaller(this);
7098 AssertComRCReturn(autoCaller.rc(), (void)0);
7099
7100 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7101
7102 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7103 // use strTarget as a temporary buffer to hold the machine settings dir
7104 strTarget = mData->m_strConfigFileFull;
7105 strTarget.stripFilename();
7106 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7107 {
7108 // is relative: then append what's left
7109 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7110 // for empty paths (only possible for subdirs) use "." to avoid
7111 // triggering default settings for not present config attributes.
7112 if (strTarget.isEmpty())
7113 strTarget = ".";
7114 }
7115 else
7116 // is not relative: then overwrite
7117 strTarget = strSource;
7118}
7119
7120/**
7121 * Returns the full path to the machine's log folder in the
7122 * \a aLogFolder argument.
7123 */
7124void Machine::getLogFolder(Utf8Str &aLogFolder)
7125{
7126 AutoCaller autoCaller(this);
7127 AssertComRCReturnVoid(autoCaller.rc());
7128
7129 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7130
7131 char szTmp[RTPATH_MAX];
7132 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7133 if (RT_SUCCESS(vrc))
7134 {
7135 if (szTmp[0] && !mUserData.isNull())
7136 {
7137 char szTmp2[RTPATH_MAX];
7138 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7139 if (RT_SUCCESS(vrc))
7140 aLogFolder = BstrFmt("%s%c%s",
7141 szTmp2,
7142 RTPATH_DELIMITER,
7143 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7144 }
7145 else
7146 vrc = VERR_PATH_IS_RELATIVE;
7147 }
7148
7149 if (RT_FAILURE(vrc))
7150 {
7151 // fallback if VBOX_USER_LOGHOME is not set or invalid
7152 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7153 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7154 aLogFolder.append(RTPATH_DELIMITER);
7155 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7156 }
7157}
7158
7159/**
7160 * Returns the full path to the machine's log file for an given index.
7161 */
7162Utf8Str Machine::queryLogFilename(ULONG idx)
7163{
7164 Utf8Str logFolder;
7165 getLogFolder(logFolder);
7166 Assert(logFolder.length());
7167 Utf8Str log;
7168 if (idx == 0)
7169 log = Utf8StrFmt("%s%cVBox.log",
7170 logFolder.c_str(), RTPATH_DELIMITER);
7171 else
7172 log = Utf8StrFmt("%s%cVBox.log.%d",
7173 logFolder.c_str(), RTPATH_DELIMITER, idx);
7174 return log;
7175}
7176
7177/**
7178 * Composes a unique saved state filename based on the current system time. The filename is
7179 * granular to the second so this will work so long as no more than one snapshot is taken on
7180 * a machine per second.
7181 *
7182 * Before version 4.1, we used this formula for saved state files:
7183 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7184 * which no longer works because saved state files can now be shared between the saved state of the
7185 * "saved" machine and an online snapshot, and the following would cause problems:
7186 * 1) save machine
7187 * 2) create online snapshot from that machine state --> reusing saved state file
7188 * 3) save machine again --> filename would be reused, breaking the online snapshot
7189 *
7190 * So instead we now use a timestamp.
7191 *
7192 * @param str
7193 */
7194void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7195{
7196 AutoCaller autoCaller(this);
7197 AssertComRCReturnVoid(autoCaller.rc());
7198
7199 {
7200 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7201 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7202 }
7203
7204 RTTIMESPEC ts;
7205 RTTimeNow(&ts);
7206 RTTIME time;
7207 RTTimeExplode(&time, &ts);
7208
7209 strStateFilePath += RTPATH_DELIMITER;
7210 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7211 time.i32Year, time.u8Month, time.u8MonthDay,
7212 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7213}
7214
7215/**
7216 * @note Locks this object for writing, calls the client process
7217 * (inside the lock).
7218 */
7219HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7220 const Utf8Str &strType,
7221 const Utf8Str &strEnvironment,
7222 ProgressProxy *aProgress)
7223{
7224 LogFlowThisFuncEnter();
7225
7226 AssertReturn(aControl, E_FAIL);
7227 AssertReturn(aProgress, E_FAIL);
7228
7229 AutoCaller autoCaller(this);
7230 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7231
7232 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7233
7234 if (!mData->mRegistered)
7235 return setError(E_UNEXPECTED,
7236 tr("The machine '%s' is not registered"),
7237 mUserData->s.strName.c_str());
7238
7239 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7240
7241 if ( mData->mSession.mState == SessionState_Locked
7242 || mData->mSession.mState == SessionState_Spawning
7243 || mData->mSession.mState == SessionState_Unlocking)
7244 return setError(VBOX_E_INVALID_OBJECT_STATE,
7245 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7246 mUserData->s.strName.c_str());
7247
7248 /* may not be busy */
7249 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7250
7251 /* get the path to the executable */
7252 char szPath[RTPATH_MAX];
7253 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7254 size_t sz = strlen(szPath);
7255 szPath[sz++] = RTPATH_DELIMITER;
7256 szPath[sz] = 0;
7257 char *cmd = szPath + sz;
7258 sz = RTPATH_MAX - sz;
7259
7260 int vrc = VINF_SUCCESS;
7261 RTPROCESS pid = NIL_RTPROCESS;
7262
7263 RTENV env = RTENV_DEFAULT;
7264
7265 if (!strEnvironment.isEmpty())
7266 {
7267 char *newEnvStr = NULL;
7268
7269 do
7270 {
7271 /* clone the current environment */
7272 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7273 AssertRCBreakStmt(vrc2, vrc = vrc2);
7274
7275 newEnvStr = RTStrDup(strEnvironment.c_str());
7276 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7277
7278 /* put new variables to the environment
7279 * (ignore empty variable names here since RTEnv API
7280 * intentionally doesn't do that) */
7281 char *var = newEnvStr;
7282 for (char *p = newEnvStr; *p; ++p)
7283 {
7284 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7285 {
7286 *p = '\0';
7287 if (*var)
7288 {
7289 char *val = strchr(var, '=');
7290 if (val)
7291 {
7292 *val++ = '\0';
7293 vrc2 = RTEnvSetEx(env, var, val);
7294 }
7295 else
7296 vrc2 = RTEnvUnsetEx(env, var);
7297 if (RT_FAILURE(vrc2))
7298 break;
7299 }
7300 var = p + 1;
7301 }
7302 }
7303 if (RT_SUCCESS(vrc2) && *var)
7304 vrc2 = RTEnvPutEx(env, var);
7305
7306 AssertRCBreakStmt(vrc2, vrc = vrc2);
7307 }
7308 while (0);
7309
7310 if (newEnvStr != NULL)
7311 RTStrFree(newEnvStr);
7312 }
7313
7314 /* Qt is default */
7315#ifdef VBOX_WITH_QTGUI
7316 if (strType == "gui" || strType == "GUI/Qt")
7317 {
7318# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7319 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7320# else
7321 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7322# endif
7323 Assert(sz >= sizeof(VirtualBox_exe));
7324 strcpy(cmd, VirtualBox_exe);
7325
7326 Utf8Str idStr = mData->mUuid.toString();
7327 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7328 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7329 }
7330#else /* !VBOX_WITH_QTGUI */
7331 if (0)
7332 ;
7333#endif /* VBOX_WITH_QTGUI */
7334
7335 else
7336
7337#ifdef VBOX_WITH_VBOXSDL
7338 if (strType == "sdl" || strType == "GUI/SDL")
7339 {
7340 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7341 Assert(sz >= sizeof(VBoxSDL_exe));
7342 strcpy(cmd, VBoxSDL_exe);
7343
7344 Utf8Str idStr = mData->mUuid.toString();
7345 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7346 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7347 }
7348#else /* !VBOX_WITH_VBOXSDL */
7349 if (0)
7350 ;
7351#endif /* !VBOX_WITH_VBOXSDL */
7352
7353 else
7354
7355#ifdef VBOX_WITH_HEADLESS
7356 if ( strType == "headless"
7357 || strType == "capture"
7358 || strType == "vrdp" /* Deprecated. Same as headless. */
7359 )
7360 {
7361 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7362 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7363 * and a VM works even if the server has not been installed.
7364 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7365 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7366 * differently in 4.0 and 3.x.
7367 */
7368 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7369 Assert(sz >= sizeof(VBoxHeadless_exe));
7370 strcpy(cmd, VBoxHeadless_exe);
7371
7372 Utf8Str idStr = mData->mUuid.toString();
7373 /* Leave space for "--capture" arg. */
7374 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7375 "--startvm", idStr.c_str(),
7376 "--vrde", "config",
7377 0, /* For "--capture". */
7378 0 };
7379 if (strType == "capture")
7380 {
7381 unsigned pos = RT_ELEMENTS(args) - 2;
7382 args[pos] = "--capture";
7383 }
7384 vrc = RTProcCreate(szPath, args, env,
7385#ifdef RT_OS_WINDOWS
7386 RTPROC_FLAGS_NO_WINDOW
7387#else
7388 0
7389#endif
7390 , &pid);
7391 }
7392#else /* !VBOX_WITH_HEADLESS */
7393 if (0)
7394 ;
7395#endif /* !VBOX_WITH_HEADLESS */
7396 else
7397 {
7398 RTEnvDestroy(env);
7399 return setError(E_INVALIDARG,
7400 tr("Invalid session type: '%s'"),
7401 strType.c_str());
7402 }
7403
7404 RTEnvDestroy(env);
7405
7406 if (RT_FAILURE(vrc))
7407 return setError(VBOX_E_IPRT_ERROR,
7408 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7409 mUserData->s.strName.c_str(), vrc);
7410
7411 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7412
7413 /*
7414 * Note that we don't release the lock here before calling the client,
7415 * because it doesn't need to call us back if called with a NULL argument.
7416 * Releasing the lock here is dangerous because we didn't prepare the
7417 * launch data yet, but the client we've just started may happen to be
7418 * too fast and call openSession() that will fail (because of PID, etc.),
7419 * so that the Machine will never get out of the Spawning session state.
7420 */
7421
7422 /* inform the session that it will be a remote one */
7423 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7424 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
7425 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7426
7427 if (FAILED(rc))
7428 {
7429 /* restore the session state */
7430 mData->mSession.mState = SessionState_Unlocked;
7431 /* The failure may occur w/o any error info (from RPC), so provide one */
7432 return setError(VBOX_E_VM_ERROR,
7433 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7434 }
7435
7436 /* attach launch data to the machine */
7437 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7438 mData->mSession.mRemoteControls.push_back(aControl);
7439 mData->mSession.mProgress = aProgress;
7440 mData->mSession.mPID = pid;
7441 mData->mSession.mState = SessionState_Spawning;
7442 mData->mSession.mType = strType;
7443
7444 LogFlowThisFuncLeave();
7445 return S_OK;
7446}
7447
7448/**
7449 * Returns @c true if the given machine has an open direct session and returns
7450 * the session machine instance and additional session data (on some platforms)
7451 * if so.
7452 *
7453 * Note that when the method returns @c false, the arguments remain unchanged.
7454 *
7455 * @param aMachine Session machine object.
7456 * @param aControl Direct session control object (optional).
7457 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7458 *
7459 * @note locks this object for reading.
7460 */
7461#if defined(RT_OS_WINDOWS)
7462bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7463 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7464 HANDLE *aIPCSem /*= NULL*/,
7465 bool aAllowClosing /*= false*/)
7466#elif defined(RT_OS_OS2)
7467bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7468 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7469 HMTX *aIPCSem /*= NULL*/,
7470 bool aAllowClosing /*= false*/)
7471#else
7472bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7473 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7474 bool aAllowClosing /*= false*/)
7475#endif
7476{
7477 AutoLimitedCaller autoCaller(this);
7478 AssertComRCReturn(autoCaller.rc(), false);
7479
7480 /* just return false for inaccessible machines */
7481 if (autoCaller.state() != Ready)
7482 return false;
7483
7484 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7485
7486 if ( mData->mSession.mState == SessionState_Locked
7487 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7488 )
7489 {
7490 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7491
7492 aMachine = mData->mSession.mMachine;
7493
7494 if (aControl != NULL)
7495 *aControl = mData->mSession.mDirectControl;
7496
7497#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7498 /* Additional session data */
7499 if (aIPCSem != NULL)
7500 *aIPCSem = aMachine->mIPCSem;
7501#endif
7502 return true;
7503 }
7504
7505 return false;
7506}
7507
7508/**
7509 * Returns @c true if the given machine has an spawning direct session and
7510 * returns and additional session data (on some platforms) if so.
7511 *
7512 * Note that when the method returns @c false, the arguments remain unchanged.
7513 *
7514 * @param aPID PID of the spawned direct session process.
7515 *
7516 * @note locks this object for reading.
7517 */
7518#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7519bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7520#else
7521bool Machine::isSessionSpawning()
7522#endif
7523{
7524 AutoLimitedCaller autoCaller(this);
7525 AssertComRCReturn(autoCaller.rc(), false);
7526
7527 /* just return false for inaccessible machines */
7528 if (autoCaller.state() != Ready)
7529 return false;
7530
7531 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7532
7533 if (mData->mSession.mState == SessionState_Spawning)
7534 {
7535#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7536 /* Additional session data */
7537 if (aPID != NULL)
7538 {
7539 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
7540 *aPID = mData->mSession.mPID;
7541 }
7542#endif
7543 return true;
7544 }
7545
7546 return false;
7547}
7548
7549/**
7550 * Called from the client watcher thread to check for unexpected client process
7551 * death during Session_Spawning state (e.g. before it successfully opened a
7552 * direct session).
7553 *
7554 * On Win32 and on OS/2, this method is called only when we've got the
7555 * direct client's process termination notification, so it always returns @c
7556 * true.
7557 *
7558 * On other platforms, this method returns @c true if the client process is
7559 * terminated and @c false if it's still alive.
7560 *
7561 * @note Locks this object for writing.
7562 */
7563bool Machine::checkForSpawnFailure()
7564{
7565 AutoCaller autoCaller(this);
7566 if (!autoCaller.isOk())
7567 {
7568 /* nothing to do */
7569 LogFlowThisFunc(("Already uninitialized!\n"));
7570 return true;
7571 }
7572
7573 /* VirtualBox::addProcessToReap() needs a write lock */
7574 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
7575
7576 if (mData->mSession.mState != SessionState_Spawning)
7577 {
7578 /* nothing to do */
7579 LogFlowThisFunc(("Not spawning any more!\n"));
7580 return true;
7581 }
7582
7583 HRESULT rc = S_OK;
7584
7585#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7586
7587 /* the process was already unexpectedly terminated, we just need to set an
7588 * error and finalize session spawning */
7589 rc = setError(E_FAIL,
7590 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
7591 getName().c_str());
7592#else
7593
7594 /* PID not yet initialized, skip check. */
7595 if (mData->mSession.mPID == NIL_RTPROCESS)
7596 return false;
7597
7598 RTPROCSTATUS status;
7599 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
7600 &status);
7601
7602 if (vrc != VERR_PROCESS_RUNNING)
7603 {
7604 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7605 rc = setError(E_FAIL,
7606 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7607 getName().c_str(), status.iStatus);
7608 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7609 rc = setError(E_FAIL,
7610 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7611 getName().c_str(), status.iStatus);
7612 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7613 rc = setError(E_FAIL,
7614 tr("The virtual machine '%s' has terminated abnormally"),
7615 getName().c_str(), status.iStatus);
7616 else
7617 rc = setError(E_FAIL,
7618 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7619 getName().c_str(), rc);
7620 }
7621
7622#endif
7623
7624 if (FAILED(rc))
7625 {
7626 /* Close the remote session, remove the remote control from the list
7627 * and reset session state to Closed (@note keep the code in sync with
7628 * the relevant part in checkForSpawnFailure()). */
7629
7630 Assert(mData->mSession.mRemoteControls.size() == 1);
7631 if (mData->mSession.mRemoteControls.size() == 1)
7632 {
7633 ErrorInfoKeeper eik;
7634 mData->mSession.mRemoteControls.front()->Uninitialize();
7635 }
7636
7637 mData->mSession.mRemoteControls.clear();
7638 mData->mSession.mState = SessionState_Unlocked;
7639
7640 /* finalize the progress after setting the state */
7641 if (!mData->mSession.mProgress.isNull())
7642 {
7643 mData->mSession.mProgress->notifyComplete(rc);
7644 mData->mSession.mProgress.setNull();
7645 }
7646
7647 mParent->addProcessToReap(mData->mSession.mPID);
7648 mData->mSession.mPID = NIL_RTPROCESS;
7649
7650 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7651 return true;
7652 }
7653
7654 return false;
7655}
7656
7657/**
7658 * Checks whether the machine can be registered. If so, commits and saves
7659 * all settings.
7660 *
7661 * @note Must be called from mParent's write lock. Locks this object and
7662 * children for writing.
7663 */
7664HRESULT Machine::prepareRegister()
7665{
7666 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7667
7668 AutoLimitedCaller autoCaller(this);
7669 AssertComRCReturnRC(autoCaller.rc());
7670
7671 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7672
7673 /* wait for state dependents to drop to zero */
7674 ensureNoStateDependencies();
7675
7676 if (!mData->mAccessible)
7677 return setError(VBOX_E_INVALID_OBJECT_STATE,
7678 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7679 mUserData->s.strName.c_str(),
7680 mData->mUuid.toString().c_str());
7681
7682 AssertReturn(autoCaller.state() == Ready, E_FAIL);
7683
7684 if (mData->mRegistered)
7685 return setError(VBOX_E_INVALID_OBJECT_STATE,
7686 tr("The machine '%s' with UUID {%s} is already registered"),
7687 mUserData->s.strName.c_str(),
7688 mData->mUuid.toString().c_str());
7689
7690 HRESULT rc = S_OK;
7691
7692 // Ensure the settings are saved. If we are going to be registered and
7693 // no config file exists yet, create it by calling saveSettings() too.
7694 if ( (mData->flModifications)
7695 || (!mData->pMachineConfigFile->fileExists())
7696 )
7697 {
7698 rc = saveSettings(NULL);
7699 // no need to check whether VirtualBox.xml needs saving too since
7700 // we can't have a machine XML file rename pending
7701 if (FAILED(rc)) return rc;
7702 }
7703
7704 /* more config checking goes here */
7705
7706 if (SUCCEEDED(rc))
7707 {
7708 /* we may have had implicit modifications we want to fix on success */
7709 commit();
7710
7711 mData->mRegistered = true;
7712 }
7713 else
7714 {
7715 /* we may have had implicit modifications we want to cancel on failure*/
7716 rollback(false /* aNotify */);
7717 }
7718
7719 return rc;
7720}
7721
7722/**
7723 * Increases the number of objects dependent on the machine state or on the
7724 * registered state. Guarantees that these two states will not change at least
7725 * until #releaseStateDependency() is called.
7726 *
7727 * Depending on the @a aDepType value, additional state checks may be made.
7728 * These checks will set extended error info on failure. See
7729 * #checkStateDependency() for more info.
7730 *
7731 * If this method returns a failure, the dependency is not added and the caller
7732 * is not allowed to rely on any particular machine state or registration state
7733 * value and may return the failed result code to the upper level.
7734 *
7735 * @param aDepType Dependency type to add.
7736 * @param aState Current machine state (NULL if not interested).
7737 * @param aRegistered Current registered state (NULL if not interested).
7738 *
7739 * @note Locks this object for writing.
7740 */
7741HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7742 MachineState_T *aState /* = NULL */,
7743 BOOL *aRegistered /* = NULL */)
7744{
7745 AutoCaller autoCaller(this);
7746 AssertComRCReturnRC(autoCaller.rc());
7747
7748 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7749
7750 HRESULT rc = checkStateDependency(aDepType);
7751 if (FAILED(rc)) return rc;
7752
7753 {
7754 if (mData->mMachineStateChangePending != 0)
7755 {
7756 /* ensureNoStateDependencies() is waiting for state dependencies to
7757 * drop to zero so don't add more. It may make sense to wait a bit
7758 * and retry before reporting an error (since the pending state
7759 * transition should be really quick) but let's just assert for
7760 * now to see if it ever happens on practice. */
7761
7762 AssertFailed();
7763
7764 return setError(E_ACCESSDENIED,
7765 tr("Machine state change is in progress. Please retry the operation later."));
7766 }
7767
7768 ++mData->mMachineStateDeps;
7769 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7770 }
7771
7772 if (aState)
7773 *aState = mData->mMachineState;
7774 if (aRegistered)
7775 *aRegistered = mData->mRegistered;
7776
7777 return S_OK;
7778}
7779
7780/**
7781 * Decreases the number of objects dependent on the machine state.
7782 * Must always complete the #addStateDependency() call after the state
7783 * dependency is no more necessary.
7784 */
7785void Machine::releaseStateDependency()
7786{
7787 AutoCaller autoCaller(this);
7788 AssertComRCReturnVoid(autoCaller.rc());
7789
7790 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7791
7792 /* releaseStateDependency() w/o addStateDependency()? */
7793 AssertReturnVoid(mData->mMachineStateDeps != 0);
7794 -- mData->mMachineStateDeps;
7795
7796 if (mData->mMachineStateDeps == 0)
7797 {
7798 /* inform ensureNoStateDependencies() that there are no more deps */
7799 if (mData->mMachineStateChangePending != 0)
7800 {
7801 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7802 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7803 }
7804 }
7805}
7806
7807// protected methods
7808/////////////////////////////////////////////////////////////////////////////
7809
7810/**
7811 * Performs machine state checks based on the @a aDepType value. If a check
7812 * fails, this method will set extended error info, otherwise it will return
7813 * S_OK. It is supposed, that on failure, the caller will immediately return
7814 * the return value of this method to the upper level.
7815 *
7816 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7817 *
7818 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7819 * current state of this machine object allows to change settings of the
7820 * machine (i.e. the machine is not registered, or registered but not running
7821 * and not saved). It is useful to call this method from Machine setters
7822 * before performing any change.
7823 *
7824 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7825 * as for MutableStateDep except that if the machine is saved, S_OK is also
7826 * returned. This is useful in setters which allow changing machine
7827 * properties when it is in the saved state.
7828 *
7829 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7830 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7831 * Aborted).
7832 *
7833 * @param aDepType Dependency type to check.
7834 *
7835 * @note Non Machine based classes should use #addStateDependency() and
7836 * #releaseStateDependency() methods or the smart AutoStateDependency
7837 * template.
7838 *
7839 * @note This method must be called from under this object's read or write
7840 * lock.
7841 */
7842HRESULT Machine::checkStateDependency(StateDependency aDepType)
7843{
7844 switch (aDepType)
7845 {
7846 case AnyStateDep:
7847 {
7848 break;
7849 }
7850 case MutableStateDep:
7851 {
7852 if ( mData->mRegistered
7853 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7854 || ( mData->mMachineState != MachineState_Paused
7855 && mData->mMachineState != MachineState_Running
7856 && mData->mMachineState != MachineState_Aborted
7857 && mData->mMachineState != MachineState_Teleported
7858 && mData->mMachineState != MachineState_PoweredOff
7859 )
7860 )
7861 )
7862 return setError(VBOX_E_INVALID_VM_STATE,
7863 tr("The machine is not mutable (state is %s)"),
7864 Global::stringifyMachineState(mData->mMachineState));
7865 break;
7866 }
7867 case MutableOrSavedStateDep:
7868 {
7869 if ( mData->mRegistered
7870 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7871 || ( mData->mMachineState != MachineState_Paused
7872 && mData->mMachineState != MachineState_Running
7873 && mData->mMachineState != MachineState_Aborted
7874 && mData->mMachineState != MachineState_Teleported
7875 && mData->mMachineState != MachineState_Saved
7876 && mData->mMachineState != MachineState_PoweredOff
7877 )
7878 )
7879 )
7880 return setError(VBOX_E_INVALID_VM_STATE,
7881 tr("The machine is not mutable (state is %s)"),
7882 Global::stringifyMachineState(mData->mMachineState));
7883 break;
7884 }
7885 case OfflineStateDep:
7886 {
7887 if ( mData->mRegistered
7888 && ( !isSessionMachine()
7889 || ( mData->mMachineState != MachineState_PoweredOff
7890 && mData->mMachineState != MachineState_Saved
7891 && mData->mMachineState != MachineState_Aborted
7892 && mData->mMachineState != MachineState_Teleported
7893 )
7894 )
7895 )
7896 return setError(VBOX_E_INVALID_VM_STATE,
7897 tr("The machine is not offline (state is %s)"),
7898 Global::stringifyMachineState(mData->mMachineState));
7899 break;
7900 }
7901 }
7902
7903 return S_OK;
7904}
7905
7906/**
7907 * Helper to initialize all associated child objects and allocate data
7908 * structures.
7909 *
7910 * This method must be called as a part of the object's initialization procedure
7911 * (usually done in the #init() method).
7912 *
7913 * @note Must be called only from #init() or from #registeredInit().
7914 */
7915HRESULT Machine::initDataAndChildObjects()
7916{
7917 AutoCaller autoCaller(this);
7918 AssertComRCReturnRC(autoCaller.rc());
7919 AssertComRCReturn(autoCaller.state() == InInit ||
7920 autoCaller.state() == Limited, E_FAIL);
7921
7922 AssertReturn(!mData->mAccessible, E_FAIL);
7923
7924 /* allocate data structures */
7925 mSSData.allocate();
7926 mUserData.allocate();
7927 mHWData.allocate();
7928 mMediaData.allocate();
7929 mStorageControllers.allocate();
7930
7931 /* initialize mOSTypeId */
7932 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
7933
7934 /* create associated BIOS settings object */
7935 unconst(mBIOSSettings).createObject();
7936 mBIOSSettings->init(this);
7937
7938 /* create an associated VRDE object (default is disabled) */
7939 unconst(mVRDEServer).createObject();
7940 mVRDEServer->init(this);
7941
7942 /* create associated serial port objects */
7943 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
7944 {
7945 unconst(mSerialPorts[slot]).createObject();
7946 mSerialPorts[slot]->init(this, slot);
7947 }
7948
7949 /* create associated parallel port objects */
7950 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7951 {
7952 unconst(mParallelPorts[slot]).createObject();
7953 mParallelPorts[slot]->init(this, slot);
7954 }
7955
7956 /* create the audio adapter object (always present, default is disabled) */
7957 unconst(mAudioAdapter).createObject();
7958 mAudioAdapter->init(this);
7959
7960 /* create the USB controller object (always present, default is disabled) */
7961 unconst(mUSBController).createObject();
7962 mUSBController->init(this);
7963
7964 /* create associated network adapter objects */
7965 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
7966 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7967 {
7968 unconst(mNetworkAdapters[slot]).createObject();
7969 mNetworkAdapters[slot]->init(this, slot);
7970 }
7971
7972 /* create the bandwidth control */
7973 unconst(mBandwidthControl).createObject();
7974 mBandwidthControl->init(this);
7975
7976 return S_OK;
7977}
7978
7979/**
7980 * Helper to uninitialize all associated child objects and to free all data
7981 * structures.
7982 *
7983 * This method must be called as a part of the object's uninitialization
7984 * procedure (usually done in the #uninit() method).
7985 *
7986 * @note Must be called only from #uninit() or from #registeredInit().
7987 */
7988void Machine::uninitDataAndChildObjects()
7989{
7990 AutoCaller autoCaller(this);
7991 AssertComRCReturnVoid(autoCaller.rc());
7992 AssertComRCReturnVoid( autoCaller.state() == InUninit
7993 || autoCaller.state() == Limited);
7994
7995 /* tell all our other child objects we've been uninitialized */
7996 if (mBandwidthControl)
7997 {
7998 mBandwidthControl->uninit();
7999 unconst(mBandwidthControl).setNull();
8000 }
8001
8002 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8003 {
8004 if (mNetworkAdapters[slot])
8005 {
8006 mNetworkAdapters[slot]->uninit();
8007 unconst(mNetworkAdapters[slot]).setNull();
8008 }
8009 }
8010
8011 if (mUSBController)
8012 {
8013 mUSBController->uninit();
8014 unconst(mUSBController).setNull();
8015 }
8016
8017 if (mAudioAdapter)
8018 {
8019 mAudioAdapter->uninit();
8020 unconst(mAudioAdapter).setNull();
8021 }
8022
8023 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8024 {
8025 if (mParallelPorts[slot])
8026 {
8027 mParallelPorts[slot]->uninit();
8028 unconst(mParallelPorts[slot]).setNull();
8029 }
8030 }
8031
8032 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8033 {
8034 if (mSerialPorts[slot])
8035 {
8036 mSerialPorts[slot]->uninit();
8037 unconst(mSerialPorts[slot]).setNull();
8038 }
8039 }
8040
8041 if (mVRDEServer)
8042 {
8043 mVRDEServer->uninit();
8044 unconst(mVRDEServer).setNull();
8045 }
8046
8047 if (mBIOSSettings)
8048 {
8049 mBIOSSettings->uninit();
8050 unconst(mBIOSSettings).setNull();
8051 }
8052
8053 /* Deassociate media (only when a real Machine or a SnapshotMachine
8054 * instance is uninitialized; SessionMachine instances refer to real
8055 * Machine media). This is necessary for a clean re-initialization of
8056 * the VM after successfully re-checking the accessibility state. Note
8057 * that in case of normal Machine or SnapshotMachine uninitialization (as
8058 * a result of unregistering or deleting the snapshot), outdated media
8059 * attachments will already be uninitialized and deleted, so this
8060 * code will not affect them. */
8061 if ( !!mMediaData
8062 && (!isSessionMachine())
8063 )
8064 {
8065 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8066 it != mMediaData->mAttachments.end();
8067 ++it)
8068 {
8069 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8070 if (pMedium.isNull())
8071 continue;
8072 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8073 AssertComRC(rc);
8074 }
8075 }
8076
8077 if (!isSessionMachine() && !isSnapshotMachine())
8078 {
8079 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8080 if (mData->mFirstSnapshot)
8081 {
8082 // snapshots tree is protected by machine write lock; strictly
8083 // this isn't necessary here since we're deleting the entire
8084 // machine, but otherwise we assert in Snapshot::uninit()
8085 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8086 mData->mFirstSnapshot->uninit();
8087 mData->mFirstSnapshot.setNull();
8088 }
8089
8090 mData->mCurrentSnapshot.setNull();
8091 }
8092
8093 /* free data structures (the essential mData structure is not freed here
8094 * since it may be still in use) */
8095 mMediaData.free();
8096 mStorageControllers.free();
8097 mHWData.free();
8098 mUserData.free();
8099 mSSData.free();
8100}
8101
8102/**
8103 * Returns a pointer to the Machine object for this machine that acts like a
8104 * parent for complex machine data objects such as shared folders, etc.
8105 *
8106 * For primary Machine objects and for SnapshotMachine objects, returns this
8107 * object's pointer itself. For SessionMachine objects, returns the peer
8108 * (primary) machine pointer.
8109 */
8110Machine* Machine::getMachine()
8111{
8112 if (isSessionMachine())
8113 return (Machine*)mPeer;
8114 return this;
8115}
8116
8117/**
8118 * Makes sure that there are no machine state dependents. If necessary, waits
8119 * for the number of dependents to drop to zero.
8120 *
8121 * Make sure this method is called from under this object's write lock to
8122 * guarantee that no new dependents may be added when this method returns
8123 * control to the caller.
8124 *
8125 * @note Locks this object for writing. The lock will be released while waiting
8126 * (if necessary).
8127 *
8128 * @warning To be used only in methods that change the machine state!
8129 */
8130void Machine::ensureNoStateDependencies()
8131{
8132 AssertReturnVoid(isWriteLockOnCurrentThread());
8133
8134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8135
8136 /* Wait for all state dependents if necessary */
8137 if (mData->mMachineStateDeps != 0)
8138 {
8139 /* lazy semaphore creation */
8140 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8141 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8142
8143 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8144 mData->mMachineStateDeps));
8145
8146 ++mData->mMachineStateChangePending;
8147
8148 /* reset the semaphore before waiting, the last dependent will signal
8149 * it */
8150 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8151
8152 alock.release();
8153
8154 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8155
8156 alock.acquire();
8157
8158 -- mData->mMachineStateChangePending;
8159 }
8160}
8161
8162/**
8163 * Changes the machine state and informs callbacks.
8164 *
8165 * This method is not intended to fail so it either returns S_OK or asserts (and
8166 * returns a failure).
8167 *
8168 * @note Locks this object for writing.
8169 */
8170HRESULT Machine::setMachineState(MachineState_T aMachineState)
8171{
8172 LogFlowThisFuncEnter();
8173 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8174
8175 AutoCaller autoCaller(this);
8176 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8177
8178 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8179
8180 /* wait for state dependents to drop to zero */
8181 ensureNoStateDependencies();
8182
8183 if (mData->mMachineState != aMachineState)
8184 {
8185 mData->mMachineState = aMachineState;
8186
8187 RTTimeNow(&mData->mLastStateChange);
8188
8189 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8190 }
8191
8192 LogFlowThisFuncLeave();
8193 return S_OK;
8194}
8195
8196/**
8197 * Searches for a shared folder with the given logical name
8198 * in the collection of shared folders.
8199 *
8200 * @param aName logical name of the shared folder
8201 * @param aSharedFolder where to return the found object
8202 * @param aSetError whether to set the error info if the folder is
8203 * not found
8204 * @return
8205 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8206 *
8207 * @note
8208 * must be called from under the object's lock!
8209 */
8210HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8211 ComObjPtr<SharedFolder> &aSharedFolder,
8212 bool aSetError /* = false */)
8213{
8214 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8215 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8216 it != mHWData->mSharedFolders.end();
8217 ++it)
8218 {
8219 SharedFolder *pSF = *it;
8220 AutoCaller autoCaller(pSF);
8221 if (pSF->getName() == aName)
8222 {
8223 aSharedFolder = pSF;
8224 rc = S_OK;
8225 break;
8226 }
8227 }
8228
8229 if (aSetError && FAILED(rc))
8230 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8231
8232 return rc;
8233}
8234
8235/**
8236 * Initializes all machine instance data from the given settings structures
8237 * from XML. The exception is the machine UUID which needs special handling
8238 * depending on the caller's use case, so the caller needs to set that herself.
8239 *
8240 * This gets called in several contexts during machine initialization:
8241 *
8242 * -- When machine XML exists on disk already and needs to be loaded into memory,
8243 * for example, from registeredInit() to load all registered machines on
8244 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8245 * attached to the machine should be part of some media registry already.
8246 *
8247 * -- During OVF import, when a machine config has been constructed from an
8248 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8249 * ensure that the media listed as attachments in the config (which have
8250 * been imported from the OVF) receive the correct registry ID.
8251 *
8252 * -- During VM cloning.
8253 *
8254 * @param config Machine settings from XML.
8255 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8256 * @return
8257 */
8258HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8259 const Guid *puuidRegistry)
8260{
8261 // copy name, description, OS type, teleporter, UTC etc.
8262 mUserData->s = config.machineUserData;
8263
8264 // look up the object by Id to check it is valid
8265 ComPtr<IGuestOSType> guestOSType;
8266 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8267 guestOSType.asOutParam());
8268 if (FAILED(rc)) return rc;
8269
8270 // stateFile (optional)
8271 if (config.strStateFile.isEmpty())
8272 mSSData->strStateFilePath.setNull();
8273 else
8274 {
8275 Utf8Str stateFilePathFull(config.strStateFile);
8276 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8277 if (RT_FAILURE(vrc))
8278 return setError(E_FAIL,
8279 tr("Invalid saved state file path '%s' (%Rrc)"),
8280 config.strStateFile.c_str(),
8281 vrc);
8282 mSSData->strStateFilePath = stateFilePathFull;
8283 }
8284
8285 // snapshot folder needs special processing so set it again
8286 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8287 if (FAILED(rc)) return rc;
8288
8289 /* Copy the extra data items (Not in any case config is already the same as
8290 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8291 * make sure the extra data map is copied). */
8292 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8293
8294 /* currentStateModified (optional, default is true) */
8295 mData->mCurrentStateModified = config.fCurrentStateModified;
8296
8297 mData->mLastStateChange = config.timeLastStateChange;
8298
8299 /*
8300 * note: all mUserData members must be assigned prior this point because
8301 * we need to commit changes in order to let mUserData be shared by all
8302 * snapshot machine instances.
8303 */
8304 mUserData.commitCopy();
8305
8306 // machine registry, if present (must be loaded before snapshots)
8307 if (config.canHaveOwnMediaRegistry())
8308 {
8309 // determine machine folder
8310 Utf8Str strMachineFolder = getSettingsFileFull();
8311 strMachineFolder.stripFilename();
8312 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8313 config.mediaRegistry,
8314 strMachineFolder);
8315 if (FAILED(rc)) return rc;
8316 }
8317
8318 /* Snapshot node (optional) */
8319 size_t cRootSnapshots;
8320 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8321 {
8322 // there must be only one root snapshot
8323 Assert(cRootSnapshots == 1);
8324
8325 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8326
8327 rc = loadSnapshot(snap,
8328 config.uuidCurrentSnapshot,
8329 NULL); // no parent == first snapshot
8330 if (FAILED(rc)) return rc;
8331 }
8332
8333 // hardware data
8334 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8335 if (FAILED(rc)) return rc;
8336
8337 // load storage controllers
8338 rc = loadStorageControllers(config.storageMachine,
8339 puuidRegistry,
8340 NULL /* puuidSnapshot */);
8341 if (FAILED(rc)) return rc;
8342
8343 /*
8344 * NOTE: the assignment below must be the last thing to do,
8345 * otherwise it will be not possible to change the settings
8346 * somewhere in the code above because all setters will be
8347 * blocked by checkStateDependency(MutableStateDep).
8348 */
8349
8350 /* set the machine state to Aborted or Saved when appropriate */
8351 if (config.fAborted)
8352 {
8353 mSSData->strStateFilePath.setNull();
8354
8355 /* no need to use setMachineState() during init() */
8356 mData->mMachineState = MachineState_Aborted;
8357 }
8358 else if (!mSSData->strStateFilePath.isEmpty())
8359 {
8360 /* no need to use setMachineState() during init() */
8361 mData->mMachineState = MachineState_Saved;
8362 }
8363
8364 // after loading settings, we are no longer different from the XML on disk
8365 mData->flModifications = 0;
8366
8367 return S_OK;
8368}
8369
8370/**
8371 * Recursively loads all snapshots starting from the given.
8372 *
8373 * @param aNode <Snapshot> node.
8374 * @param aCurSnapshotId Current snapshot ID from the settings file.
8375 * @param aParentSnapshot Parent snapshot.
8376 */
8377HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8378 const Guid &aCurSnapshotId,
8379 Snapshot *aParentSnapshot)
8380{
8381 AssertReturn(!isSnapshotMachine(), E_FAIL);
8382 AssertReturn(!isSessionMachine(), E_FAIL);
8383
8384 HRESULT rc = S_OK;
8385
8386 Utf8Str strStateFile;
8387 if (!data.strStateFile.isEmpty())
8388 {
8389 /* optional */
8390 strStateFile = data.strStateFile;
8391 int vrc = calculateFullPath(strStateFile, strStateFile);
8392 if (RT_FAILURE(vrc))
8393 return setError(E_FAIL,
8394 tr("Invalid saved state file path '%s' (%Rrc)"),
8395 strStateFile.c_str(),
8396 vrc);
8397 }
8398
8399 /* create a snapshot machine object */
8400 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8401 pSnapshotMachine.createObject();
8402 rc = pSnapshotMachine->initFromSettings(this,
8403 data.hardware,
8404 &data.debugging,
8405 &data.autostart,
8406 data.storage,
8407 data.uuid.ref(),
8408 strStateFile);
8409 if (FAILED(rc)) return rc;
8410
8411 /* create a snapshot object */
8412 ComObjPtr<Snapshot> pSnapshot;
8413 pSnapshot.createObject();
8414 /* initialize the snapshot */
8415 rc = pSnapshot->init(mParent, // VirtualBox object
8416 data.uuid,
8417 data.strName,
8418 data.strDescription,
8419 data.timestamp,
8420 pSnapshotMachine,
8421 aParentSnapshot);
8422 if (FAILED(rc)) return rc;
8423
8424 /* memorize the first snapshot if necessary */
8425 if (!mData->mFirstSnapshot)
8426 mData->mFirstSnapshot = pSnapshot;
8427
8428 /* memorize the current snapshot when appropriate */
8429 if ( !mData->mCurrentSnapshot
8430 && pSnapshot->getId() == aCurSnapshotId
8431 )
8432 mData->mCurrentSnapshot = pSnapshot;
8433
8434 // now create the children
8435 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8436 it != data.llChildSnapshots.end();
8437 ++it)
8438 {
8439 const settings::Snapshot &childData = *it;
8440 // recurse
8441 rc = loadSnapshot(childData,
8442 aCurSnapshotId,
8443 pSnapshot); // parent = the one we created above
8444 if (FAILED(rc)) return rc;
8445 }
8446
8447 return rc;
8448}
8449
8450/**
8451 * Loads settings into mHWData.
8452 *
8453 * @param data Reference to the hardware settings.
8454 * @param pDbg Pointer to the debugging settings.
8455 * @param pAutostart Pointer to the autostart settings.
8456 */
8457HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8458 const settings::Autostart *pAutostart)
8459{
8460 AssertReturn(!isSessionMachine(), E_FAIL);
8461
8462 HRESULT rc = S_OK;
8463
8464 try
8465 {
8466 /* The hardware version attribute (optional). */
8467 mHWData->mHWVersion = data.strVersion;
8468 mHWData->mHardwareUUID = data.uuid;
8469
8470 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8471 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8472 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8473 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8474 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8475 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8476 mHWData->mPAEEnabled = data.fPAE;
8477 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8478
8479 mHWData->mCPUCount = data.cCPUs;
8480 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8481 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8482
8483 // cpu
8484 if (mHWData->mCPUHotPlugEnabled)
8485 {
8486 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8487 it != data.llCpus.end();
8488 ++it)
8489 {
8490 const settings::Cpu &cpu = *it;
8491
8492 mHWData->mCPUAttached[cpu.ulId] = true;
8493 }
8494 }
8495
8496 // cpuid leafs
8497 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8498 it != data.llCpuIdLeafs.end();
8499 ++it)
8500 {
8501 const settings::CpuIdLeaf &leaf = *it;
8502
8503 switch (leaf.ulId)
8504 {
8505 case 0x0:
8506 case 0x1:
8507 case 0x2:
8508 case 0x3:
8509 case 0x4:
8510 case 0x5:
8511 case 0x6:
8512 case 0x7:
8513 case 0x8:
8514 case 0x9:
8515 case 0xA:
8516 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8517 break;
8518
8519 case 0x80000000:
8520 case 0x80000001:
8521 case 0x80000002:
8522 case 0x80000003:
8523 case 0x80000004:
8524 case 0x80000005:
8525 case 0x80000006:
8526 case 0x80000007:
8527 case 0x80000008:
8528 case 0x80000009:
8529 case 0x8000000A:
8530 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8531 break;
8532
8533 default:
8534 /* just ignore */
8535 break;
8536 }
8537 }
8538
8539 mHWData->mMemorySize = data.ulMemorySizeMB;
8540 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8541
8542 // boot order
8543 for (size_t i = 0;
8544 i < RT_ELEMENTS(mHWData->mBootOrder);
8545 i++)
8546 {
8547 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8548 if (it == data.mapBootOrder.end())
8549 mHWData->mBootOrder[i] = DeviceType_Null;
8550 else
8551 mHWData->mBootOrder[i] = it->second;
8552 }
8553
8554 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8555 mHWData->mMonitorCount = data.cMonitors;
8556 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8557 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8558 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8559 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8560 mHWData->mVideoCaptureEnabled = false; /* @todo r=klaus restore to data.fVideoCaptureEnabled */
8561 mHWData->mVideoCaptureFile = data.strVideoCaptureFile;
8562 mHWData->mFirmwareType = data.firmwareType;
8563 mHWData->mPointingHIDType = data.pointingHIDType;
8564 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8565 mHWData->mChipsetType = data.chipsetType;
8566 mHWData->mEmulatedUSBWebcamEnabled = data.fEmulatedUSBWebcam;
8567 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8568 mHWData->mHPETEnabled = data.fHPETEnabled;
8569
8570 /* VRDEServer */
8571 rc = mVRDEServer->loadSettings(data.vrdeSettings);
8572 if (FAILED(rc)) return rc;
8573
8574 /* BIOS */
8575 rc = mBIOSSettings->loadSettings(data.biosSettings);
8576 if (FAILED(rc)) return rc;
8577
8578 // Bandwidth control (must come before network adapters)
8579 rc = mBandwidthControl->loadSettings(data.ioSettings);
8580 if (FAILED(rc)) return rc;
8581
8582 /* USB Controller */
8583 rc = mUSBController->loadSettings(data.usbController);
8584 if (FAILED(rc)) return rc;
8585
8586 // network adapters
8587 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8588 uint32_t oldCount = mNetworkAdapters.size();
8589 if (newCount > oldCount)
8590 {
8591 mNetworkAdapters.resize(newCount);
8592 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
8593 {
8594 unconst(mNetworkAdapters[slot]).createObject();
8595 mNetworkAdapters[slot]->init(this, slot);
8596 }
8597 }
8598 else if (newCount < oldCount)
8599 mNetworkAdapters.resize(newCount);
8600 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8601 it != data.llNetworkAdapters.end();
8602 ++it)
8603 {
8604 const settings::NetworkAdapter &nic = *it;
8605
8606 /* slot unicity is guaranteed by XML Schema */
8607 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8608 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
8609 if (FAILED(rc)) return rc;
8610 }
8611
8612 // serial ports
8613 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8614 it != data.llSerialPorts.end();
8615 ++it)
8616 {
8617 const settings::SerialPort &s = *it;
8618
8619 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8620 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
8621 if (FAILED(rc)) return rc;
8622 }
8623
8624 // parallel ports (optional)
8625 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8626 it != data.llParallelPorts.end();
8627 ++it)
8628 {
8629 const settings::ParallelPort &p = *it;
8630
8631 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8632 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
8633 if (FAILED(rc)) return rc;
8634 }
8635
8636 /* AudioAdapter */
8637 rc = mAudioAdapter->loadSettings(data.audioAdapter);
8638 if (FAILED(rc)) return rc;
8639
8640 /* Shared folders */
8641 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8642 it != data.llSharedFolders.end();
8643 ++it)
8644 {
8645 const settings::SharedFolder &sf = *it;
8646
8647 ComObjPtr<SharedFolder> sharedFolder;
8648 /* Check for double entries. Not allowed! */
8649 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8650 if (SUCCEEDED(rc))
8651 return setError(VBOX_E_OBJECT_IN_USE,
8652 tr("Shared folder named '%s' already exists"),
8653 sf.strName.c_str());
8654
8655 /* Create the new shared folder. Don't break on error. This will be
8656 * reported when the machine starts. */
8657 sharedFolder.createObject();
8658 rc = sharedFolder->init(getMachine(),
8659 sf.strName,
8660 sf.strHostPath,
8661 RT_BOOL(sf.fWritable),
8662 RT_BOOL(sf.fAutoMount),
8663 false /* fFailOnError */);
8664 if (FAILED(rc)) return rc;
8665 mHWData->mSharedFolders.push_back(sharedFolder);
8666 }
8667
8668 // Clipboard
8669 mHWData->mClipboardMode = data.clipboardMode;
8670
8671 // drag'n'drop
8672 mHWData->mDragAndDropMode = data.dragAndDropMode;
8673
8674 // guest settings
8675 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8676
8677 // IO settings
8678 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8679 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8680
8681 // Host PCI devices
8682 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8683 it != data.pciAttachments.end();
8684 ++it)
8685 {
8686 const settings::HostPCIDeviceAttachment &hpda = *it;
8687 ComObjPtr<PCIDeviceAttachment> pda;
8688
8689 pda.createObject();
8690 pda->loadSettings(this, hpda);
8691 mHWData->mPCIDeviceAssignments.push_back(pda);
8692 }
8693
8694 /*
8695 * (The following isn't really real hardware, but it lives in HWData
8696 * for reasons of convenience.)
8697 */
8698
8699#ifdef VBOX_WITH_GUEST_PROPS
8700 /* Guest properties (optional) */
8701 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8702 it != data.llGuestProperties.end();
8703 ++it)
8704 {
8705 const settings::GuestProperty &prop = *it;
8706 uint32_t fFlags = guestProp::NILFLAG;
8707 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8708 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8709 mHWData->mGuestProperties[prop.strName] = property;
8710 }
8711
8712 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8713#endif /* VBOX_WITH_GUEST_PROPS defined */
8714
8715 rc = loadDebugging(pDbg);
8716 if (FAILED(rc))
8717 return rc;
8718
8719 mHWData->mAutostart = *pAutostart;
8720 }
8721 catch(std::bad_alloc &)
8722 {
8723 return E_OUTOFMEMORY;
8724 }
8725
8726 AssertComRC(rc);
8727 return rc;
8728}
8729
8730/**
8731 * Called from Machine::loadHardware() to load the debugging settings of the
8732 * machine.
8733 *
8734 * @param pDbg Pointer to the settings.
8735 */
8736HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
8737{
8738 mHWData->mDebugging = *pDbg;
8739 /* no more processing currently required, this will probably change. */
8740 return S_OK;
8741}
8742
8743/**
8744 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
8745 *
8746 * @param data
8747 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8748 * @param puuidSnapshot
8749 * @return
8750 */
8751HRESULT Machine::loadStorageControllers(const settings::Storage &data,
8752 const Guid *puuidRegistry,
8753 const Guid *puuidSnapshot)
8754{
8755 AssertReturn(!isSessionMachine(), E_FAIL);
8756
8757 HRESULT rc = S_OK;
8758
8759 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8760 it != data.llStorageControllers.end();
8761 ++it)
8762 {
8763 const settings::StorageController &ctlData = *it;
8764
8765 ComObjPtr<StorageController> pCtl;
8766 /* Try to find one with the name first. */
8767 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8768 if (SUCCEEDED(rc))
8769 return setError(VBOX_E_OBJECT_IN_USE,
8770 tr("Storage controller named '%s' already exists"),
8771 ctlData.strName.c_str());
8772
8773 pCtl.createObject();
8774 rc = pCtl->init(this,
8775 ctlData.strName,
8776 ctlData.storageBus,
8777 ctlData.ulInstance,
8778 ctlData.fBootable);
8779 if (FAILED(rc)) return rc;
8780
8781 mStorageControllers->push_back(pCtl);
8782
8783 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8784 if (FAILED(rc)) return rc;
8785
8786 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8787 if (FAILED(rc)) return rc;
8788
8789 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8790 if (FAILED(rc)) return rc;
8791
8792 /* Set IDE emulation settings (only for AHCI controller). */
8793 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8794 {
8795 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8796 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8797 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8798 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8799 )
8800 return rc;
8801 }
8802
8803 /* Load the attached devices now. */
8804 rc = loadStorageDevices(pCtl,
8805 ctlData,
8806 puuidRegistry,
8807 puuidSnapshot);
8808 if (FAILED(rc)) return rc;
8809 }
8810
8811 return S_OK;
8812}
8813
8814/**
8815 * Called from loadStorageControllers for a controller's devices.
8816 *
8817 * @param aStorageController
8818 * @param data
8819 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8820 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
8821 * @return
8822 */
8823HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
8824 const settings::StorageController &data,
8825 const Guid *puuidRegistry,
8826 const Guid *puuidSnapshot)
8827{
8828 HRESULT rc = S_OK;
8829
8830 /* paranoia: detect duplicate attachments */
8831 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8832 it != data.llAttachedDevices.end();
8833 ++it)
8834 {
8835 const settings::AttachedDevice &ad = *it;
8836
8837 for (settings::AttachedDevicesList::const_iterator it2 = it;
8838 it2 != data.llAttachedDevices.end();
8839 ++it2)
8840 {
8841 if (it == it2)
8842 continue;
8843
8844 const settings::AttachedDevice &ad2 = *it2;
8845
8846 if ( ad.lPort == ad2.lPort
8847 && ad.lDevice == ad2.lDevice)
8848 {
8849 return setError(E_FAIL,
8850 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
8851 aStorageController->getName().c_str(),
8852 ad.lPort,
8853 ad.lDevice,
8854 mUserData->s.strName.c_str());
8855 }
8856 }
8857 }
8858
8859 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8860 it != data.llAttachedDevices.end();
8861 ++it)
8862 {
8863 const settings::AttachedDevice &dev = *it;
8864 ComObjPtr<Medium> medium;
8865
8866 switch (dev.deviceType)
8867 {
8868 case DeviceType_Floppy:
8869 case DeviceType_DVD:
8870 if (dev.strHostDriveSrc.isNotEmpty())
8871 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
8872 else
8873 rc = mParent->findRemoveableMedium(dev.deviceType,
8874 dev.uuid,
8875 false /* fRefresh */,
8876 false /* aSetError */,
8877 medium);
8878 if (rc == VBOX_E_OBJECT_NOT_FOUND)
8879 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
8880 rc = S_OK;
8881 break;
8882
8883 case DeviceType_HardDisk:
8884 {
8885 /* find a hard disk by UUID */
8886 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
8887 if (FAILED(rc))
8888 {
8889 if (isSnapshotMachine())
8890 {
8891 // wrap another error message around the "cannot find hard disk" set by findHardDisk
8892 // so the user knows that the bad disk is in a snapshot somewhere
8893 com::ErrorInfo info;
8894 return setError(E_FAIL,
8895 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
8896 puuidSnapshot->raw(),
8897 info.getText().raw());
8898 }
8899 else
8900 return rc;
8901 }
8902
8903 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
8904
8905 if (medium->getType() == MediumType_Immutable)
8906 {
8907 if (isSnapshotMachine())
8908 return setError(E_FAIL,
8909 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8910 "of the virtual machine '%s' ('%s')"),
8911 medium->getLocationFull().c_str(),
8912 dev.uuid.raw(),
8913 puuidSnapshot->raw(),
8914 mUserData->s.strName.c_str(),
8915 mData->m_strConfigFileFull.c_str());
8916
8917 return setError(E_FAIL,
8918 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8919 medium->getLocationFull().c_str(),
8920 dev.uuid.raw(),
8921 mUserData->s.strName.c_str(),
8922 mData->m_strConfigFileFull.c_str());
8923 }
8924
8925 if (medium->getType() == MediumType_MultiAttach)
8926 {
8927 if (isSnapshotMachine())
8928 return setError(E_FAIL,
8929 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8930 "of the virtual machine '%s' ('%s')"),
8931 medium->getLocationFull().c_str(),
8932 dev.uuid.raw(),
8933 puuidSnapshot->raw(),
8934 mUserData->s.strName.c_str(),
8935 mData->m_strConfigFileFull.c_str());
8936
8937 return setError(E_FAIL,
8938 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8939 medium->getLocationFull().c_str(),
8940 dev.uuid.raw(),
8941 mUserData->s.strName.c_str(),
8942 mData->m_strConfigFileFull.c_str());
8943 }
8944
8945 if ( !isSnapshotMachine()
8946 && medium->getChildren().size() != 0
8947 )
8948 return setError(E_FAIL,
8949 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
8950 "because it has %d differencing child hard disks"),
8951 medium->getLocationFull().c_str(),
8952 dev.uuid.raw(),
8953 mUserData->s.strName.c_str(),
8954 mData->m_strConfigFileFull.c_str(),
8955 medium->getChildren().size());
8956
8957 if (findAttachment(mMediaData->mAttachments,
8958 medium))
8959 return setError(E_FAIL,
8960 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
8961 medium->getLocationFull().c_str(),
8962 dev.uuid.raw(),
8963 mUserData->s.strName.c_str(),
8964 mData->m_strConfigFileFull.c_str());
8965
8966 break;
8967 }
8968
8969 default:
8970 return setError(E_FAIL,
8971 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
8972 medium->getLocationFull().c_str(),
8973 mUserData->s.strName.c_str(),
8974 mData->m_strConfigFileFull.c_str());
8975 }
8976
8977 if (FAILED(rc))
8978 break;
8979
8980 /* Bandwidth groups are loaded at this point. */
8981 ComObjPtr<BandwidthGroup> pBwGroup;
8982
8983 if (!dev.strBwGroup.isEmpty())
8984 {
8985 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
8986 if (FAILED(rc))
8987 return setError(E_FAIL,
8988 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
8989 medium->getLocationFull().c_str(),
8990 dev.strBwGroup.c_str(),
8991 mUserData->s.strName.c_str(),
8992 mData->m_strConfigFileFull.c_str());
8993 pBwGroup->reference();
8994 }
8995
8996 const Bstr controllerName = aStorageController->getName();
8997 ComObjPtr<MediumAttachment> pAttachment;
8998 pAttachment.createObject();
8999 rc = pAttachment->init(this,
9000 medium,
9001 controllerName,
9002 dev.lPort,
9003 dev.lDevice,
9004 dev.deviceType,
9005 false,
9006 dev.fPassThrough,
9007 dev.fTempEject,
9008 dev.fNonRotational,
9009 dev.fDiscard,
9010 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
9011 if (FAILED(rc)) break;
9012
9013 /* associate the medium with this machine and snapshot */
9014 if (!medium.isNull())
9015 {
9016 AutoCaller medCaller(medium);
9017 if (FAILED(medCaller.rc())) return medCaller.rc();
9018 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9019
9020 if (isSnapshotMachine())
9021 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
9022 else
9023 rc = medium->addBackReference(mData->mUuid);
9024 /* If the medium->addBackReference fails it sets an appropriate
9025 * error message, so no need to do any guesswork here. */
9026
9027 if (puuidRegistry)
9028 // caller wants registry ID to be set on all attached media (OVF import case)
9029 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9030 }
9031
9032 if (FAILED(rc))
9033 break;
9034
9035 /* back up mMediaData to let registeredInit() properly rollback on failure
9036 * (= limited accessibility) */
9037 setModified(IsModified_Storage);
9038 mMediaData.backup();
9039 mMediaData->mAttachments.push_back(pAttachment);
9040 }
9041
9042 return rc;
9043}
9044
9045/**
9046 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9047 *
9048 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9049 * @param aSnapshot where to return the found snapshot
9050 * @param aSetError true to set extended error info on failure
9051 */
9052HRESULT Machine::findSnapshotById(const Guid &aId,
9053 ComObjPtr<Snapshot> &aSnapshot,
9054 bool aSetError /* = false */)
9055{
9056 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9057
9058 if (!mData->mFirstSnapshot)
9059 {
9060 if (aSetError)
9061 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9062 return E_FAIL;
9063 }
9064
9065 if (aId.isZero())
9066 aSnapshot = mData->mFirstSnapshot;
9067 else
9068 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9069
9070 if (!aSnapshot)
9071 {
9072 if (aSetError)
9073 return setError(E_FAIL,
9074 tr("Could not find a snapshot with UUID {%s}"),
9075 aId.toString().c_str());
9076 return E_FAIL;
9077 }
9078
9079 return S_OK;
9080}
9081
9082/**
9083 * Returns the snapshot with the given name or fails of no such snapshot.
9084 *
9085 * @param aName snapshot name to find
9086 * @param aSnapshot where to return the found snapshot
9087 * @param aSetError true to set extended error info on failure
9088 */
9089HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9090 ComObjPtr<Snapshot> &aSnapshot,
9091 bool aSetError /* = false */)
9092{
9093 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9094
9095 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9096
9097 if (!mData->mFirstSnapshot)
9098 {
9099 if (aSetError)
9100 return setError(VBOX_E_OBJECT_NOT_FOUND,
9101 tr("This machine does not have any snapshots"));
9102 return VBOX_E_OBJECT_NOT_FOUND;
9103 }
9104
9105 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9106
9107 if (!aSnapshot)
9108 {
9109 if (aSetError)
9110 return setError(VBOX_E_OBJECT_NOT_FOUND,
9111 tr("Could not find a snapshot named '%s'"), strName.c_str());
9112 return VBOX_E_OBJECT_NOT_FOUND;
9113 }
9114
9115 return S_OK;
9116}
9117
9118/**
9119 * Returns a storage controller object with the given name.
9120 *
9121 * @param aName storage controller name to find
9122 * @param aStorageController where to return the found storage controller
9123 * @param aSetError true to set extended error info on failure
9124 */
9125HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9126 ComObjPtr<StorageController> &aStorageController,
9127 bool aSetError /* = false */)
9128{
9129 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9130
9131 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9132 it != mStorageControllers->end();
9133 ++it)
9134 {
9135 if ((*it)->getName() == aName)
9136 {
9137 aStorageController = (*it);
9138 return S_OK;
9139 }
9140 }
9141
9142 if (aSetError)
9143 return setError(VBOX_E_OBJECT_NOT_FOUND,
9144 tr("Could not find a storage controller named '%s'"),
9145 aName.c_str());
9146 return VBOX_E_OBJECT_NOT_FOUND;
9147}
9148
9149HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9150 MediaData::AttachmentList &atts)
9151{
9152 AutoCaller autoCaller(this);
9153 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9154
9155 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9156
9157 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9158 it != mMediaData->mAttachments.end();
9159 ++it)
9160 {
9161 const ComObjPtr<MediumAttachment> &pAtt = *it;
9162
9163 // should never happen, but deal with NULL pointers in the list.
9164 AssertStmt(!pAtt.isNull(), continue);
9165
9166 // getControllerName() needs caller+read lock
9167 AutoCaller autoAttCaller(pAtt);
9168 if (FAILED(autoAttCaller.rc()))
9169 {
9170 atts.clear();
9171 return autoAttCaller.rc();
9172 }
9173 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9174
9175 if (pAtt->getControllerName() == aName)
9176 atts.push_back(pAtt);
9177 }
9178
9179 return S_OK;
9180}
9181
9182/**
9183 * Helper for #saveSettings. Cares about renaming the settings directory and
9184 * file if the machine name was changed and about creating a new settings file
9185 * if this is a new machine.
9186 *
9187 * @note Must be never called directly but only from #saveSettings().
9188 */
9189HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9190{
9191 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9192
9193 HRESULT rc = S_OK;
9194
9195 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9196
9197 /// @todo need to handle primary group change, too
9198
9199 /* attempt to rename the settings file if machine name is changed */
9200 if ( mUserData->s.fNameSync
9201 && mUserData.isBackedUp()
9202 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9203 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9204 )
9205 {
9206 bool dirRenamed = false;
9207 bool fileRenamed = false;
9208
9209 Utf8Str configFile, newConfigFile;
9210 Utf8Str configFilePrev, newConfigFilePrev;
9211 Utf8Str configDir, newConfigDir;
9212
9213 do
9214 {
9215 int vrc = VINF_SUCCESS;
9216
9217 Utf8Str name = mUserData.backedUpData()->s.strName;
9218 Utf8Str newName = mUserData->s.strName;
9219 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9220 if (group == "/")
9221 group.setNull();
9222 Utf8Str newGroup = mUserData->s.llGroups.front();
9223 if (newGroup == "/")
9224 newGroup.setNull();
9225
9226 configFile = mData->m_strConfigFileFull;
9227
9228 /* first, rename the directory if it matches the group and machine name */
9229 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9230 group.c_str(), RTPATH_DELIMITER, name.c_str());
9231 /** @todo hack, make somehow use of ComposeMachineFilename */
9232 if (mUserData->s.fDirectoryIncludesUUID)
9233 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9234 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9235 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9236 /** @todo hack, make somehow use of ComposeMachineFilename */
9237 if (mUserData->s.fDirectoryIncludesUUID)
9238 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9239 configDir = configFile;
9240 configDir.stripFilename();
9241 newConfigDir = configDir;
9242 if ( configDir.length() >= groupPlusName.length()
9243 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9244 {
9245 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9246 Utf8Str newConfigBaseDir(newConfigDir);
9247 newConfigDir.append(newGroupPlusName);
9248 /* consistency: use \ if appropriate on the platform */
9249 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9250 /* new dir and old dir cannot be equal here because of 'if'
9251 * above and because name != newName */
9252 Assert(configDir != newConfigDir);
9253 if (!fSettingsFileIsNew)
9254 {
9255 /* perform real rename only if the machine is not new */
9256 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9257 if ( vrc == VERR_FILE_NOT_FOUND
9258 || vrc == VERR_PATH_NOT_FOUND)
9259 {
9260 /* create the parent directory, then retry renaming */
9261 Utf8Str parent(newConfigDir);
9262 parent.stripFilename();
9263 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9264 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9265 }
9266 if (RT_FAILURE(vrc))
9267 {
9268 rc = setError(E_FAIL,
9269 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9270 configDir.c_str(),
9271 newConfigDir.c_str(),
9272 vrc);
9273 break;
9274 }
9275 /* delete subdirectories which are no longer needed */
9276 Utf8Str dir(configDir);
9277 dir.stripFilename();
9278 while (dir != newConfigBaseDir && dir != ".")
9279 {
9280 vrc = RTDirRemove(dir.c_str());
9281 if (RT_FAILURE(vrc))
9282 break;
9283 dir.stripFilename();
9284 }
9285 dirRenamed = true;
9286 }
9287 }
9288
9289 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9290 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9291
9292 /* then try to rename the settings file itself */
9293 if (newConfigFile != configFile)
9294 {
9295 /* get the path to old settings file in renamed directory */
9296 configFile = Utf8StrFmt("%s%c%s",
9297 newConfigDir.c_str(),
9298 RTPATH_DELIMITER,
9299 RTPathFilename(configFile.c_str()));
9300 if (!fSettingsFileIsNew)
9301 {
9302 /* perform real rename only if the machine is not new */
9303 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9304 if (RT_FAILURE(vrc))
9305 {
9306 rc = setError(E_FAIL,
9307 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9308 configFile.c_str(),
9309 newConfigFile.c_str(),
9310 vrc);
9311 break;
9312 }
9313 fileRenamed = true;
9314 configFilePrev = configFile;
9315 configFilePrev += "-prev";
9316 newConfigFilePrev = newConfigFile;
9317 newConfigFilePrev += "-prev";
9318 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9319 }
9320 }
9321
9322 // update m_strConfigFileFull amd mConfigFile
9323 mData->m_strConfigFileFull = newConfigFile;
9324 // compute the relative path too
9325 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9326
9327 // store the old and new so that VirtualBox::saveSettings() can update
9328 // the media registry
9329 if ( mData->mRegistered
9330 && configDir != newConfigDir)
9331 {
9332 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9333
9334 if (pfNeedsGlobalSaveSettings)
9335 *pfNeedsGlobalSaveSettings = true;
9336 }
9337
9338 // in the saved state file path, replace the old directory with the new directory
9339 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9340 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9341
9342 // and do the same thing for the saved state file paths of all the online snapshots
9343 if (mData->mFirstSnapshot)
9344 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9345 newConfigDir.c_str());
9346 }
9347 while (0);
9348
9349 if (FAILED(rc))
9350 {
9351 /* silently try to rename everything back */
9352 if (fileRenamed)
9353 {
9354 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9355 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9356 }
9357 if (dirRenamed)
9358 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9359 }
9360
9361 if (FAILED(rc)) return rc;
9362 }
9363
9364 if (fSettingsFileIsNew)
9365 {
9366 /* create a virgin config file */
9367 int vrc = VINF_SUCCESS;
9368
9369 /* ensure the settings directory exists */
9370 Utf8Str path(mData->m_strConfigFileFull);
9371 path.stripFilename();
9372 if (!RTDirExists(path.c_str()))
9373 {
9374 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9375 if (RT_FAILURE(vrc))
9376 {
9377 return setError(E_FAIL,
9378 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9379 path.c_str(),
9380 vrc);
9381 }
9382 }
9383
9384 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9385 path = Utf8Str(mData->m_strConfigFileFull);
9386 RTFILE f = NIL_RTFILE;
9387 vrc = RTFileOpen(&f, path.c_str(),
9388 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9389 if (RT_FAILURE(vrc))
9390 return setError(E_FAIL,
9391 tr("Could not create the settings file '%s' (%Rrc)"),
9392 path.c_str(),
9393 vrc);
9394 RTFileClose(f);
9395 }
9396
9397 return rc;
9398}
9399
9400/**
9401 * Saves and commits machine data, user data and hardware data.
9402 *
9403 * Note that on failure, the data remains uncommitted.
9404 *
9405 * @a aFlags may combine the following flags:
9406 *
9407 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9408 * Used when saving settings after an operation that makes them 100%
9409 * correspond to the settings from the current snapshot.
9410 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9411 * #isReallyModified() returns false. This is necessary for cases when we
9412 * change machine data directly, not through the backup()/commit() mechanism.
9413 * - SaveS_Force: settings will be saved without doing a deep compare of the
9414 * settings structures. This is used when this is called because snapshots
9415 * have changed to avoid the overhead of the deep compare.
9416 *
9417 * @note Must be called from under this object's write lock. Locks children for
9418 * writing.
9419 *
9420 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9421 * initialized to false and that will be set to true by this function if
9422 * the caller must invoke VirtualBox::saveSettings() because the global
9423 * settings have changed. This will happen if a machine rename has been
9424 * saved and the global machine and media registries will therefore need
9425 * updating.
9426 */
9427HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9428 int aFlags /*= 0*/)
9429{
9430 LogFlowThisFuncEnter();
9431
9432 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9433
9434 /* make sure child objects are unable to modify the settings while we are
9435 * saving them */
9436 ensureNoStateDependencies();
9437
9438 AssertReturn(!isSnapshotMachine(),
9439 E_FAIL);
9440
9441 HRESULT rc = S_OK;
9442 bool fNeedsWrite = false;
9443
9444 /* First, prepare to save settings. It will care about renaming the
9445 * settings directory and file if the machine name was changed and about
9446 * creating a new settings file if this is a new machine. */
9447 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9448 if (FAILED(rc)) return rc;
9449
9450 // keep a pointer to the current settings structures
9451 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9452 settings::MachineConfigFile *pNewConfig = NULL;
9453
9454 try
9455 {
9456 // make a fresh one to have everyone write stuff into
9457 pNewConfig = new settings::MachineConfigFile(NULL);
9458 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9459
9460 // now go and copy all the settings data from COM to the settings structures
9461 // (this calles saveSettings() on all the COM objects in the machine)
9462 copyMachineDataToSettings(*pNewConfig);
9463
9464 if (aFlags & SaveS_ResetCurStateModified)
9465 {
9466 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9467 mData->mCurrentStateModified = FALSE;
9468 fNeedsWrite = true; // always, no need to compare
9469 }
9470 else if (aFlags & SaveS_Force)
9471 {
9472 fNeedsWrite = true; // always, no need to compare
9473 }
9474 else
9475 {
9476 if (!mData->mCurrentStateModified)
9477 {
9478 // do a deep compare of the settings that we just saved with the settings
9479 // previously stored in the config file; this invokes MachineConfigFile::operator==
9480 // which does a deep compare of all the settings, which is expensive but less expensive
9481 // than writing out XML in vain
9482 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9483
9484 // could still be modified if any settings changed
9485 mData->mCurrentStateModified = fAnySettingsChanged;
9486
9487 fNeedsWrite = fAnySettingsChanged;
9488 }
9489 else
9490 fNeedsWrite = true;
9491 }
9492
9493 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9494
9495 if (fNeedsWrite)
9496 // now spit it all out!
9497 pNewConfig->write(mData->m_strConfigFileFull);
9498
9499 mData->pMachineConfigFile = pNewConfig;
9500 delete pOldConfig;
9501 commit();
9502
9503 // after saving settings, we are no longer different from the XML on disk
9504 mData->flModifications = 0;
9505 }
9506 catch (HRESULT err)
9507 {
9508 // we assume that error info is set by the thrower
9509 rc = err;
9510
9511 // restore old config
9512 delete pNewConfig;
9513 mData->pMachineConfigFile = pOldConfig;
9514 }
9515 catch (...)
9516 {
9517 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9518 }
9519
9520 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9521 {
9522 /* Fire the data change event, even on failure (since we've already
9523 * committed all data). This is done only for SessionMachines because
9524 * mutable Machine instances are always not registered (i.e. private
9525 * to the client process that creates them) and thus don't need to
9526 * inform callbacks. */
9527 if (isSessionMachine())
9528 mParent->onMachineDataChange(mData->mUuid);
9529 }
9530
9531 LogFlowThisFunc(("rc=%08X\n", rc));
9532 LogFlowThisFuncLeave();
9533 return rc;
9534}
9535
9536/**
9537 * Implementation for saving the machine settings into the given
9538 * settings::MachineConfigFile instance. This copies machine extradata
9539 * from the previous machine config file in the instance data, if any.
9540 *
9541 * This gets called from two locations:
9542 *
9543 * -- Machine::saveSettings(), during the regular XML writing;
9544 *
9545 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9546 * exported to OVF and we write the VirtualBox proprietary XML
9547 * into a <vbox:Machine> tag.
9548 *
9549 * This routine fills all the fields in there, including snapshots, *except*
9550 * for the following:
9551 *
9552 * -- fCurrentStateModified. There is some special logic associated with that.
9553 *
9554 * The caller can then call MachineConfigFile::write() or do something else
9555 * with it.
9556 *
9557 * Caller must hold the machine lock!
9558 *
9559 * This throws XML errors and HRESULT, so the caller must have a catch block!
9560 */
9561void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
9562{
9563 // deep copy extradata
9564 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9565
9566 config.uuid = mData->mUuid;
9567
9568 // copy name, description, OS type, teleport, UTC etc.
9569 config.machineUserData = mUserData->s;
9570
9571 if ( mData->mMachineState == MachineState_Saved
9572 || mData->mMachineState == MachineState_Restoring
9573 // when deleting a snapshot we may or may not have a saved state in the current state,
9574 // so let's not assert here please
9575 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9576 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9577 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9578 && (!mSSData->strStateFilePath.isEmpty())
9579 )
9580 )
9581 {
9582 Assert(!mSSData->strStateFilePath.isEmpty());
9583 /* try to make the file name relative to the settings file dir */
9584 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9585 }
9586 else
9587 {
9588 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9589 config.strStateFile.setNull();
9590 }
9591
9592 if (mData->mCurrentSnapshot)
9593 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
9594 else
9595 config.uuidCurrentSnapshot.clear();
9596
9597 config.timeLastStateChange = mData->mLastStateChange;
9598 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9599 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9600
9601 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9602 if (FAILED(rc)) throw rc;
9603
9604 rc = saveStorageControllers(config.storageMachine);
9605 if (FAILED(rc)) throw rc;
9606
9607 // save machine's media registry if this is VirtualBox 4.0 or later
9608 if (config.canHaveOwnMediaRegistry())
9609 {
9610 // determine machine folder
9611 Utf8Str strMachineFolder = getSettingsFileFull();
9612 strMachineFolder.stripFilename();
9613 mParent->saveMediaRegistry(config.mediaRegistry,
9614 getId(), // only media with registry ID == machine UUID
9615 strMachineFolder);
9616 // this throws HRESULT
9617 }
9618
9619 // save snapshots
9620 rc = saveAllSnapshots(config);
9621 if (FAILED(rc)) throw rc;
9622}
9623
9624/**
9625 * Saves all snapshots of the machine into the given machine config file. Called
9626 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9627 * @param config
9628 * @return
9629 */
9630HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
9631{
9632 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9633
9634 HRESULT rc = S_OK;
9635
9636 try
9637 {
9638 config.llFirstSnapshot.clear();
9639
9640 if (mData->mFirstSnapshot)
9641 {
9642 settings::Snapshot snapNew;
9643 config.llFirstSnapshot.push_back(snapNew);
9644
9645 // get reference to the fresh copy of the snapshot on the list and
9646 // work on that copy directly to avoid excessive copying later
9647 settings::Snapshot &snap = config.llFirstSnapshot.front();
9648
9649 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
9650 if (FAILED(rc)) throw rc;
9651 }
9652
9653// if (mType == IsSessionMachine)
9654// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9655
9656 }
9657 catch (HRESULT err)
9658 {
9659 /* we assume that error info is set by the thrower */
9660 rc = err;
9661 }
9662 catch (...)
9663 {
9664 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9665 }
9666
9667 return rc;
9668}
9669
9670/**
9671 * Saves the VM hardware configuration. It is assumed that the
9672 * given node is empty.
9673 *
9674 * @param data Reference to the settings object for the hardware config.
9675 * @param pDbg Pointer to the settings object for the debugging config
9676 * which happens to live in mHWData.
9677 * @param pAutostart Pointer to the settings object for the autostart config
9678 * which happens to live in mHWData.
9679 */
9680HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9681 settings::Autostart *pAutostart)
9682{
9683 HRESULT rc = S_OK;
9684
9685 try
9686 {
9687 /* The hardware version attribute (optional).
9688 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9689 if ( mHWData->mHWVersion == "1"
9690 && mSSData->strStateFilePath.isEmpty()
9691 )
9692 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. */
9693
9694 data.strVersion = mHWData->mHWVersion;
9695 data.uuid = mHWData->mHardwareUUID;
9696
9697 // CPU
9698 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9699 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
9700 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9701 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9702 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9703 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9704 data.fPAE = !!mHWData->mPAEEnabled;
9705 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9706
9707 /* Standard and Extended CPUID leafs. */
9708 data.llCpuIdLeafs.clear();
9709 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
9710 {
9711 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9712 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9713 }
9714 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
9715 {
9716 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9717 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9718 }
9719
9720 data.cCPUs = mHWData->mCPUCount;
9721 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9722 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9723
9724 data.llCpus.clear();
9725 if (data.fCpuHotPlug)
9726 {
9727 for (unsigned idx = 0; idx < data.cCPUs; idx++)
9728 {
9729 if (mHWData->mCPUAttached[idx])
9730 {
9731 settings::Cpu cpu;
9732 cpu.ulId = idx;
9733 data.llCpus.push_back(cpu);
9734 }
9735 }
9736 }
9737
9738 // memory
9739 data.ulMemorySizeMB = mHWData->mMemorySize;
9740 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9741
9742 // firmware
9743 data.firmwareType = mHWData->mFirmwareType;
9744
9745 // HID
9746 data.pointingHIDType = mHWData->mPointingHIDType;
9747 data.keyboardHIDType = mHWData->mKeyboardHIDType;
9748
9749 // chipset
9750 data.chipsetType = mHWData->mChipsetType;
9751
9752 data.fEmulatedUSBWebcam = !!mHWData->mEmulatedUSBWebcamEnabled;
9753 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9754
9755 // HPET
9756 data.fHPETEnabled = !!mHWData->mHPETEnabled;
9757
9758 // boot order
9759 data.mapBootOrder.clear();
9760 for (size_t i = 0;
9761 i < RT_ELEMENTS(mHWData->mBootOrder);
9762 ++i)
9763 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9764
9765 // display
9766 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9767 data.cMonitors = mHWData->mMonitorCount;
9768 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9769 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9770 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
9771 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
9772 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
9773 data.strVideoCaptureFile = mHWData->mVideoCaptureFile;
9774
9775 /* VRDEServer settings (optional) */
9776 rc = mVRDEServer->saveSettings(data.vrdeSettings);
9777 if (FAILED(rc)) throw rc;
9778
9779 /* BIOS (required) */
9780 rc = mBIOSSettings->saveSettings(data.biosSettings);
9781 if (FAILED(rc)) throw rc;
9782
9783 /* USB Controller (required) */
9784 rc = mUSBController->saveSettings(data.usbController);
9785 if (FAILED(rc)) throw rc;
9786
9787 /* Network adapters (required) */
9788 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
9789 data.llNetworkAdapters.clear();
9790 /* Write out only the nominal number of network adapters for this
9791 * chipset type. Since Machine::commit() hasn't been called there
9792 * may be extra NIC settings in the vector. */
9793 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
9794 {
9795 settings::NetworkAdapter nic;
9796 nic.ulSlot = slot;
9797 /* paranoia check... must not be NULL, but must not crash either. */
9798 if (mNetworkAdapters[slot])
9799 {
9800 rc = mNetworkAdapters[slot]->saveSettings(nic);
9801 if (FAILED(rc)) throw rc;
9802
9803 data.llNetworkAdapters.push_back(nic);
9804 }
9805 }
9806
9807 /* Serial ports */
9808 data.llSerialPorts.clear();
9809 for (ULONG slot = 0;
9810 slot < RT_ELEMENTS(mSerialPorts);
9811 ++slot)
9812 {
9813 settings::SerialPort s;
9814 s.ulSlot = slot;
9815 rc = mSerialPorts[slot]->saveSettings(s);
9816 if (FAILED(rc)) return rc;
9817
9818 data.llSerialPorts.push_back(s);
9819 }
9820
9821 /* Parallel ports */
9822 data.llParallelPorts.clear();
9823 for (ULONG slot = 0;
9824 slot < RT_ELEMENTS(mParallelPorts);
9825 ++slot)
9826 {
9827 settings::ParallelPort p;
9828 p.ulSlot = slot;
9829 rc = mParallelPorts[slot]->saveSettings(p);
9830 if (FAILED(rc)) return rc;
9831
9832 data.llParallelPorts.push_back(p);
9833 }
9834
9835 /* Audio adapter */
9836 rc = mAudioAdapter->saveSettings(data.audioAdapter);
9837 if (FAILED(rc)) return rc;
9838
9839 /* Shared folders */
9840 data.llSharedFolders.clear();
9841 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
9842 it != mHWData->mSharedFolders.end();
9843 ++it)
9844 {
9845 SharedFolder *pSF = *it;
9846 AutoCaller sfCaller(pSF);
9847 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
9848 settings::SharedFolder sf;
9849 sf.strName = pSF->getName();
9850 sf.strHostPath = pSF->getHostPath();
9851 sf.fWritable = !!pSF->isWritable();
9852 sf.fAutoMount = !!pSF->isAutoMounted();
9853
9854 data.llSharedFolders.push_back(sf);
9855 }
9856
9857 // clipboard
9858 data.clipboardMode = mHWData->mClipboardMode;
9859
9860 // drag'n'drop
9861 data.dragAndDropMode = mHWData->mDragAndDropMode;
9862
9863 /* Guest */
9864 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
9865
9866 // IO settings
9867 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
9868 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
9869
9870 /* BandwidthControl (required) */
9871 rc = mBandwidthControl->saveSettings(data.ioSettings);
9872 if (FAILED(rc)) throw rc;
9873
9874 /* Host PCI devices */
9875 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
9876 it != mHWData->mPCIDeviceAssignments.end();
9877 ++it)
9878 {
9879 ComObjPtr<PCIDeviceAttachment> pda = *it;
9880 settings::HostPCIDeviceAttachment hpda;
9881
9882 rc = pda->saveSettings(hpda);
9883 if (FAILED(rc)) throw rc;
9884
9885 data.pciAttachments.push_back(hpda);
9886 }
9887
9888
9889 // guest properties
9890 data.llGuestProperties.clear();
9891#ifdef VBOX_WITH_GUEST_PROPS
9892 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
9893 it != mHWData->mGuestProperties.end();
9894 ++it)
9895 {
9896 HWData::GuestProperty property = it->second;
9897
9898 /* Remove transient guest properties at shutdown unless we
9899 * are saving state */
9900 if ( ( mData->mMachineState == MachineState_PoweredOff
9901 || mData->mMachineState == MachineState_Aborted
9902 || mData->mMachineState == MachineState_Teleported)
9903 && ( property.mFlags & guestProp::TRANSIENT
9904 || property.mFlags & guestProp::TRANSRESET))
9905 continue;
9906 settings::GuestProperty prop;
9907 prop.strName = it->first;
9908 prop.strValue = property.strValue;
9909 prop.timestamp = property.mTimestamp;
9910 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
9911 guestProp::writeFlags(property.mFlags, szFlags);
9912 prop.strFlags = szFlags;
9913
9914 data.llGuestProperties.push_back(prop);
9915 }
9916
9917 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
9918 /* I presume this doesn't require a backup(). */
9919 mData->mGuestPropertiesModified = FALSE;
9920#endif /* VBOX_WITH_GUEST_PROPS defined */
9921
9922 *pDbg = mHWData->mDebugging;
9923 *pAutostart = mHWData->mAutostart;
9924 }
9925 catch(std::bad_alloc &)
9926 {
9927 return E_OUTOFMEMORY;
9928 }
9929
9930 AssertComRC(rc);
9931 return rc;
9932}
9933
9934/**
9935 * Saves the storage controller configuration.
9936 *
9937 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
9938 */
9939HRESULT Machine::saveStorageControllers(settings::Storage &data)
9940{
9941 data.llStorageControllers.clear();
9942
9943 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9944 it != mStorageControllers->end();
9945 ++it)
9946 {
9947 HRESULT rc;
9948 ComObjPtr<StorageController> pCtl = *it;
9949
9950 settings::StorageController ctl;
9951 ctl.strName = pCtl->getName();
9952 ctl.controllerType = pCtl->getControllerType();
9953 ctl.storageBus = pCtl->getStorageBus();
9954 ctl.ulInstance = pCtl->getInstance();
9955 ctl.fBootable = pCtl->getBootable();
9956
9957 /* Save the port count. */
9958 ULONG portCount;
9959 rc = pCtl->COMGETTER(PortCount)(&portCount);
9960 ComAssertComRCRet(rc, rc);
9961 ctl.ulPortCount = portCount;
9962
9963 /* Save fUseHostIOCache */
9964 BOOL fUseHostIOCache;
9965 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
9966 ComAssertComRCRet(rc, rc);
9967 ctl.fUseHostIOCache = !!fUseHostIOCache;
9968
9969 /* Save IDE emulation settings. */
9970 if (ctl.controllerType == StorageControllerType_IntelAhci)
9971 {
9972 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
9973 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
9974 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
9975 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
9976 )
9977 ComAssertComRCRet(rc, rc);
9978 }
9979
9980 /* save the devices now. */
9981 rc = saveStorageDevices(pCtl, ctl);
9982 ComAssertComRCRet(rc, rc);
9983
9984 data.llStorageControllers.push_back(ctl);
9985 }
9986
9987 return S_OK;
9988}
9989
9990/**
9991 * Saves the hard disk configuration.
9992 */
9993HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
9994 settings::StorageController &data)
9995{
9996 MediaData::AttachmentList atts;
9997
9998 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
9999 if (FAILED(rc)) return rc;
10000
10001 data.llAttachedDevices.clear();
10002 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10003 it != atts.end();
10004 ++it)
10005 {
10006 settings::AttachedDevice dev;
10007
10008 MediumAttachment *pAttach = *it;
10009 Medium *pMedium = pAttach->getMedium();
10010
10011 dev.deviceType = pAttach->getType();
10012 dev.lPort = pAttach->getPort();
10013 dev.lDevice = pAttach->getDevice();
10014 if (pMedium)
10015 {
10016 if (pMedium->isHostDrive())
10017 dev.strHostDriveSrc = pMedium->getLocationFull();
10018 else
10019 dev.uuid = pMedium->getId();
10020 dev.fPassThrough = pAttach->getPassthrough();
10021 dev.fTempEject = pAttach->getTempEject();
10022 dev.fNonRotational = pAttach->getNonRotational();
10023 dev.fDiscard = pAttach->getDiscard();
10024 }
10025
10026 dev.strBwGroup = pAttach->getBandwidthGroup();
10027
10028 data.llAttachedDevices.push_back(dev);
10029 }
10030
10031 return S_OK;
10032}
10033
10034/**
10035 * Saves machine state settings as defined by aFlags
10036 * (SaveSTS_* values).
10037 *
10038 * @param aFlags Combination of SaveSTS_* flags.
10039 *
10040 * @note Locks objects for writing.
10041 */
10042HRESULT Machine::saveStateSettings(int aFlags)
10043{
10044 if (aFlags == 0)
10045 return S_OK;
10046
10047 AutoCaller autoCaller(this);
10048 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10049
10050 /* This object's write lock is also necessary to serialize file access
10051 * (prevent concurrent reads and writes) */
10052 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10053
10054 HRESULT rc = S_OK;
10055
10056 Assert(mData->pMachineConfigFile);
10057
10058 try
10059 {
10060 if (aFlags & SaveSTS_CurStateModified)
10061 mData->pMachineConfigFile->fCurrentStateModified = true;
10062
10063 if (aFlags & SaveSTS_StateFilePath)
10064 {
10065 if (!mSSData->strStateFilePath.isEmpty())
10066 /* try to make the file name relative to the settings file dir */
10067 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10068 else
10069 mData->pMachineConfigFile->strStateFile.setNull();
10070 }
10071
10072 if (aFlags & SaveSTS_StateTimeStamp)
10073 {
10074 Assert( mData->mMachineState != MachineState_Aborted
10075 || mSSData->strStateFilePath.isEmpty());
10076
10077 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10078
10079 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10080//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10081 }
10082
10083 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10084 }
10085 catch (...)
10086 {
10087 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10088 }
10089
10090 return rc;
10091}
10092
10093/**
10094 * Ensures that the given medium is added to a media registry. If this machine
10095 * was created with 4.0 or later, then the machine registry is used. Otherwise
10096 * the global VirtualBox media registry is used.
10097 *
10098 * Caller must NOT hold machine lock, media tree or any medium locks!
10099 *
10100 * @param pMedium
10101 */
10102void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10103{
10104 /* Paranoia checks: do not hold machine or media tree locks. */
10105 AssertReturnVoid(!isWriteLockOnCurrentThread());
10106 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10107
10108 ComObjPtr<Medium> pBase;
10109 {
10110 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10111 pBase = pMedium->getBase();
10112 }
10113
10114 /* Paranoia checks: do not hold medium locks. */
10115 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10116 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10117
10118 // decide which medium registry to use now that the medium is attached:
10119 Guid uuid;
10120 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10121 // machine XML is VirtualBox 4.0 or higher:
10122 uuid = getId(); // machine UUID
10123 else
10124 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10125
10126 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10127 mParent->markRegistryModified(uuid);
10128
10129 /* For more complex hard disk structures it can happen that the base
10130 * medium isn't yet associated with any medium registry. Do that now. */
10131 if (pMedium != pBase)
10132 {
10133 if (pBase->addRegistry(uuid, true /* fRecurse */))
10134 mParent->markRegistryModified(uuid);
10135 }
10136}
10137
10138/**
10139 * Creates differencing hard disks for all normal hard disks attached to this
10140 * machine and a new set of attachments to refer to created disks.
10141 *
10142 * Used when taking a snapshot or when deleting the current state. Gets called
10143 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10144 *
10145 * This method assumes that mMediaData contains the original hard disk attachments
10146 * it needs to create diffs for. On success, these attachments will be replaced
10147 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10148 * called to delete created diffs which will also rollback mMediaData and restore
10149 * whatever was backed up before calling this method.
10150 *
10151 * Attachments with non-normal hard disks are left as is.
10152 *
10153 * If @a aOnline is @c false then the original hard disks that require implicit
10154 * diffs will be locked for reading. Otherwise it is assumed that they are
10155 * already locked for writing (when the VM was started). Note that in the latter
10156 * case it is responsibility of the caller to lock the newly created diffs for
10157 * writing if this method succeeds.
10158 *
10159 * @param aProgress Progress object to run (must contain at least as
10160 * many operations left as the number of hard disks
10161 * attached).
10162 * @param aOnline Whether the VM was online prior to this operation.
10163 *
10164 * @note The progress object is not marked as completed, neither on success nor
10165 * on failure. This is a responsibility of the caller.
10166 *
10167 * @note Locks this object and the media tree for writing.
10168 */
10169HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10170 ULONG aWeight,
10171 bool aOnline)
10172{
10173 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10174
10175 AutoCaller autoCaller(this);
10176 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10177
10178 AutoMultiWriteLock2 alock(this->lockHandle(),
10179 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10180
10181 /* must be in a protective state because we release the lock below */
10182 AssertReturn( mData->mMachineState == MachineState_Saving
10183 || mData->mMachineState == MachineState_LiveSnapshotting
10184 || mData->mMachineState == MachineState_RestoringSnapshot
10185 || mData->mMachineState == MachineState_DeletingSnapshot
10186 , E_FAIL);
10187
10188 HRESULT rc = S_OK;
10189
10190 // use appropriate locked media map (online or offline)
10191 MediumLockListMap lockedMediaOffline;
10192 MediumLockListMap *lockedMediaMap;
10193 if (aOnline)
10194 lockedMediaMap = &mData->mSession.mLockedMedia;
10195 else
10196 lockedMediaMap = &lockedMediaOffline;
10197
10198 try
10199 {
10200 if (!aOnline)
10201 {
10202 /* lock all attached hard disks early to detect "in use"
10203 * situations before creating actual diffs */
10204 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10205 it != mMediaData->mAttachments.end();
10206 ++it)
10207 {
10208 MediumAttachment* pAtt = *it;
10209 if (pAtt->getType() == DeviceType_HardDisk)
10210 {
10211 Medium* pMedium = pAtt->getMedium();
10212 Assert(pMedium);
10213
10214 MediumLockList *pMediumLockList(new MediumLockList());
10215 alock.release();
10216 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10217 false /* fMediumLockWrite */,
10218 NULL,
10219 *pMediumLockList);
10220 alock.acquire();
10221 if (FAILED(rc))
10222 {
10223 delete pMediumLockList;
10224 throw rc;
10225 }
10226 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10227 if (FAILED(rc))
10228 {
10229 throw setError(rc,
10230 tr("Collecting locking information for all attached media failed"));
10231 }
10232 }
10233 }
10234
10235 /* Now lock all media. If this fails, nothing is locked. */
10236 alock.release();
10237 rc = lockedMediaMap->Lock();
10238 alock.acquire();
10239 if (FAILED(rc))
10240 {
10241 throw setError(rc,
10242 tr("Locking of attached media failed"));
10243 }
10244 }
10245
10246 /* remember the current list (note that we don't use backup() since
10247 * mMediaData may be already backed up) */
10248 MediaData::AttachmentList atts = mMediaData->mAttachments;
10249
10250 /* start from scratch */
10251 mMediaData->mAttachments.clear();
10252
10253 /* go through remembered attachments and create diffs for normal hard
10254 * disks and attach them */
10255 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10256 it != atts.end();
10257 ++it)
10258 {
10259 MediumAttachment* pAtt = *it;
10260
10261 DeviceType_T devType = pAtt->getType();
10262 Medium* pMedium = pAtt->getMedium();
10263
10264 if ( devType != DeviceType_HardDisk
10265 || pMedium == NULL
10266 || pMedium->getType() != MediumType_Normal)
10267 {
10268 /* copy the attachment as is */
10269
10270 /** @todo the progress object created in Console::TakeSnaphot
10271 * only expects operations for hard disks. Later other
10272 * device types need to show up in the progress as well. */
10273 if (devType == DeviceType_HardDisk)
10274 {
10275 if (pMedium == NULL)
10276 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10277 aWeight); // weight
10278 else
10279 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10280 pMedium->getBase()->getName().c_str()).raw(),
10281 aWeight); // weight
10282 }
10283
10284 mMediaData->mAttachments.push_back(pAtt);
10285 continue;
10286 }
10287
10288 /* need a diff */
10289 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10290 pMedium->getBase()->getName().c_str()).raw(),
10291 aWeight); // weight
10292
10293 Utf8Str strFullSnapshotFolder;
10294 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10295
10296 ComObjPtr<Medium> diff;
10297 diff.createObject();
10298 // store the diff in the same registry as the parent
10299 // (this cannot fail here because we can't create implicit diffs for
10300 // unregistered images)
10301 Guid uuidRegistryParent;
10302 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10303 Assert(fInRegistry); NOREF(fInRegistry);
10304 rc = diff->init(mParent,
10305 pMedium->getPreferredDiffFormat(),
10306 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10307 uuidRegistryParent);
10308 if (FAILED(rc)) throw rc;
10309
10310 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10311 * the push_back? Looks like we're going to release medium with the
10312 * wrong kind of lock (general issue with if we fail anywhere at all)
10313 * and an orphaned VDI in the snapshots folder. */
10314
10315 /* update the appropriate lock list */
10316 MediumLockList *pMediumLockList;
10317 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10318 AssertComRCThrowRC(rc);
10319 if (aOnline)
10320 {
10321 alock.release();
10322 /* The currently attached medium will be read-only, change
10323 * the lock type to read. */
10324 rc = pMediumLockList->Update(pMedium, false);
10325 alock.acquire();
10326 AssertComRCThrowRC(rc);
10327 }
10328
10329 /* release the locks before the potentially lengthy operation */
10330 alock.release();
10331 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10332 pMediumLockList,
10333 NULL /* aProgress */,
10334 true /* aWait */);
10335 alock.acquire();
10336 if (FAILED(rc)) throw rc;
10337
10338 rc = lockedMediaMap->Unlock();
10339 AssertComRCThrowRC(rc);
10340 alock.release();
10341 rc = pMediumLockList->Append(diff, true);
10342 alock.acquire();
10343 AssertComRCThrowRC(rc);
10344 alock.release();
10345 rc = lockedMediaMap->Lock();
10346 alock.acquire();
10347 AssertComRCThrowRC(rc);
10348
10349 rc = diff->addBackReference(mData->mUuid);
10350 AssertComRCThrowRC(rc);
10351
10352 /* add a new attachment */
10353 ComObjPtr<MediumAttachment> attachment;
10354 attachment.createObject();
10355 rc = attachment->init(this,
10356 diff,
10357 pAtt->getControllerName(),
10358 pAtt->getPort(),
10359 pAtt->getDevice(),
10360 DeviceType_HardDisk,
10361 true /* aImplicit */,
10362 false /* aPassthrough */,
10363 false /* aTempEject */,
10364 pAtt->getNonRotational(),
10365 pAtt->getDiscard(),
10366 pAtt->getBandwidthGroup());
10367 if (FAILED(rc)) throw rc;
10368
10369 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10370 AssertComRCThrowRC(rc);
10371 mMediaData->mAttachments.push_back(attachment);
10372 }
10373 }
10374 catch (HRESULT aRC) { rc = aRC; }
10375
10376 /* unlock all hard disks we locked when there is no VM */
10377 if (!aOnline)
10378 {
10379 ErrorInfoKeeper eik;
10380
10381 HRESULT rc1 = lockedMediaMap->Clear();
10382 AssertComRC(rc1);
10383 }
10384
10385 return rc;
10386}
10387
10388/**
10389 * Deletes implicit differencing hard disks created either by
10390 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10391 *
10392 * Note that to delete hard disks created by #AttachDevice() this method is
10393 * called from #fixupMedia() when the changes are rolled back.
10394 *
10395 * @note Locks this object and the media tree for writing.
10396 */
10397HRESULT Machine::deleteImplicitDiffs(bool aOnline)
10398{
10399 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10400
10401 AutoCaller autoCaller(this);
10402 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10403
10404 AutoMultiWriteLock2 alock(this->lockHandle(),
10405 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10406
10407 /* We absolutely must have backed up state. */
10408 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10409
10410 /* Check if there are any implicitly created diff images. */
10411 bool fImplicitDiffs = false;
10412 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10413 it != mMediaData->mAttachments.end();
10414 ++it)
10415 {
10416 const ComObjPtr<MediumAttachment> &pAtt = *it;
10417 if (pAtt->isImplicit())
10418 {
10419 fImplicitDiffs = true;
10420 break;
10421 }
10422 }
10423 /* If there is nothing to do, leave early. This saves lots of image locking
10424 * effort. It also avoids a MachineStateChanged event without real reason.
10425 * This is important e.g. when loading a VM config, because there should be
10426 * no events. Otherwise API clients can become thoroughly confused for
10427 * inaccessible VMs (the code for loading VM configs uses this method for
10428 * cleanup if the config makes no sense), as they take such events as an
10429 * indication that the VM is alive, and they would force the VM config to
10430 * be reread, leading to an endless loop. */
10431 if (!fImplicitDiffs)
10432 return S_OK;
10433
10434 HRESULT rc = S_OK;
10435 MachineState_T oldState = mData->mMachineState;
10436
10437 /* will release the lock before the potentially lengthy operation,
10438 * so protect with the special state (unless already protected) */
10439 if ( oldState != MachineState_Saving
10440 && oldState != MachineState_LiveSnapshotting
10441 && oldState != MachineState_RestoringSnapshot
10442 && oldState != MachineState_DeletingSnapshot
10443 && oldState != MachineState_DeletingSnapshotOnline
10444 && oldState != MachineState_DeletingSnapshotPaused
10445 )
10446 setMachineState(MachineState_SettingUp);
10447
10448 // use appropriate locked media map (online or offline)
10449 MediumLockListMap lockedMediaOffline;
10450 MediumLockListMap *lockedMediaMap;
10451 if (aOnline)
10452 lockedMediaMap = &mData->mSession.mLockedMedia;
10453 else
10454 lockedMediaMap = &lockedMediaOffline;
10455
10456 try
10457 {
10458 if (!aOnline)
10459 {
10460 /* lock all attached hard disks early to detect "in use"
10461 * situations before deleting actual diffs */
10462 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10463 it != mMediaData->mAttachments.end();
10464 ++it)
10465 {
10466 MediumAttachment* pAtt = *it;
10467 if (pAtt->getType() == DeviceType_HardDisk)
10468 {
10469 Medium* pMedium = pAtt->getMedium();
10470 Assert(pMedium);
10471
10472 MediumLockList *pMediumLockList(new MediumLockList());
10473 alock.release();
10474 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10475 false /* fMediumLockWrite */,
10476 NULL,
10477 *pMediumLockList);
10478 alock.acquire();
10479
10480 if (FAILED(rc))
10481 {
10482 delete pMediumLockList;
10483 throw rc;
10484 }
10485
10486 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10487 if (FAILED(rc))
10488 throw rc;
10489 }
10490 }
10491
10492 if (FAILED(rc))
10493 throw rc;
10494 } // end of offline
10495
10496 /* Lock lists are now up to date and include implicitly created media */
10497
10498 /* Go through remembered attachments and delete all implicitly created
10499 * diffs and fix up the attachment information */
10500 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10501 MediaData::AttachmentList implicitAtts;
10502 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10503 it != mMediaData->mAttachments.end();
10504 ++it)
10505 {
10506 ComObjPtr<MediumAttachment> pAtt = *it;
10507 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10508 if (pMedium.isNull())
10509 continue;
10510
10511 // Implicit attachments go on the list for deletion and back references are removed.
10512 if (pAtt->isImplicit())
10513 {
10514 /* Deassociate and mark for deletion */
10515 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
10516 rc = pMedium->removeBackReference(mData->mUuid);
10517 if (FAILED(rc))
10518 throw rc;
10519 implicitAtts.push_back(pAtt);
10520 continue;
10521 }
10522
10523 /* Was this medium attached before? */
10524 if (!findAttachment(oldAtts, pMedium))
10525 {
10526 /* no: de-associate */
10527 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
10528 rc = pMedium->removeBackReference(mData->mUuid);
10529 if (FAILED(rc))
10530 throw rc;
10531 continue;
10532 }
10533 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
10534 }
10535
10536 /* If there are implicit attachments to delete, throw away the lock
10537 * map contents (which will unlock all media) since the medium
10538 * attachments will be rolled back. Below we need to completely
10539 * recreate the lock map anyway since it is infinitely complex to
10540 * do this incrementally (would need reconstructing each attachment
10541 * change, which would be extremely hairy). */
10542 if (implicitAtts.size() != 0)
10543 {
10544 ErrorInfoKeeper eik;
10545
10546 HRESULT rc1 = lockedMediaMap->Clear();
10547 AssertComRC(rc1);
10548 }
10549
10550 /* rollback hard disk changes */
10551 mMediaData.rollback();
10552
10553 MultiResult mrc(S_OK);
10554
10555 // Delete unused implicit diffs.
10556 if (implicitAtts.size() != 0)
10557 {
10558 alock.release();
10559
10560 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
10561 it != implicitAtts.end();
10562 ++it)
10563 {
10564 // Remove medium associated with this attachment.
10565 ComObjPtr<MediumAttachment> pAtt = *it;
10566 Assert(pAtt);
10567 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
10568 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10569 Assert(pMedium);
10570
10571 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10572 // continue on delete failure, just collect error messages
10573 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
10574 mrc = rc;
10575 }
10576
10577 alock.acquire();
10578
10579 /* if there is a VM recreate media lock map as mentioned above,
10580 * otherwise it is a waste of time and we leave things unlocked */
10581 if (aOnline)
10582 {
10583 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10584 /* must never be NULL, but better safe than sorry */
10585 if (!pMachine.isNull())
10586 {
10587 alock.release();
10588 rc = mData->mSession.mMachine->lockMedia();
10589 alock.acquire();
10590 if (FAILED(rc))
10591 throw rc;
10592 }
10593 }
10594 }
10595 }
10596 catch (HRESULT aRC) {rc = aRC;}
10597
10598 if (mData->mMachineState == MachineState_SettingUp)
10599 setMachineState(oldState);
10600
10601 /* unlock all hard disks we locked when there is no VM */
10602 if (!aOnline)
10603 {
10604 ErrorInfoKeeper eik;
10605
10606 HRESULT rc1 = lockedMediaMap->Clear();
10607 AssertComRC(rc1);
10608 }
10609
10610 return rc;
10611}
10612
10613
10614/**
10615 * Looks through the given list of media attachments for one with the given parameters
10616 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10617 * can be searched as well if needed.
10618 *
10619 * @param list
10620 * @param aControllerName
10621 * @param aControllerPort
10622 * @param aDevice
10623 * @return
10624 */
10625MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10626 IN_BSTR aControllerName,
10627 LONG aControllerPort,
10628 LONG aDevice)
10629{
10630 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10631 it != ll.end();
10632 ++it)
10633 {
10634 MediumAttachment *pAttach = *it;
10635 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
10636 return pAttach;
10637 }
10638
10639 return NULL;
10640}
10641
10642/**
10643 * Looks through the given list of media attachments for one with the given parameters
10644 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10645 * can be searched as well if needed.
10646 *
10647 * @param list
10648 * @param aControllerName
10649 * @param aControllerPort
10650 * @param aDevice
10651 * @return
10652 */
10653MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10654 ComObjPtr<Medium> pMedium)
10655{
10656 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10657 it != ll.end();
10658 ++it)
10659 {
10660 MediumAttachment *pAttach = *it;
10661 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10662 if (pMediumThis == pMedium)
10663 return pAttach;
10664 }
10665
10666 return NULL;
10667}
10668
10669/**
10670 * Looks through the given list of media attachments for one with the given parameters
10671 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10672 * can be searched as well if needed.
10673 *
10674 * @param list
10675 * @param aControllerName
10676 * @param aControllerPort
10677 * @param aDevice
10678 * @return
10679 */
10680MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10681 Guid &id)
10682{
10683 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10684 it != ll.end();
10685 ++it)
10686 {
10687 MediumAttachment *pAttach = *it;
10688 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10689 if (pMediumThis->getId() == id)
10690 return pAttach;
10691 }
10692
10693 return NULL;
10694}
10695
10696/**
10697 * Main implementation for Machine::DetachDevice. This also gets called
10698 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10699 *
10700 * @param pAttach Medium attachment to detach.
10701 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10702 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
10703 * @return
10704 */
10705HRESULT Machine::detachDevice(MediumAttachment *pAttach,
10706 AutoWriteLock &writeLock,
10707 Snapshot *pSnapshot)
10708{
10709 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
10710 DeviceType_T mediumType = pAttach->getType();
10711
10712 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
10713
10714 if (pAttach->isImplicit())
10715 {
10716 /* attempt to implicitly delete the implicitly created diff */
10717
10718 /// @todo move the implicit flag from MediumAttachment to Medium
10719 /// and forbid any hard disk operation when it is implicit. Or maybe
10720 /// a special media state for it to make it even more simple.
10721
10722 Assert(mMediaData.isBackedUp());
10723
10724 /* will release the lock before the potentially lengthy operation, so
10725 * protect with the special state */
10726 MachineState_T oldState = mData->mMachineState;
10727 setMachineState(MachineState_SettingUp);
10728
10729 writeLock.release();
10730
10731 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
10732 true /*aWait*/);
10733
10734 writeLock.acquire();
10735
10736 setMachineState(oldState);
10737
10738 if (FAILED(rc)) return rc;
10739 }
10740
10741 setModified(IsModified_Storage);
10742 mMediaData.backup();
10743 mMediaData->mAttachments.remove(pAttach);
10744
10745 if (!oldmedium.isNull())
10746 {
10747 // if this is from a snapshot, do not defer detachment to commitMedia()
10748 if (pSnapshot)
10749 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
10750 // else if non-hard disk media, do not defer detachment to commitMedia() either
10751 else if (mediumType != DeviceType_HardDisk)
10752 oldmedium->removeBackReference(mData->mUuid);
10753 }
10754
10755 return S_OK;
10756}
10757
10758/**
10759 * Goes thru all media of the given list and
10760 *
10761 * 1) calls detachDevice() on each of them for this machine and
10762 * 2) adds all Medium objects found in the process to the given list,
10763 * depending on cleanupMode.
10764 *
10765 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10766 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10767 * media to the list.
10768 *
10769 * This gets called from Machine::Unregister, both for the actual Machine and
10770 * the SnapshotMachine objects that might be found in the snapshots.
10771 *
10772 * Requires caller and locking. The machine lock must be passed in because it
10773 * will be passed on to detachDevice which needs it for temporary unlocking.
10774 *
10775 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
10776 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
10777 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
10778 * otherwise no media get added.
10779 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
10780 * @return
10781 */
10782HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
10783 Snapshot *pSnapshot,
10784 CleanupMode_T cleanupMode,
10785 MediaList &llMedia)
10786{
10787 Assert(isWriteLockOnCurrentThread());
10788
10789 HRESULT rc;
10790
10791 // make a temporary list because detachDevice invalidates iterators into
10792 // mMediaData->mAttachments
10793 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
10794
10795 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
10796 it != llAttachments2.end();
10797 ++it)
10798 {
10799 ComObjPtr<MediumAttachment> &pAttach = *it;
10800 ComObjPtr<Medium> pMedium = pAttach->getMedium();
10801
10802 if (!pMedium.isNull())
10803 {
10804 AutoCaller mac(pMedium);
10805 if (FAILED(mac.rc())) return mac.rc();
10806 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
10807 DeviceType_T devType = pMedium->getDeviceType();
10808 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
10809 && devType == DeviceType_HardDisk)
10810 || (cleanupMode == CleanupMode_Full)
10811 )
10812 {
10813 llMedia.push_back(pMedium);
10814 ComObjPtr<Medium> pParent = pMedium->getParent();
10815 /*
10816 * Search for medias which are not attached to any machine, but
10817 * in the chain to an attached disk. Mediums are only consided
10818 * if they are:
10819 * - have only one child
10820 * - no references to any machines
10821 * - are of normal medium type
10822 */
10823 while (!pParent.isNull())
10824 {
10825 AutoCaller mac1(pParent);
10826 if (FAILED(mac1.rc())) return mac1.rc();
10827 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
10828 if (pParent->getChildren().size() == 1)
10829 {
10830 if ( pParent->getMachineBackRefCount() == 0
10831 && pParent->getType() == MediumType_Normal
10832 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
10833 llMedia.push_back(pParent);
10834 }
10835 else
10836 break;
10837 pParent = pParent->getParent();
10838 }
10839 }
10840 }
10841
10842 // real machine: then we need to use the proper method
10843 rc = detachDevice(pAttach, writeLock, pSnapshot);
10844
10845 if (FAILED(rc))
10846 return rc;
10847 }
10848
10849 return S_OK;
10850}
10851
10852/**
10853 * Perform deferred hard disk detachments.
10854 *
10855 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10856 * backed up).
10857 *
10858 * If @a aOnline is @c true then this method will also unlock the old hard disks
10859 * for which the new implicit diffs were created and will lock these new diffs for
10860 * writing.
10861 *
10862 * @param aOnline Whether the VM was online prior to this operation.
10863 *
10864 * @note Locks this object for writing!
10865 */
10866void Machine::commitMedia(bool aOnline /*= false*/)
10867{
10868 AutoCaller autoCaller(this);
10869 AssertComRCReturnVoid(autoCaller.rc());
10870
10871 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10872
10873 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
10874
10875 HRESULT rc = S_OK;
10876
10877 /* no attach/detach operations -- nothing to do */
10878 if (!mMediaData.isBackedUp())
10879 return;
10880
10881 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10882 bool fMediaNeedsLocking = false;
10883
10884 /* enumerate new attachments */
10885 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10886 it != mMediaData->mAttachments.end();
10887 ++it)
10888 {
10889 MediumAttachment *pAttach = *it;
10890
10891 pAttach->commit();
10892
10893 Medium* pMedium = pAttach->getMedium();
10894 bool fImplicit = pAttach->isImplicit();
10895
10896 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
10897 (pMedium) ? pMedium->getName().c_str() : "NULL",
10898 fImplicit));
10899
10900 /** @todo convert all this Machine-based voodoo to MediumAttachment
10901 * based commit logic. */
10902 if (fImplicit)
10903 {
10904 /* convert implicit attachment to normal */
10905 pAttach->setImplicit(false);
10906
10907 if ( aOnline
10908 && pMedium
10909 && pAttach->getType() == DeviceType_HardDisk
10910 )
10911 {
10912 ComObjPtr<Medium> parent = pMedium->getParent();
10913 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
10914
10915 /* update the appropriate lock list */
10916 MediumLockList *pMediumLockList;
10917 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10918 AssertComRC(rc);
10919 if (pMediumLockList)
10920 {
10921 /* unlock if there's a need to change the locking */
10922 if (!fMediaNeedsLocking)
10923 {
10924 rc = mData->mSession.mLockedMedia.Unlock();
10925 AssertComRC(rc);
10926 fMediaNeedsLocking = true;
10927 }
10928 rc = pMediumLockList->Update(parent, false);
10929 AssertComRC(rc);
10930 rc = pMediumLockList->Append(pMedium, true);
10931 AssertComRC(rc);
10932 }
10933 }
10934
10935 continue;
10936 }
10937
10938 if (pMedium)
10939 {
10940 /* was this medium attached before? */
10941 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
10942 oldIt != oldAtts.end();
10943 ++oldIt)
10944 {
10945 MediumAttachment *pOldAttach = *oldIt;
10946 if (pOldAttach->getMedium() == pMedium)
10947 {
10948 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
10949
10950 /* yes: remove from old to avoid de-association */
10951 oldAtts.erase(oldIt);
10952 break;
10953 }
10954 }
10955 }
10956 }
10957
10958 /* enumerate remaining old attachments and de-associate from the
10959 * current machine state */
10960 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
10961 it != oldAtts.end();
10962 ++it)
10963 {
10964 MediumAttachment *pAttach = *it;
10965 Medium* pMedium = pAttach->getMedium();
10966
10967 /* Detach only hard disks, since DVD/floppy media is detached
10968 * instantly in MountMedium. */
10969 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
10970 {
10971 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
10972
10973 /* now de-associate from the current machine state */
10974 rc = pMedium->removeBackReference(mData->mUuid);
10975 AssertComRC(rc);
10976
10977 if (aOnline)
10978 {
10979 /* unlock since medium is not used anymore */
10980 MediumLockList *pMediumLockList;
10981 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10982 AssertComRC(rc);
10983 if (pMediumLockList)
10984 {
10985 rc = mData->mSession.mLockedMedia.Remove(pAttach);
10986 AssertComRC(rc);
10987 }
10988 }
10989 }
10990 }
10991
10992 /* take media locks again so that the locking state is consistent */
10993 if (fMediaNeedsLocking)
10994 {
10995 Assert(aOnline);
10996 rc = mData->mSession.mLockedMedia.Lock();
10997 AssertComRC(rc);
10998 }
10999
11000 /* commit the hard disk changes */
11001 mMediaData.commit();
11002
11003 if (isSessionMachine())
11004 {
11005 /*
11006 * Update the parent machine to point to the new owner.
11007 * This is necessary because the stored parent will point to the
11008 * session machine otherwise and cause crashes or errors later
11009 * when the session machine gets invalid.
11010 */
11011 /** @todo Change the MediumAttachment class to behave like any other
11012 * class in this regard by creating peer MediumAttachment
11013 * objects for session machines and share the data with the peer
11014 * machine.
11015 */
11016 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11017 it != mMediaData->mAttachments.end();
11018 ++it)
11019 {
11020 (*it)->updateParentMachine(mPeer);
11021 }
11022
11023 /* attach new data to the primary machine and reshare it */
11024 mPeer->mMediaData.attach(mMediaData);
11025 }
11026
11027 return;
11028}
11029
11030/**
11031 * Perform deferred deletion of implicitly created diffs.
11032 *
11033 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11034 * backed up).
11035 *
11036 * @note Locks this object for writing!
11037 */
11038void Machine::rollbackMedia()
11039{
11040 AutoCaller autoCaller(this);
11041 AssertComRCReturnVoid(autoCaller.rc());
11042
11043 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11044 LogFlowThisFunc(("Entering rollbackMedia\n"));
11045
11046 HRESULT rc = S_OK;
11047
11048 /* no attach/detach operations -- nothing to do */
11049 if (!mMediaData.isBackedUp())
11050 return;
11051
11052 /* enumerate new attachments */
11053 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11054 it != mMediaData->mAttachments.end();
11055 ++it)
11056 {
11057 MediumAttachment *pAttach = *it;
11058 /* Fix up the backrefs for DVD/floppy media. */
11059 if (pAttach->getType() != DeviceType_HardDisk)
11060 {
11061 Medium* pMedium = pAttach->getMedium();
11062 if (pMedium)
11063 {
11064 rc = pMedium->removeBackReference(mData->mUuid);
11065 AssertComRC(rc);
11066 }
11067 }
11068
11069 (*it)->rollback();
11070
11071 pAttach = *it;
11072 /* Fix up the backrefs for DVD/floppy media. */
11073 if (pAttach->getType() != DeviceType_HardDisk)
11074 {
11075 Medium* pMedium = pAttach->getMedium();
11076 if (pMedium)
11077 {
11078 rc = pMedium->addBackReference(mData->mUuid);
11079 AssertComRC(rc);
11080 }
11081 }
11082 }
11083
11084 /** @todo convert all this Machine-based voodoo to MediumAttachment
11085 * based rollback logic. */
11086 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11087
11088 return;
11089}
11090
11091/**
11092 * Returns true if the settings file is located in the directory named exactly
11093 * as the machine; this means, among other things, that the machine directory
11094 * should be auto-renamed.
11095 *
11096 * @param aSettingsDir if not NULL, the full machine settings file directory
11097 * name will be assigned there.
11098 *
11099 * @note Doesn't lock anything.
11100 * @note Not thread safe (must be called from this object's lock).
11101 */
11102bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11103{
11104 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11105 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11106 if (aSettingsDir)
11107 *aSettingsDir = strMachineDirName;
11108 strMachineDirName.stripPath(); // vmname
11109 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11110 strConfigFileOnly.stripPath() // vmname.vbox
11111 .stripExt(); // vmname
11112 /** @todo hack, make somehow use of ComposeMachineFilename */
11113 if (mUserData->s.fDirectoryIncludesUUID)
11114 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11115
11116 AssertReturn(!strMachineDirName.isEmpty(), false);
11117 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11118
11119 return strMachineDirName == strConfigFileOnly;
11120}
11121
11122/**
11123 * Discards all changes to machine settings.
11124 *
11125 * @param aNotify Whether to notify the direct session about changes or not.
11126 *
11127 * @note Locks objects for writing!
11128 */
11129void Machine::rollback(bool aNotify)
11130{
11131 AutoCaller autoCaller(this);
11132 AssertComRCReturn(autoCaller.rc(), (void)0);
11133
11134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11135
11136 if (!mStorageControllers.isNull())
11137 {
11138 if (mStorageControllers.isBackedUp())
11139 {
11140 /* unitialize all new devices (absent in the backed up list). */
11141 StorageControllerList::const_iterator it = mStorageControllers->begin();
11142 StorageControllerList *backedList = mStorageControllers.backedUpData();
11143 while (it != mStorageControllers->end())
11144 {
11145 if ( std::find(backedList->begin(), backedList->end(), *it)
11146 == backedList->end()
11147 )
11148 {
11149 (*it)->uninit();
11150 }
11151 ++it;
11152 }
11153
11154 /* restore the list */
11155 mStorageControllers.rollback();
11156 }
11157
11158 /* rollback any changes to devices after restoring the list */
11159 if (mData->flModifications & IsModified_Storage)
11160 {
11161 StorageControllerList::const_iterator it = mStorageControllers->begin();
11162 while (it != mStorageControllers->end())
11163 {
11164 (*it)->rollback();
11165 ++it;
11166 }
11167 }
11168 }
11169
11170 mUserData.rollback();
11171
11172 mHWData.rollback();
11173
11174 if (mData->flModifications & IsModified_Storage)
11175 rollbackMedia();
11176
11177 if (mBIOSSettings)
11178 mBIOSSettings->rollback();
11179
11180 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11181 mVRDEServer->rollback();
11182
11183 if (mAudioAdapter)
11184 mAudioAdapter->rollback();
11185
11186 if (mUSBController && (mData->flModifications & IsModified_USB))
11187 mUSBController->rollback();
11188
11189 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11190 mBandwidthControl->rollback();
11191
11192 if (!mHWData.isNull())
11193 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11194 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11195 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11196 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11197
11198 if (mData->flModifications & IsModified_NetworkAdapters)
11199 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11200 if ( mNetworkAdapters[slot]
11201 && mNetworkAdapters[slot]->isModified())
11202 {
11203 mNetworkAdapters[slot]->rollback();
11204 networkAdapters[slot] = mNetworkAdapters[slot];
11205 }
11206
11207 if (mData->flModifications & IsModified_SerialPorts)
11208 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11209 if ( mSerialPorts[slot]
11210 && mSerialPorts[slot]->isModified())
11211 {
11212 mSerialPorts[slot]->rollback();
11213 serialPorts[slot] = mSerialPorts[slot];
11214 }
11215
11216 if (mData->flModifications & IsModified_ParallelPorts)
11217 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11218 if ( mParallelPorts[slot]
11219 && mParallelPorts[slot]->isModified())
11220 {
11221 mParallelPorts[slot]->rollback();
11222 parallelPorts[slot] = mParallelPorts[slot];
11223 }
11224
11225 if (aNotify)
11226 {
11227 /* inform the direct session about changes */
11228
11229 ComObjPtr<Machine> that = this;
11230 uint32_t flModifications = mData->flModifications;
11231 alock.release();
11232
11233 if (flModifications & IsModified_SharedFolders)
11234 that->onSharedFolderChange();
11235
11236 if (flModifications & IsModified_VRDEServer)
11237 that->onVRDEServerChange(/* aRestart */ TRUE);
11238 if (flModifications & IsModified_USB)
11239 that->onUSBControllerChange();
11240
11241 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
11242 if (networkAdapters[slot])
11243 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
11244 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
11245 if (serialPorts[slot])
11246 that->onSerialPortChange(serialPorts[slot]);
11247 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
11248 if (parallelPorts[slot])
11249 that->onParallelPortChange(parallelPorts[slot]);
11250
11251 if (flModifications & IsModified_Storage)
11252 that->onStorageControllerChange();
11253
11254#if 0
11255 if (flModifications & IsModified_BandwidthControl)
11256 that->onBandwidthControlChange();
11257#endif
11258 }
11259}
11260
11261/**
11262 * Commits all the changes to machine settings.
11263 *
11264 * Note that this operation is supposed to never fail.
11265 *
11266 * @note Locks this object and children for writing.
11267 */
11268void Machine::commit()
11269{
11270 AutoCaller autoCaller(this);
11271 AssertComRCReturnVoid(autoCaller.rc());
11272
11273 AutoCaller peerCaller(mPeer);
11274 AssertComRCReturnVoid(peerCaller.rc());
11275
11276 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11277
11278 /*
11279 * use safe commit to ensure Snapshot machines (that share mUserData)
11280 * will still refer to a valid memory location
11281 */
11282 mUserData.commitCopy();
11283
11284 mHWData.commit();
11285
11286 if (mMediaData.isBackedUp())
11287 commitMedia();
11288
11289 mBIOSSettings->commit();
11290 mVRDEServer->commit();
11291 mAudioAdapter->commit();
11292 mUSBController->commit();
11293 mBandwidthControl->commit();
11294
11295 /* Since mNetworkAdapters is a list which might have been changed (resized)
11296 * without using the Backupable<> template we need to handle the copying
11297 * of the list entries manually, including the creation of peers for the
11298 * new objects. */
11299 bool commitNetworkAdapters = false;
11300 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11301 if (mPeer)
11302 {
11303 /* commit everything, even the ones which will go away */
11304 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11305 mNetworkAdapters[slot]->commit();
11306 /* copy over the new entries, creating a peer and uninit the original */
11307 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11308 for (size_t slot = 0; slot < newSize; slot++)
11309 {
11310 /* look if this adapter has a peer device */
11311 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
11312 if (!peer)
11313 {
11314 /* no peer means the adapter is a newly created one;
11315 * create a peer owning data this data share it with */
11316 peer.createObject();
11317 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11318 }
11319 mPeer->mNetworkAdapters[slot] = peer;
11320 }
11321 /* uninit any no longer needed network adapters */
11322 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
11323 mNetworkAdapters[slot]->uninit();
11324 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
11325 {
11326 if (mPeer->mNetworkAdapters[slot])
11327 mPeer->mNetworkAdapters[slot]->uninit();
11328 }
11329 /* Keep the original network adapter count until this point, so that
11330 * discarding a chipset type change will not lose settings. */
11331 mNetworkAdapters.resize(newSize);
11332 mPeer->mNetworkAdapters.resize(newSize);
11333 }
11334 else
11335 {
11336 /* we have no peer (our parent is the newly created machine);
11337 * just commit changes to the network adapters */
11338 commitNetworkAdapters = true;
11339 }
11340 if (commitNetworkAdapters)
11341 {
11342 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11343 mNetworkAdapters[slot]->commit();
11344 }
11345
11346 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11347 mSerialPorts[slot]->commit();
11348 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11349 mParallelPorts[slot]->commit();
11350
11351 bool commitStorageControllers = false;
11352
11353 if (mStorageControllers.isBackedUp())
11354 {
11355 mStorageControllers.commit();
11356
11357 if (mPeer)
11358 {
11359 /* Commit all changes to new controllers (this will reshare data with
11360 * peers for those who have peers) */
11361 StorageControllerList *newList = new StorageControllerList();
11362 StorageControllerList::const_iterator it = mStorageControllers->begin();
11363 while (it != mStorageControllers->end())
11364 {
11365 (*it)->commit();
11366
11367 /* look if this controller has a peer device */
11368 ComObjPtr<StorageController> peer = (*it)->getPeer();
11369 if (!peer)
11370 {
11371 /* no peer means the device is a newly created one;
11372 * create a peer owning data this device share it with */
11373 peer.createObject();
11374 peer->init(mPeer, *it, true /* aReshare */);
11375 }
11376 else
11377 {
11378 /* remove peer from the old list */
11379 mPeer->mStorageControllers->remove(peer);
11380 }
11381 /* and add it to the new list */
11382 newList->push_back(peer);
11383
11384 ++it;
11385 }
11386
11387 /* uninit old peer's controllers that are left */
11388 it = mPeer->mStorageControllers->begin();
11389 while (it != mPeer->mStorageControllers->end())
11390 {
11391 (*it)->uninit();
11392 ++it;
11393 }
11394
11395 /* attach new list of controllers to our peer */
11396 mPeer->mStorageControllers.attach(newList);
11397 }
11398 else
11399 {
11400 /* we have no peer (our parent is the newly created machine);
11401 * just commit changes to devices */
11402 commitStorageControllers = true;
11403 }
11404 }
11405 else
11406 {
11407 /* the list of controllers itself is not changed,
11408 * just commit changes to controllers themselves */
11409 commitStorageControllers = true;
11410 }
11411
11412 if (commitStorageControllers)
11413 {
11414 StorageControllerList::const_iterator it = mStorageControllers->begin();
11415 while (it != mStorageControllers->end())
11416 {
11417 (*it)->commit();
11418 ++it;
11419 }
11420 }
11421
11422 if (isSessionMachine())
11423 {
11424 /* attach new data to the primary machine and reshare it */
11425 mPeer->mUserData.attach(mUserData);
11426 mPeer->mHWData.attach(mHWData);
11427 /* mMediaData is reshared by fixupMedia */
11428 // mPeer->mMediaData.attach(mMediaData);
11429 Assert(mPeer->mMediaData.data() == mMediaData.data());
11430 }
11431}
11432
11433/**
11434 * Copies all the hardware data from the given machine.
11435 *
11436 * Currently, only called when the VM is being restored from a snapshot. In
11437 * particular, this implies that the VM is not running during this method's
11438 * call.
11439 *
11440 * @note This method must be called from under this object's lock.
11441 *
11442 * @note This method doesn't call #commit(), so all data remains backed up and
11443 * unsaved.
11444 */
11445void Machine::copyFrom(Machine *aThat)
11446{
11447 AssertReturnVoid(!isSnapshotMachine());
11448 AssertReturnVoid(aThat->isSnapshotMachine());
11449
11450 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11451
11452 mHWData.assignCopy(aThat->mHWData);
11453
11454 // create copies of all shared folders (mHWData after attaching a copy
11455 // contains just references to original objects)
11456 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11457 it != mHWData->mSharedFolders.end();
11458 ++it)
11459 {
11460 ComObjPtr<SharedFolder> folder;
11461 folder.createObject();
11462 HRESULT rc = folder->initCopy(getMachine(), *it);
11463 AssertComRC(rc);
11464 *it = folder;
11465 }
11466
11467 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
11468 mVRDEServer->copyFrom(aThat->mVRDEServer);
11469 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
11470 mUSBController->copyFrom(aThat->mUSBController);
11471 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
11472
11473 /* create private copies of all controllers */
11474 mStorageControllers.backup();
11475 mStorageControllers->clear();
11476 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11477 it != aThat->mStorageControllers->end();
11478 ++it)
11479 {
11480 ComObjPtr<StorageController> ctrl;
11481 ctrl.createObject();
11482 ctrl->initCopy(this, *it);
11483 mStorageControllers->push_back(ctrl);
11484 }
11485
11486 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11487 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11488 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
11489 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11490 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
11491 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11492 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
11493}
11494
11495/**
11496 * Returns whether the given storage controller is hotplug capable.
11497 *
11498 * @returns true if the controller supports hotplugging
11499 * false otherwise.
11500 * @param enmCtrlType The controller type to check for.
11501 */
11502bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11503{
11504 switch (enmCtrlType)
11505 {
11506 case StorageControllerType_IntelAhci:
11507 return true;
11508 case StorageControllerType_LsiLogic:
11509 case StorageControllerType_LsiLogicSas:
11510 case StorageControllerType_BusLogic:
11511 case StorageControllerType_PIIX3:
11512 case StorageControllerType_PIIX4:
11513 case StorageControllerType_ICH6:
11514 case StorageControllerType_I82078:
11515 default:
11516 return false;
11517 }
11518}
11519
11520#ifdef VBOX_WITH_RESOURCE_USAGE_API
11521
11522void Machine::getDiskList(MediaList &list)
11523{
11524 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11525 it != mMediaData->mAttachments.end();
11526 ++it)
11527 {
11528 MediumAttachment* pAttach = *it;
11529 /* just in case */
11530 AssertStmt(pAttach, continue);
11531
11532 AutoCaller localAutoCallerA(pAttach);
11533 if (FAILED(localAutoCallerA.rc())) continue;
11534
11535 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11536
11537 if (pAttach->getType() == DeviceType_HardDisk)
11538 list.push_back(pAttach->getMedium());
11539 }
11540}
11541
11542void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11543{
11544 AssertReturnVoid(isWriteLockOnCurrentThread());
11545 AssertPtrReturnVoid(aCollector);
11546
11547 pm::CollectorHAL *hal = aCollector->getHAL();
11548 /* Create sub metrics */
11549 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11550 "Percentage of processor time spent in user mode by the VM process.");
11551 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11552 "Percentage of processor time spent in kernel mode by the VM process.");
11553 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11554 "Size of resident portion of VM process in memory.");
11555 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11556 "Actual size of all VM disks combined.");
11557 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11558 "Network receive rate.");
11559 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11560 "Network transmit rate.");
11561 /* Create and register base metrics */
11562 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11563 cpuLoadUser, cpuLoadKernel);
11564 aCollector->registerBaseMetric(cpuLoad);
11565 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11566 ramUsageUsed);
11567 aCollector->registerBaseMetric(ramUsage);
11568 MediaList disks;
11569 getDiskList(disks);
11570 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11571 diskUsageUsed);
11572 aCollector->registerBaseMetric(diskUsage);
11573
11574 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11575 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11576 new pm::AggregateAvg()));
11577 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11578 new pm::AggregateMin()));
11579 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11580 new pm::AggregateMax()));
11581 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11582 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11583 new pm::AggregateAvg()));
11584 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11585 new pm::AggregateMin()));
11586 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11587 new pm::AggregateMax()));
11588
11589 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11590 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11591 new pm::AggregateAvg()));
11592 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11593 new pm::AggregateMin()));
11594 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11595 new pm::AggregateMax()));
11596
11597 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11598 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11599 new pm::AggregateAvg()));
11600 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11601 new pm::AggregateMin()));
11602 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11603 new pm::AggregateMax()));
11604
11605
11606 /* Guest metrics collector */
11607 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11608 aCollector->registerGuest(mCollectorGuest);
11609 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11610 this, __PRETTY_FUNCTION__, mCollectorGuest));
11611
11612 /* Create sub metrics */
11613 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11614 "Percentage of processor time spent in user mode as seen by the guest.");
11615 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11616 "Percentage of processor time spent in kernel mode as seen by the guest.");
11617 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11618 "Percentage of processor time spent idling as seen by the guest.");
11619
11620 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11621 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11622 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11623 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11624 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11625 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11626
11627 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11628
11629 /* Create and register base metrics */
11630 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
11631 machineNetRx, machineNetTx);
11632 aCollector->registerBaseMetric(machineNetRate);
11633
11634 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11635 guestLoadUser, guestLoadKernel, guestLoadIdle);
11636 aCollector->registerBaseMetric(guestCpuLoad);
11637
11638 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11639 guestMemTotal, guestMemFree,
11640 guestMemBalloon, guestMemShared,
11641 guestMemCache, guestPagedTotal);
11642 aCollector->registerBaseMetric(guestCpuMem);
11643
11644 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
11645 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
11646 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
11647 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
11648
11649 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
11650 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
11651 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
11652 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
11653
11654 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11655 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11656 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11657 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11658
11659 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11660 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11661 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11662 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11663
11664 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11665 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11666 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11667 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11668
11669 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11670 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11671 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11672 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11673
11674 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11675 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11676 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11677 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11678
11679 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11680 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11681 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11682 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11683
11684 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11685 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11686 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11687 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11688
11689 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11690 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11691 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11692 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11693
11694 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11695 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11696 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11697 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11698}
11699
11700void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11701{
11702 AssertReturnVoid(isWriteLockOnCurrentThread());
11703
11704 if (aCollector)
11705 {
11706 aCollector->unregisterMetricsFor(aMachine);
11707 aCollector->unregisterBaseMetricsFor(aMachine);
11708 }
11709}
11710
11711#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11712
11713
11714////////////////////////////////////////////////////////////////////////////////
11715
11716DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11717
11718HRESULT SessionMachine::FinalConstruct()
11719{
11720 LogFlowThisFunc(("\n"));
11721
11722#if defined(RT_OS_WINDOWS)
11723 mIPCSem = NULL;
11724#elif defined(RT_OS_OS2)
11725 mIPCSem = NULLHANDLE;
11726#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11727 mIPCSem = -1;
11728#else
11729# error "Port me!"
11730#endif
11731
11732 return BaseFinalConstruct();
11733}
11734
11735void SessionMachine::FinalRelease()
11736{
11737 LogFlowThisFunc(("\n"));
11738
11739 uninit(Uninit::Unexpected);
11740
11741 BaseFinalRelease();
11742}
11743
11744/**
11745 * @note Must be called only by Machine::openSession() from its own write lock.
11746 */
11747HRESULT SessionMachine::init(Machine *aMachine)
11748{
11749 LogFlowThisFuncEnter();
11750 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
11751
11752 AssertReturn(aMachine, E_INVALIDARG);
11753
11754 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
11755
11756 /* Enclose the state transition NotReady->InInit->Ready */
11757 AutoInitSpan autoInitSpan(this);
11758 AssertReturn(autoInitSpan.isOk(), E_FAIL);
11759
11760 /* create the interprocess semaphore */
11761#if defined(RT_OS_WINDOWS)
11762 mIPCSemName = aMachine->mData->m_strConfigFileFull;
11763 for (size_t i = 0; i < mIPCSemName.length(); i++)
11764 if (mIPCSemName.raw()[i] == '\\')
11765 mIPCSemName.raw()[i] = '/';
11766 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
11767 ComAssertMsgRet(mIPCSem,
11768 ("Cannot create IPC mutex '%ls', err=%d",
11769 mIPCSemName.raw(), ::GetLastError()),
11770 E_FAIL);
11771#elif defined(RT_OS_OS2)
11772 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
11773 aMachine->mData->mUuid.raw());
11774 mIPCSemName = ipcSem;
11775 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
11776 ComAssertMsgRet(arc == NO_ERROR,
11777 ("Cannot create IPC mutex '%s', arc=%ld",
11778 ipcSem.c_str(), arc),
11779 E_FAIL);
11780#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11781# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11782# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
11783 /** @todo Check that this still works correctly. */
11784 AssertCompileSize(key_t, 8);
11785# else
11786 AssertCompileSize(key_t, 4);
11787# endif
11788 key_t key;
11789 mIPCSem = -1;
11790 mIPCKey = "0";
11791 for (uint32_t i = 0; i < 1 << 24; i++)
11792 {
11793 key = ((uint32_t)'V' << 24) | i;
11794 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
11795 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
11796 {
11797 mIPCSem = sem;
11798 if (sem >= 0)
11799 mIPCKey = BstrFmt("%u", key);
11800 break;
11801 }
11802 }
11803# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11804 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
11805 char *pszSemName = NULL;
11806 RTStrUtf8ToCurrentCP(&pszSemName, semName);
11807 key_t key = ::ftok(pszSemName, 'V');
11808 RTStrFree(pszSemName);
11809
11810 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
11811# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11812
11813 int errnoSave = errno;
11814 if (mIPCSem < 0 && errnoSave == ENOSYS)
11815 {
11816 setError(E_FAIL,
11817 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
11818 "support for SysV IPC. Check the host kernel configuration for "
11819 "CONFIG_SYSVIPC=y"));
11820 return E_FAIL;
11821 }
11822 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
11823 * the IPC semaphores */
11824 if (mIPCSem < 0 && errnoSave == ENOSPC)
11825 {
11826#ifdef RT_OS_LINUX
11827 setError(E_FAIL,
11828 tr("Cannot create IPC semaphore because the system limit for the "
11829 "maximum number of semaphore sets (SEMMNI), or the system wide "
11830 "maximum number of semaphores (SEMMNS) would be exceeded. The "
11831 "current set of SysV IPC semaphores can be determined from "
11832 "the file /proc/sysvipc/sem"));
11833#else
11834 setError(E_FAIL,
11835 tr("Cannot create IPC semaphore because the system-imposed limit "
11836 "on the maximum number of allowed semaphores or semaphore "
11837 "identifiers system-wide would be exceeded"));
11838#endif
11839 return E_FAIL;
11840 }
11841 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
11842 E_FAIL);
11843 /* set the initial value to 1 */
11844 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
11845 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
11846 E_FAIL);
11847#else
11848# error "Port me!"
11849#endif
11850
11851 /* memorize the peer Machine */
11852 unconst(mPeer) = aMachine;
11853 /* share the parent pointer */
11854 unconst(mParent) = aMachine->mParent;
11855
11856 /* take the pointers to data to share */
11857 mData.share(aMachine->mData);
11858 mSSData.share(aMachine->mSSData);
11859
11860 mUserData.share(aMachine->mUserData);
11861 mHWData.share(aMachine->mHWData);
11862 mMediaData.share(aMachine->mMediaData);
11863
11864 mStorageControllers.allocate();
11865 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
11866 it != aMachine->mStorageControllers->end();
11867 ++it)
11868 {
11869 ComObjPtr<StorageController> ctl;
11870 ctl.createObject();
11871 ctl->init(this, *it);
11872 mStorageControllers->push_back(ctl);
11873 }
11874
11875 unconst(mBIOSSettings).createObject();
11876 mBIOSSettings->init(this, aMachine->mBIOSSettings);
11877 /* create another VRDEServer object that will be mutable */
11878 unconst(mVRDEServer).createObject();
11879 mVRDEServer->init(this, aMachine->mVRDEServer);
11880 /* create another audio adapter object that will be mutable */
11881 unconst(mAudioAdapter).createObject();
11882 mAudioAdapter->init(this, aMachine->mAudioAdapter);
11883 /* create a list of serial ports that will be mutable */
11884 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11885 {
11886 unconst(mSerialPorts[slot]).createObject();
11887 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
11888 }
11889 /* create a list of parallel ports that will be mutable */
11890 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11891 {
11892 unconst(mParallelPorts[slot]).createObject();
11893 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
11894 }
11895 /* create another USB controller object that will be mutable */
11896 unconst(mUSBController).createObject();
11897 mUSBController->init(this, aMachine->mUSBController);
11898
11899 /* create a list of network adapters that will be mutable */
11900 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
11901 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11902 {
11903 unconst(mNetworkAdapters[slot]).createObject();
11904 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
11905 }
11906
11907 /* create another bandwidth control object that will be mutable */
11908 unconst(mBandwidthControl).createObject();
11909 mBandwidthControl->init(this, aMachine->mBandwidthControl);
11910
11911 /* default is to delete saved state on Saved -> PoweredOff transition */
11912 mRemoveSavedState = true;
11913
11914 /* Confirm a successful initialization when it's the case */
11915 autoInitSpan.setSucceeded();
11916
11917 LogFlowThisFuncLeave();
11918 return S_OK;
11919}
11920
11921/**
11922 * Uninitializes this session object. If the reason is other than
11923 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
11924 *
11925 * @param aReason uninitialization reason
11926 *
11927 * @note Locks mParent + this object for writing.
11928 */
11929void SessionMachine::uninit(Uninit::Reason aReason)
11930{
11931 LogFlowThisFuncEnter();
11932 LogFlowThisFunc(("reason=%d\n", aReason));
11933
11934 /*
11935 * Strongly reference ourselves to prevent this object deletion after
11936 * mData->mSession.mMachine.setNull() below (which can release the last
11937 * reference and call the destructor). Important: this must be done before
11938 * accessing any members (and before AutoUninitSpan that does it as well).
11939 * This self reference will be released as the very last step on return.
11940 */
11941 ComObjPtr<SessionMachine> selfRef = this;
11942
11943 /* Enclose the state transition Ready->InUninit->NotReady */
11944 AutoUninitSpan autoUninitSpan(this);
11945 if (autoUninitSpan.uninitDone())
11946 {
11947 LogFlowThisFunc(("Already uninitialized\n"));
11948 LogFlowThisFuncLeave();
11949 return;
11950 }
11951
11952 if (autoUninitSpan.initFailed())
11953 {
11954 /* We've been called by init() because it's failed. It's not really
11955 * necessary (nor it's safe) to perform the regular uninit sequence
11956 * below, the following is enough.
11957 */
11958 LogFlowThisFunc(("Initialization failed.\n"));
11959#if defined(RT_OS_WINDOWS)
11960 if (mIPCSem)
11961 ::CloseHandle(mIPCSem);
11962 mIPCSem = NULL;
11963#elif defined(RT_OS_OS2)
11964 if (mIPCSem != NULLHANDLE)
11965 ::DosCloseMutexSem(mIPCSem);
11966 mIPCSem = NULLHANDLE;
11967#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11968 if (mIPCSem >= 0)
11969 ::semctl(mIPCSem, 0, IPC_RMID);
11970 mIPCSem = -1;
11971# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11972 mIPCKey = "0";
11973# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
11974#else
11975# error "Port me!"
11976#endif
11977 uninitDataAndChildObjects();
11978 mData.free();
11979 unconst(mParent) = NULL;
11980 unconst(mPeer) = NULL;
11981 LogFlowThisFuncLeave();
11982 return;
11983 }
11984
11985 MachineState_T lastState;
11986 {
11987 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
11988 lastState = mData->mMachineState;
11989 }
11990 NOREF(lastState);
11991
11992#ifdef VBOX_WITH_USB
11993 // release all captured USB devices, but do this before requesting the locks below
11994 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
11995 {
11996 /* Console::captureUSBDevices() is called in the VM process only after
11997 * setting the machine state to Starting or Restoring.
11998 * Console::detachAllUSBDevices() will be called upon successful
11999 * termination. So, we need to release USB devices only if there was
12000 * an abnormal termination of a running VM.
12001 *
12002 * This is identical to SessionMachine::DetachAllUSBDevices except
12003 * for the aAbnormal argument. */
12004 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12005 AssertComRC(rc);
12006 NOREF(rc);
12007
12008 USBProxyService *service = mParent->host()->usbProxyService();
12009 if (service)
12010 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12011 }
12012#endif /* VBOX_WITH_USB */
12013
12014 // we need to lock this object in uninit() because the lock is shared
12015 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12016 // and others need mParent lock, and USB needs host lock.
12017 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12018
12019#if 0
12020 // Trigger async cleanup tasks, avoid doing things here which are not
12021 // vital to be done immediately and maybe need more locks. This calls
12022 // Machine::unregisterMetrics().
12023 mParent->onMachineUninit(mPeer);
12024#else
12025 /*
12026 * It is safe to call Machine::unregisterMetrics() here because
12027 * PerformanceCollector::samplerCallback no longer accesses guest methods
12028 * holding the lock.
12029 */
12030 unregisterMetrics(mParent->performanceCollector(), mPeer);
12031#endif
12032 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12033 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12034 this, __PRETTY_FUNCTION__, mCollectorGuest));
12035 if (mCollectorGuest)
12036 {
12037 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
12038 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12039 mCollectorGuest = NULL;
12040 }
12041
12042 if (aReason == Uninit::Abnormal)
12043 {
12044 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12045 Global::IsOnlineOrTransient(lastState)));
12046
12047 /* reset the state to Aborted */
12048 if (mData->mMachineState != MachineState_Aborted)
12049 setMachineState(MachineState_Aborted);
12050 }
12051
12052 // any machine settings modified?
12053 if (mData->flModifications)
12054 {
12055 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12056 rollback(false /* aNotify */);
12057 }
12058
12059 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12060 || !mConsoleTaskData.mSnapshot);
12061 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12062 {
12063 LogWarningThisFunc(("canceling failed save state request!\n"));
12064 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12065 }
12066 else if (!mConsoleTaskData.mSnapshot.isNull())
12067 {
12068 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12069
12070 /* delete all differencing hard disks created (this will also attach
12071 * their parents back by rolling back mMediaData) */
12072 rollbackMedia();
12073
12074 // delete the saved state file (it might have been already created)
12075 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12076 // think it's still in use
12077 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12078 mConsoleTaskData.mSnapshot->uninit();
12079 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12080 }
12081
12082 if (!mData->mSession.mType.isEmpty())
12083 {
12084 /* mType is not null when this machine's process has been started by
12085 * Machine::LaunchVMProcess(), therefore it is our child. We
12086 * need to queue the PID to reap the process (and avoid zombies on
12087 * Linux). */
12088 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12089 mParent->addProcessToReap(mData->mSession.mPID);
12090 }
12091
12092 mData->mSession.mPID = NIL_RTPROCESS;
12093
12094 if (aReason == Uninit::Unexpected)
12095 {
12096 /* Uninitialization didn't come from #checkForDeath(), so tell the
12097 * client watcher thread to update the set of machines that have open
12098 * sessions. */
12099 mParent->updateClientWatcher();
12100 }
12101
12102 /* uninitialize all remote controls */
12103 if (mData->mSession.mRemoteControls.size())
12104 {
12105 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12106 mData->mSession.mRemoteControls.size()));
12107
12108 Data::Session::RemoteControlList::iterator it =
12109 mData->mSession.mRemoteControls.begin();
12110 while (it != mData->mSession.mRemoteControls.end())
12111 {
12112 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12113 HRESULT rc = (*it)->Uninitialize();
12114 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12115 if (FAILED(rc))
12116 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12117 ++it;
12118 }
12119 mData->mSession.mRemoteControls.clear();
12120 }
12121
12122 /*
12123 * An expected uninitialization can come only from #checkForDeath().
12124 * Otherwise it means that something's gone really wrong (for example,
12125 * the Session implementation has released the VirtualBox reference
12126 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12127 * etc). However, it's also possible, that the client releases the IPC
12128 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12129 * but the VirtualBox release event comes first to the server process.
12130 * This case is practically possible, so we should not assert on an
12131 * unexpected uninit, just log a warning.
12132 */
12133
12134 if ((aReason == Uninit::Unexpected))
12135 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12136
12137 if (aReason != Uninit::Normal)
12138 {
12139 mData->mSession.mDirectControl.setNull();
12140 }
12141 else
12142 {
12143 /* this must be null here (see #OnSessionEnd()) */
12144 Assert(mData->mSession.mDirectControl.isNull());
12145 Assert(mData->mSession.mState == SessionState_Unlocking);
12146 Assert(!mData->mSession.mProgress.isNull());
12147 }
12148 if (mData->mSession.mProgress)
12149 {
12150 if (aReason == Uninit::Normal)
12151 mData->mSession.mProgress->notifyComplete(S_OK);
12152 else
12153 mData->mSession.mProgress->notifyComplete(E_FAIL,
12154 COM_IIDOF(ISession),
12155 getComponentName(),
12156 tr("The VM session was aborted"));
12157 mData->mSession.mProgress.setNull();
12158 }
12159
12160 /* remove the association between the peer machine and this session machine */
12161 Assert( (SessionMachine*)mData->mSession.mMachine == this
12162 || aReason == Uninit::Unexpected);
12163
12164 /* reset the rest of session data */
12165 mData->mSession.mMachine.setNull();
12166 mData->mSession.mState = SessionState_Unlocked;
12167 mData->mSession.mType.setNull();
12168
12169 /* close the interprocess semaphore before leaving the exclusive lock */
12170#if defined(RT_OS_WINDOWS)
12171 if (mIPCSem)
12172 ::CloseHandle(mIPCSem);
12173 mIPCSem = NULL;
12174#elif defined(RT_OS_OS2)
12175 if (mIPCSem != NULLHANDLE)
12176 ::DosCloseMutexSem(mIPCSem);
12177 mIPCSem = NULLHANDLE;
12178#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12179 if (mIPCSem >= 0)
12180 ::semctl(mIPCSem, 0, IPC_RMID);
12181 mIPCSem = -1;
12182# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12183 mIPCKey = "0";
12184# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12185#else
12186# error "Port me!"
12187#endif
12188
12189 /* fire an event */
12190 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12191
12192 uninitDataAndChildObjects();
12193
12194 /* free the essential data structure last */
12195 mData.free();
12196
12197 /* release the exclusive lock before setting the below two to NULL */
12198 multilock.release();
12199
12200 unconst(mParent) = NULL;
12201 unconst(mPeer) = NULL;
12202
12203 LogFlowThisFuncLeave();
12204}
12205
12206// util::Lockable interface
12207////////////////////////////////////////////////////////////////////////////////
12208
12209/**
12210 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12211 * with the primary Machine instance (mPeer).
12212 */
12213RWLockHandle *SessionMachine::lockHandle() const
12214{
12215 AssertReturn(mPeer != NULL, NULL);
12216 return mPeer->lockHandle();
12217}
12218
12219// IInternalMachineControl methods
12220////////////////////////////////////////////////////////////////////////////////
12221
12222/**
12223 * Passes collected guest statistics to performance collector object
12224 */
12225STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12226 ULONG aCpuKernel, ULONG aCpuIdle,
12227 ULONG aMemTotal, ULONG aMemFree,
12228 ULONG aMemBalloon, ULONG aMemShared,
12229 ULONG aMemCache, ULONG aPageTotal,
12230 ULONG aAllocVMM, ULONG aFreeVMM,
12231 ULONG aBalloonedVMM, ULONG aSharedVMM,
12232 ULONG aVmNetRx, ULONG aVmNetTx)
12233{
12234 if (mCollectorGuest)
12235 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12236 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12237 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12238 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12239
12240 return S_OK;
12241}
12242
12243/**
12244 * @note Locks this object for writing.
12245 */
12246STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12247{
12248 AutoCaller autoCaller(this);
12249 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12250
12251 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12252
12253 mRemoveSavedState = aRemove;
12254
12255 return S_OK;
12256}
12257
12258/**
12259 * @note Locks the same as #setMachineState() does.
12260 */
12261STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12262{
12263 return setMachineState(aMachineState);
12264}
12265
12266/**
12267 * @note Locks this object for reading.
12268 */
12269STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
12270{
12271 AutoCaller autoCaller(this);
12272 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12273
12274 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12275
12276#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
12277 mIPCSemName.cloneTo(aId);
12278 return S_OK;
12279#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12280# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12281 mIPCKey.cloneTo(aId);
12282# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12283 mData->m_strConfigFileFull.cloneTo(aId);
12284# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12285 return S_OK;
12286#else
12287# error "Port me!"
12288#endif
12289}
12290
12291/**
12292 * @note Locks this object for writing.
12293 */
12294STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12295{
12296 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12297 AutoCaller autoCaller(this);
12298 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12299
12300 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12301
12302 if (mData->mSession.mState != SessionState_Locked)
12303 return VBOX_E_INVALID_OBJECT_STATE;
12304
12305 if (!mData->mSession.mProgress.isNull())
12306 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12307
12308 LogFlowThisFunc(("returns S_OK.\n"));
12309 return S_OK;
12310}
12311
12312/**
12313 * @note Locks this object for writing.
12314 */
12315STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12316{
12317 AutoCaller autoCaller(this);
12318 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12319
12320 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12321
12322 if (mData->mSession.mState != SessionState_Locked)
12323 return VBOX_E_INVALID_OBJECT_STATE;
12324
12325 /* Finalize the LaunchVMProcess progress object. */
12326 if (mData->mSession.mProgress)
12327 {
12328 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12329 mData->mSession.mProgress.setNull();
12330 }
12331
12332 if (SUCCEEDED((HRESULT)iResult))
12333 {
12334#ifdef VBOX_WITH_RESOURCE_USAGE_API
12335 /* The VM has been powered up successfully, so it makes sense
12336 * now to offer the performance metrics for a running machine
12337 * object. Doing it earlier wouldn't be safe. */
12338 registerMetrics(mParent->performanceCollector(), mPeer,
12339 mData->mSession.mPID);
12340#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12341 }
12342
12343 return S_OK;
12344}
12345
12346/**
12347 * @note Locks this object for writing.
12348 */
12349STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12350{
12351 LogFlowThisFuncEnter();
12352
12353 CheckComArgOutPointerValid(aProgress);
12354
12355 AutoCaller autoCaller(this);
12356 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12357
12358 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12359
12360 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12361 E_FAIL);
12362
12363 /* create a progress object to track operation completion */
12364 ComObjPtr<Progress> pProgress;
12365 pProgress.createObject();
12366 pProgress->init(getVirtualBox(),
12367 static_cast<IMachine *>(this) /* aInitiator */,
12368 Bstr(tr("Stopping the virtual machine")).raw(),
12369 FALSE /* aCancelable */);
12370
12371 /* fill in the console task data */
12372 mConsoleTaskData.mLastState = mData->mMachineState;
12373 mConsoleTaskData.mProgress = pProgress;
12374
12375 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12376 setMachineState(MachineState_Stopping);
12377
12378 pProgress.queryInterfaceTo(aProgress);
12379
12380 return S_OK;
12381}
12382
12383/**
12384 * @note Locks this object for writing.
12385 */
12386STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12387{
12388 LogFlowThisFuncEnter();
12389
12390 AutoCaller autoCaller(this);
12391 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12392
12393 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12394
12395 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12396 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12397 && mConsoleTaskData.mLastState != MachineState_Null,
12398 E_FAIL);
12399
12400 /*
12401 * On failure, set the state to the state we had when BeginPoweringDown()
12402 * was called (this is expected by Console::PowerDown() and the associated
12403 * task). On success the VM process already changed the state to
12404 * MachineState_PoweredOff, so no need to do anything.
12405 */
12406 if (FAILED(iResult))
12407 setMachineState(mConsoleTaskData.mLastState);
12408
12409 /* notify the progress object about operation completion */
12410 Assert(mConsoleTaskData.mProgress);
12411 if (SUCCEEDED(iResult))
12412 mConsoleTaskData.mProgress->notifyComplete(S_OK);
12413 else
12414 {
12415 Utf8Str strErrMsg(aErrMsg);
12416 if (strErrMsg.length())
12417 mConsoleTaskData.mProgress->notifyComplete(iResult,
12418 COM_IIDOF(ISession),
12419 getComponentName(),
12420 strErrMsg.c_str());
12421 else
12422 mConsoleTaskData.mProgress->notifyComplete(iResult);
12423 }
12424
12425 /* clear out the temporary saved state data */
12426 mConsoleTaskData.mLastState = MachineState_Null;
12427 mConsoleTaskData.mProgress.setNull();
12428
12429 LogFlowThisFuncLeave();
12430 return S_OK;
12431}
12432
12433
12434/**
12435 * Goes through the USB filters of the given machine to see if the given
12436 * device matches any filter or not.
12437 *
12438 * @note Locks the same as USBController::hasMatchingFilter() does.
12439 */
12440STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12441 BOOL *aMatched,
12442 ULONG *aMaskedIfs)
12443{
12444 LogFlowThisFunc(("\n"));
12445
12446 CheckComArgNotNull(aUSBDevice);
12447 CheckComArgOutPointerValid(aMatched);
12448
12449 AutoCaller autoCaller(this);
12450 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12451
12452#ifdef VBOX_WITH_USB
12453 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
12454#else
12455 NOREF(aUSBDevice);
12456 NOREF(aMaskedIfs);
12457 *aMatched = FALSE;
12458#endif
12459
12460 return S_OK;
12461}
12462
12463/**
12464 * @note Locks the same as Host::captureUSBDevice() does.
12465 */
12466STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12467{
12468 LogFlowThisFunc(("\n"));
12469
12470 AutoCaller autoCaller(this);
12471 AssertComRCReturnRC(autoCaller.rc());
12472
12473#ifdef VBOX_WITH_USB
12474 /* if captureDeviceForVM() fails, it must have set extended error info */
12475 clearError();
12476 MultiResult rc = mParent->host()->checkUSBProxyService();
12477 if (FAILED(rc)) return rc;
12478
12479 USBProxyService *service = mParent->host()->usbProxyService();
12480 AssertReturn(service, E_FAIL);
12481 return service->captureDeviceForVM(this, Guid(aId).ref());
12482#else
12483 NOREF(aId);
12484 return E_NOTIMPL;
12485#endif
12486}
12487
12488/**
12489 * @note Locks the same as Host::detachUSBDevice() does.
12490 */
12491STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12492{
12493 LogFlowThisFunc(("\n"));
12494
12495 AutoCaller autoCaller(this);
12496 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12497
12498#ifdef VBOX_WITH_USB
12499 USBProxyService *service = mParent->host()->usbProxyService();
12500 AssertReturn(service, E_FAIL);
12501 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12502#else
12503 NOREF(aId);
12504 NOREF(aDone);
12505 return E_NOTIMPL;
12506#endif
12507}
12508
12509/**
12510 * Inserts all machine filters to the USB proxy service and then calls
12511 * Host::autoCaptureUSBDevices().
12512 *
12513 * Called by Console from the VM process upon VM startup.
12514 *
12515 * @note Locks what called methods lock.
12516 */
12517STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12518{
12519 LogFlowThisFunc(("\n"));
12520
12521 AutoCaller autoCaller(this);
12522 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12523
12524#ifdef VBOX_WITH_USB
12525 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
12526 AssertComRC(rc);
12527 NOREF(rc);
12528
12529 USBProxyService *service = mParent->host()->usbProxyService();
12530 AssertReturn(service, E_FAIL);
12531 return service->autoCaptureDevicesForVM(this);
12532#else
12533 return S_OK;
12534#endif
12535}
12536
12537/**
12538 * Removes all machine filters from the USB proxy service and then calls
12539 * Host::detachAllUSBDevices().
12540 *
12541 * Called by Console from the VM process upon normal VM termination or by
12542 * SessionMachine::uninit() upon abnormal VM termination (from under the
12543 * Machine/SessionMachine lock).
12544 *
12545 * @note Locks what called methods lock.
12546 */
12547STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12548{
12549 LogFlowThisFunc(("\n"));
12550
12551 AutoCaller autoCaller(this);
12552 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12553
12554#ifdef VBOX_WITH_USB
12555 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12556 AssertComRC(rc);
12557 NOREF(rc);
12558
12559 USBProxyService *service = mParent->host()->usbProxyService();
12560 AssertReturn(service, E_FAIL);
12561 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12562#else
12563 NOREF(aDone);
12564 return S_OK;
12565#endif
12566}
12567
12568/**
12569 * @note Locks this object for writing.
12570 */
12571STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12572 IProgress **aProgress)
12573{
12574 LogFlowThisFuncEnter();
12575
12576 AssertReturn(aSession, E_INVALIDARG);
12577 AssertReturn(aProgress, E_INVALIDARG);
12578
12579 AutoCaller autoCaller(this);
12580
12581 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
12582 /*
12583 * We don't assert below because it might happen that a non-direct session
12584 * informs us it is closed right after we've been uninitialized -- it's ok.
12585 */
12586 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12587
12588 /* get IInternalSessionControl interface */
12589 ComPtr<IInternalSessionControl> control(aSession);
12590
12591 ComAssertRet(!control.isNull(), E_INVALIDARG);
12592
12593 /* Creating a Progress object requires the VirtualBox lock, and
12594 * thus locking it here is required by the lock order rules. */
12595 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12596
12597 if (control == mData->mSession.mDirectControl)
12598 {
12599 ComAssertRet(aProgress, E_POINTER);
12600
12601 /* The direct session is being normally closed by the client process
12602 * ----------------------------------------------------------------- */
12603
12604 /* go to the closing state (essential for all open*Session() calls and
12605 * for #checkForDeath()) */
12606 Assert(mData->mSession.mState == SessionState_Locked);
12607 mData->mSession.mState = SessionState_Unlocking;
12608
12609 /* set direct control to NULL to release the remote instance */
12610 mData->mSession.mDirectControl.setNull();
12611 LogFlowThisFunc(("Direct control is set to NULL\n"));
12612
12613 if (mData->mSession.mProgress)
12614 {
12615 /* finalize the progress, someone might wait if a frontend
12616 * closes the session before powering on the VM. */
12617 mData->mSession.mProgress->notifyComplete(E_FAIL,
12618 COM_IIDOF(ISession),
12619 getComponentName(),
12620 tr("The VM session was closed before any attempt to power it on"));
12621 mData->mSession.mProgress.setNull();
12622 }
12623
12624 /* Create the progress object the client will use to wait until
12625 * #checkForDeath() is called to uninitialize this session object after
12626 * it releases the IPC semaphore.
12627 * Note! Because we're "reusing" mProgress here, this must be a proxy
12628 * object just like for LaunchVMProcess. */
12629 Assert(mData->mSession.mProgress.isNull());
12630 ComObjPtr<ProgressProxy> progress;
12631 progress.createObject();
12632 ComPtr<IUnknown> pPeer(mPeer);
12633 progress->init(mParent, pPeer,
12634 Bstr(tr("Closing session")).raw(),
12635 FALSE /* aCancelable */);
12636 progress.queryInterfaceTo(aProgress);
12637 mData->mSession.mProgress = progress;
12638 }
12639 else
12640 {
12641 /* the remote session is being normally closed */
12642 Data::Session::RemoteControlList::iterator it =
12643 mData->mSession.mRemoteControls.begin();
12644 while (it != mData->mSession.mRemoteControls.end())
12645 {
12646 if (control == *it)
12647 break;
12648 ++it;
12649 }
12650 BOOL found = it != mData->mSession.mRemoteControls.end();
12651 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12652 E_INVALIDARG);
12653 // This MUST be erase(it), not remove(*it) as the latter triggers a
12654 // very nasty use after free due to the place where the value "lives".
12655 mData->mSession.mRemoteControls.erase(it);
12656 }
12657
12658 /* signal the client watcher thread, because the client is going away */
12659 mParent->updateClientWatcher();
12660
12661 LogFlowThisFuncLeave();
12662 return S_OK;
12663}
12664
12665/**
12666 * @note Locks this object for writing.
12667 */
12668STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12669{
12670 LogFlowThisFuncEnter();
12671
12672 CheckComArgOutPointerValid(aProgress);
12673 CheckComArgOutPointerValid(aStateFilePath);
12674
12675 AutoCaller autoCaller(this);
12676 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12677
12678 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12679
12680 AssertReturn( mData->mMachineState == MachineState_Paused
12681 && mConsoleTaskData.mLastState == MachineState_Null
12682 && mConsoleTaskData.strStateFilePath.isEmpty(),
12683 E_FAIL);
12684
12685 /* create a progress object to track operation completion */
12686 ComObjPtr<Progress> pProgress;
12687 pProgress.createObject();
12688 pProgress->init(getVirtualBox(),
12689 static_cast<IMachine *>(this) /* aInitiator */,
12690 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12691 FALSE /* aCancelable */);
12692
12693 Utf8Str strStateFilePath;
12694 /* stateFilePath is null when the machine is not running */
12695 if (mData->mMachineState == MachineState_Paused)
12696 composeSavedStateFilename(strStateFilePath);
12697
12698 /* fill in the console task data */
12699 mConsoleTaskData.mLastState = mData->mMachineState;
12700 mConsoleTaskData.strStateFilePath = strStateFilePath;
12701 mConsoleTaskData.mProgress = pProgress;
12702
12703 /* set the state to Saving (this is expected by Console::SaveState()) */
12704 setMachineState(MachineState_Saving);
12705
12706 strStateFilePath.cloneTo(aStateFilePath);
12707 pProgress.queryInterfaceTo(aProgress);
12708
12709 return S_OK;
12710}
12711
12712/**
12713 * @note Locks mParent + this object for writing.
12714 */
12715STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12716{
12717 LogFlowThisFunc(("\n"));
12718
12719 AutoCaller autoCaller(this);
12720 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12721
12722 /* endSavingState() need mParent lock */
12723 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12724
12725 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12726 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12727 && mConsoleTaskData.mLastState != MachineState_Null
12728 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12729 E_FAIL);
12730
12731 /*
12732 * On failure, set the state to the state we had when BeginSavingState()
12733 * was called (this is expected by Console::SaveState() and the associated
12734 * task). On success the VM process already changed the state to
12735 * MachineState_Saved, so no need to do anything.
12736 */
12737 if (FAILED(iResult))
12738 setMachineState(mConsoleTaskData.mLastState);
12739
12740 return endSavingState(iResult, aErrMsg);
12741}
12742
12743/**
12744 * @note Locks this object for writing.
12745 */
12746STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12747{
12748 LogFlowThisFunc(("\n"));
12749
12750 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12751
12752 AutoCaller autoCaller(this);
12753 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12754
12755 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12756
12757 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12758 || mData->mMachineState == MachineState_Teleported
12759 || mData->mMachineState == MachineState_Aborted
12760 , E_FAIL); /** @todo setError. */
12761
12762 Utf8Str stateFilePathFull = aSavedStateFile;
12763 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
12764 if (RT_FAILURE(vrc))
12765 return setError(VBOX_E_FILE_ERROR,
12766 tr("Invalid saved state file path '%ls' (%Rrc)"),
12767 aSavedStateFile,
12768 vrc);
12769
12770 mSSData->strStateFilePath = stateFilePathFull;
12771
12772 /* The below setMachineState() will detect the state transition and will
12773 * update the settings file */
12774
12775 return setMachineState(MachineState_Saved);
12776}
12777
12778STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
12779 ComSafeArrayOut(BSTR, aValues),
12780 ComSafeArrayOut(LONG64, aTimestamps),
12781 ComSafeArrayOut(BSTR, aFlags))
12782{
12783 LogFlowThisFunc(("\n"));
12784
12785#ifdef VBOX_WITH_GUEST_PROPS
12786 using namespace guestProp;
12787
12788 AutoCaller autoCaller(this);
12789 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12790
12791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12792
12793 CheckComArgOutSafeArrayPointerValid(aNames);
12794 CheckComArgOutSafeArrayPointerValid(aValues);
12795 CheckComArgOutSafeArrayPointerValid(aTimestamps);
12796 CheckComArgOutSafeArrayPointerValid(aFlags);
12797
12798 size_t cEntries = mHWData->mGuestProperties.size();
12799 com::SafeArray<BSTR> names(cEntries);
12800 com::SafeArray<BSTR> values(cEntries);
12801 com::SafeArray<LONG64> timestamps(cEntries);
12802 com::SafeArray<BSTR> flags(cEntries);
12803 unsigned i = 0;
12804 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
12805 it != mHWData->mGuestProperties.end();
12806 ++it)
12807 {
12808 char szFlags[MAX_FLAGS_LEN + 1];
12809 it->first.cloneTo(&names[i]);
12810 it->second.strValue.cloneTo(&values[i]);
12811 timestamps[i] = it->second.mTimestamp;
12812 /* If it is NULL, keep it NULL. */
12813 if (it->second.mFlags)
12814 {
12815 writeFlags(it->second.mFlags, szFlags);
12816 Bstr(szFlags).cloneTo(&flags[i]);
12817 }
12818 else
12819 flags[i] = NULL;
12820 ++i;
12821 }
12822 names.detachTo(ComSafeArrayOutArg(aNames));
12823 values.detachTo(ComSafeArrayOutArg(aValues));
12824 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
12825 flags.detachTo(ComSafeArrayOutArg(aFlags));
12826 return S_OK;
12827#else
12828 ReturnComNotImplemented();
12829#endif
12830}
12831
12832STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
12833 IN_BSTR aValue,
12834 LONG64 aTimestamp,
12835 IN_BSTR aFlags)
12836{
12837 LogFlowThisFunc(("\n"));
12838
12839#ifdef VBOX_WITH_GUEST_PROPS
12840 using namespace guestProp;
12841
12842 CheckComArgStrNotEmptyOrNull(aName);
12843 CheckComArgNotNull(aValue);
12844 CheckComArgNotNull(aFlags);
12845
12846 try
12847 {
12848 /*
12849 * Convert input up front.
12850 */
12851 Utf8Str utf8Name(aName);
12852 uint32_t fFlags = NILFLAG;
12853 if (aFlags)
12854 {
12855 Utf8Str utf8Flags(aFlags);
12856 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
12857 AssertRCReturn(vrc, E_INVALIDARG);
12858 }
12859
12860 /*
12861 * Now grab the object lock, validate the state and do the update.
12862 */
12863 AutoCaller autoCaller(this);
12864 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12865
12866 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12867
12868 switch (mData->mMachineState)
12869 {
12870 case MachineState_Paused:
12871 case MachineState_Running:
12872 case MachineState_Teleporting:
12873 case MachineState_TeleportingPausedVM:
12874 case MachineState_LiveSnapshotting:
12875 case MachineState_DeletingSnapshotOnline:
12876 case MachineState_DeletingSnapshotPaused:
12877 case MachineState_Saving:
12878 break;
12879
12880 default:
12881#ifndef DEBUG_sunlover
12882 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
12883 VBOX_E_INVALID_VM_STATE);
12884#else
12885 return VBOX_E_INVALID_VM_STATE;
12886#endif
12887 }
12888
12889 setModified(IsModified_MachineData);
12890 mHWData.backup();
12891
12892 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
12893 if (it != mHWData->mGuestProperties.end())
12894 {
12895 if (RT_VALID_PTR(aValue) && *(aValue) != '\0')
12896 {
12897 it->second.strValue = aValue;
12898 it->second.mFlags = fFlags;
12899 it->second.mTimestamp = aTimestamp;
12900 }
12901 else
12902 mHWData->mGuestProperties.erase(it);
12903
12904 mData->mGuestPropertiesModified = TRUE;
12905 }
12906
12907 /*
12908 * Send a callback notification if appropriate
12909 */
12910 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
12911 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
12912 RTSTR_MAX,
12913 utf8Name.c_str(),
12914 RTSTR_MAX, NULL)
12915 )
12916 {
12917 alock.release();
12918
12919 mParent->onGuestPropertyChange(mData->mUuid,
12920 aName,
12921 aValue,
12922 aFlags);
12923 }
12924 }
12925 catch (...)
12926 {
12927 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
12928 }
12929 return S_OK;
12930#else
12931 ReturnComNotImplemented();
12932#endif
12933}
12934
12935STDMETHODIMP SessionMachine::LockMedia()
12936{
12937 AutoCaller autoCaller(this);
12938 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12939
12940 AutoMultiWriteLock2 alock(this->lockHandle(),
12941 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
12942
12943 AssertReturn( mData->mMachineState == MachineState_Starting
12944 || mData->mMachineState == MachineState_Restoring
12945 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
12946
12947 clearError();
12948 alock.release();
12949 return lockMedia();
12950}
12951
12952STDMETHODIMP SessionMachine::UnlockMedia()
12953{
12954 unlockMedia();
12955 return S_OK;
12956}
12957
12958STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
12959 IMediumAttachment **aNewAttachment)
12960{
12961 CheckComArgNotNull(aAttachment);
12962 CheckComArgOutPointerValid(aNewAttachment);
12963
12964 AutoCaller autoCaller(this);
12965 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12966
12967 // request the host lock first, since might be calling Host methods for getting host drives;
12968 // next, protect the media tree all the while we're in here, as well as our member variables
12969 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
12970 this->lockHandle(),
12971 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
12972
12973 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
12974
12975 Bstr ctrlName;
12976 LONG lPort;
12977 LONG lDevice;
12978 bool fTempEject;
12979 {
12980 AutoCaller autoAttachCaller(this);
12981 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12982
12983 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12984
12985 /* Need to query the details first, as the IMediumAttachment reference
12986 * might be to the original settings, which we are going to change. */
12987 ctrlName = pAttach->getControllerName();
12988 lPort = pAttach->getPort();
12989 lDevice = pAttach->getDevice();
12990 fTempEject = pAttach->getTempEject();
12991 }
12992
12993 if (!fTempEject)
12994 {
12995 /* Remember previously mounted medium. The medium before taking the
12996 * backup is not necessarily the same thing. */
12997 ComObjPtr<Medium> oldmedium;
12998 oldmedium = pAttach->getMedium();
12999
13000 setModified(IsModified_Storage);
13001 mMediaData.backup();
13002
13003 // The backup operation makes the pAttach reference point to the
13004 // old settings. Re-get the correct reference.
13005 pAttach = findAttachment(mMediaData->mAttachments,
13006 ctrlName.raw(),
13007 lPort,
13008 lDevice);
13009
13010 {
13011 AutoCaller autoAttachCaller(this);
13012 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13013
13014 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13015 if (!oldmedium.isNull())
13016 oldmedium->removeBackReference(mData->mUuid);
13017
13018 pAttach->updateMedium(NULL);
13019 pAttach->updateEjected();
13020 }
13021
13022 setModified(IsModified_Storage);
13023 }
13024 else
13025 {
13026 {
13027 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13028 pAttach->updateEjected();
13029 }
13030 }
13031
13032 pAttach.queryInterfaceTo(aNewAttachment);
13033
13034 return S_OK;
13035}
13036
13037// public methods only for internal purposes
13038/////////////////////////////////////////////////////////////////////////////
13039
13040/**
13041 * Called from the client watcher thread to check for expected or unexpected
13042 * death of the client process that has a direct session to this machine.
13043 *
13044 * On Win32 and on OS/2, this method is called only when we've got the
13045 * mutex (i.e. the client has either died or terminated normally) so it always
13046 * returns @c true (the client is terminated, the session machine is
13047 * uninitialized).
13048 *
13049 * On other platforms, the method returns @c true if the client process has
13050 * terminated normally or abnormally and the session machine was uninitialized,
13051 * and @c false if the client process is still alive.
13052 *
13053 * @note Locks this object for writing.
13054 */
13055bool SessionMachine::checkForDeath()
13056{
13057 Uninit::Reason reason;
13058 bool terminated = false;
13059
13060 /* Enclose autoCaller with a block because calling uninit() from under it
13061 * will deadlock. */
13062 {
13063 AutoCaller autoCaller(this);
13064 if (!autoCaller.isOk())
13065 {
13066 /* return true if not ready, to cause the client watcher to exclude
13067 * the corresponding session from watching */
13068 LogFlowThisFunc(("Already uninitialized!\n"));
13069 return true;
13070 }
13071
13072 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13073
13074 /* Determine the reason of death: if the session state is Closing here,
13075 * everything is fine. Otherwise it means that the client did not call
13076 * OnSessionEnd() before it released the IPC semaphore. This may happen
13077 * either because the client process has abnormally terminated, or
13078 * because it simply forgot to call ISession::Close() before exiting. We
13079 * threat the latter also as an abnormal termination (see
13080 * Session::uninit() for details). */
13081 reason = mData->mSession.mState == SessionState_Unlocking ?
13082 Uninit::Normal :
13083 Uninit::Abnormal;
13084
13085#if defined(RT_OS_WINDOWS)
13086
13087 AssertMsg(mIPCSem, ("semaphore must be created"));
13088
13089 /* release the IPC mutex */
13090 ::ReleaseMutex(mIPCSem);
13091
13092 terminated = true;
13093
13094#elif defined(RT_OS_OS2)
13095
13096 AssertMsg(mIPCSem, ("semaphore must be created"));
13097
13098 /* release the IPC mutex */
13099 ::DosReleaseMutexSem(mIPCSem);
13100
13101 terminated = true;
13102
13103#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
13104
13105 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
13106
13107 int val = ::semctl(mIPCSem, 0, GETVAL);
13108 if (val > 0)
13109 {
13110 /* the semaphore is signaled, meaning the session is terminated */
13111 terminated = true;
13112 }
13113
13114#else
13115# error "Port me!"
13116#endif
13117
13118 } /* AutoCaller block */
13119
13120 if (terminated)
13121 uninit(reason);
13122
13123 return terminated;
13124}
13125
13126/**
13127 * @note Locks this object for reading.
13128 */
13129HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13130{
13131 LogFlowThisFunc(("\n"));
13132
13133 AutoCaller autoCaller(this);
13134 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13135
13136 ComPtr<IInternalSessionControl> directControl;
13137 {
13138 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13139 directControl = mData->mSession.mDirectControl;
13140 }
13141
13142 /* ignore notifications sent after #OnSessionEnd() is called */
13143 if (!directControl)
13144 return S_OK;
13145
13146 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13147}
13148
13149/**
13150 * @note Locks this object for reading.
13151 */
13152HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13153 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
13154{
13155 LogFlowThisFunc(("\n"));
13156
13157 AutoCaller autoCaller(this);
13158 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13159
13160 ComPtr<IInternalSessionControl> directControl;
13161 {
13162 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13163 directControl = mData->mSession.mDirectControl;
13164 }
13165
13166 /* ignore notifications sent after #OnSessionEnd() is called */
13167 if (!directControl)
13168 return S_OK;
13169 /*
13170 * instead acting like callback we ask IVirtualBox deliver corresponding event
13171 */
13172
13173 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13174 return S_OK;
13175}
13176
13177/**
13178 * @note Locks this object for reading.
13179 */
13180HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
13181{
13182 LogFlowThisFunc(("\n"));
13183
13184 AutoCaller autoCaller(this);
13185 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13186
13187 ComPtr<IInternalSessionControl> directControl;
13188 {
13189 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13190 directControl = mData->mSession.mDirectControl;
13191 }
13192
13193 /* ignore notifications sent after #OnSessionEnd() is called */
13194 if (!directControl)
13195 return S_OK;
13196
13197 return directControl->OnSerialPortChange(serialPort);
13198}
13199
13200/**
13201 * @note Locks this object for reading.
13202 */
13203HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
13204{
13205 LogFlowThisFunc(("\n"));
13206
13207 AutoCaller autoCaller(this);
13208 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13209
13210 ComPtr<IInternalSessionControl> directControl;
13211 {
13212 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13213 directControl = mData->mSession.mDirectControl;
13214 }
13215
13216 /* ignore notifications sent after #OnSessionEnd() is called */
13217 if (!directControl)
13218 return S_OK;
13219
13220 return directControl->OnParallelPortChange(parallelPort);
13221}
13222
13223/**
13224 * @note Locks this object for reading.
13225 */
13226HRESULT SessionMachine::onStorageControllerChange()
13227{
13228 LogFlowThisFunc(("\n"));
13229
13230 AutoCaller autoCaller(this);
13231 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13232
13233 ComPtr<IInternalSessionControl> directControl;
13234 {
13235 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13236 directControl = mData->mSession.mDirectControl;
13237 }
13238
13239 /* ignore notifications sent after #OnSessionEnd() is called */
13240 if (!directControl)
13241 return S_OK;
13242
13243 return directControl->OnStorageControllerChange();
13244}
13245
13246/**
13247 * @note Locks this object for reading.
13248 */
13249HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13250{
13251 LogFlowThisFunc(("\n"));
13252
13253 AutoCaller autoCaller(this);
13254 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13255
13256 ComPtr<IInternalSessionControl> directControl;
13257 {
13258 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13259 directControl = mData->mSession.mDirectControl;
13260 }
13261
13262 /* ignore notifications sent after #OnSessionEnd() is called */
13263 if (!directControl)
13264 return S_OK;
13265
13266 return directControl->OnMediumChange(aAttachment, aForce);
13267}
13268
13269/**
13270 * @note Locks this object for reading.
13271 */
13272HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
13273{
13274 LogFlowThisFunc(("\n"));
13275
13276 AutoCaller autoCaller(this);
13277 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13278
13279 ComPtr<IInternalSessionControl> directControl;
13280 {
13281 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13282 directControl = mData->mSession.mDirectControl;
13283 }
13284
13285 /* ignore notifications sent after #OnSessionEnd() is called */
13286 if (!directControl)
13287 return S_OK;
13288
13289 return directControl->OnCPUChange(aCPU, aRemove);
13290}
13291
13292HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
13293{
13294 LogFlowThisFunc(("\n"));
13295
13296 AutoCaller autoCaller(this);
13297 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13298
13299 ComPtr<IInternalSessionControl> directControl;
13300 {
13301 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13302 directControl = mData->mSession.mDirectControl;
13303 }
13304
13305 /* ignore notifications sent after #OnSessionEnd() is called */
13306 if (!directControl)
13307 return S_OK;
13308
13309 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13310}
13311
13312/**
13313 * @note Locks this object for reading.
13314 */
13315HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
13316{
13317 LogFlowThisFunc(("\n"));
13318
13319 AutoCaller autoCaller(this);
13320 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13321
13322 ComPtr<IInternalSessionControl> directControl;
13323 {
13324 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13325 directControl = mData->mSession.mDirectControl;
13326 }
13327
13328 /* ignore notifications sent after #OnSessionEnd() is called */
13329 if (!directControl)
13330 return S_OK;
13331
13332 return directControl->OnVRDEServerChange(aRestart);
13333}
13334
13335/**
13336 * @note Locks this object for reading.
13337 */
13338HRESULT SessionMachine::onUSBControllerChange()
13339{
13340 LogFlowThisFunc(("\n"));
13341
13342 AutoCaller autoCaller(this);
13343 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13344
13345 ComPtr<IInternalSessionControl> directControl;
13346 {
13347 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13348 directControl = mData->mSession.mDirectControl;
13349 }
13350
13351 /* ignore notifications sent after #OnSessionEnd() is called */
13352 if (!directControl)
13353 return S_OK;
13354
13355 return directControl->OnUSBControllerChange();
13356}
13357
13358/**
13359 * @note Locks this object for reading.
13360 */
13361HRESULT SessionMachine::onSharedFolderChange()
13362{
13363 LogFlowThisFunc(("\n"));
13364
13365 AutoCaller autoCaller(this);
13366 AssertComRCReturnRC(autoCaller.rc());
13367
13368 ComPtr<IInternalSessionControl> directControl;
13369 {
13370 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13371 directControl = mData->mSession.mDirectControl;
13372 }
13373
13374 /* ignore notifications sent after #OnSessionEnd() is called */
13375 if (!directControl)
13376 return S_OK;
13377
13378 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13379}
13380
13381/**
13382 * @note Locks this object for reading.
13383 */
13384HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
13385{
13386 LogFlowThisFunc(("\n"));
13387
13388 AutoCaller autoCaller(this);
13389 AssertComRCReturnRC(autoCaller.rc());
13390
13391 ComPtr<IInternalSessionControl> directControl;
13392 {
13393 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13394 directControl = mData->mSession.mDirectControl;
13395 }
13396
13397 /* ignore notifications sent after #OnSessionEnd() is called */
13398 if (!directControl)
13399 return S_OK;
13400
13401 return directControl->OnClipboardModeChange(aClipboardMode);
13402}
13403
13404/**
13405 * @note Locks this object for reading.
13406 */
13407HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
13408{
13409 LogFlowThisFunc(("\n"));
13410
13411 AutoCaller autoCaller(this);
13412 AssertComRCReturnRC(autoCaller.rc());
13413
13414 ComPtr<IInternalSessionControl> directControl;
13415 {
13416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13417 directControl = mData->mSession.mDirectControl;
13418 }
13419
13420 /* ignore notifications sent after #OnSessionEnd() is called */
13421 if (!directControl)
13422 return S_OK;
13423
13424 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
13425}
13426
13427/**
13428 * @note Locks this object for reading.
13429 */
13430HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13431{
13432 LogFlowThisFunc(("\n"));
13433
13434 AutoCaller autoCaller(this);
13435 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13436
13437 ComPtr<IInternalSessionControl> directControl;
13438 {
13439 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13440 directControl = mData->mSession.mDirectControl;
13441 }
13442
13443 /* ignore notifications sent after #OnSessionEnd() is called */
13444 if (!directControl)
13445 return S_OK;
13446
13447 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13448}
13449
13450/**
13451 * @note Locks this object for reading.
13452 */
13453HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove)
13454{
13455 LogFlowThisFunc(("\n"));
13456
13457 AutoCaller autoCaller(this);
13458 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13459
13460 ComPtr<IInternalSessionControl> directControl;
13461 {
13462 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13463 directControl = mData->mSession.mDirectControl;
13464 }
13465
13466 /* ignore notifications sent after #OnSessionEnd() is called */
13467 if (!directControl)
13468 return S_OK;
13469
13470 return directControl->OnStorageDeviceChange(aAttachment, aRemove);
13471}
13472
13473/**
13474 * Returns @c true if this machine's USB controller reports it has a matching
13475 * filter for the given USB device and @c false otherwise.
13476 *
13477 * @note locks this object for reading.
13478 */
13479bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13480{
13481 AutoCaller autoCaller(this);
13482 /* silently return if not ready -- this method may be called after the
13483 * direct machine session has been called */
13484 if (!autoCaller.isOk())
13485 return false;
13486
13487#ifdef VBOX_WITH_USB
13488 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13489
13490 switch (mData->mMachineState)
13491 {
13492 case MachineState_Starting:
13493 case MachineState_Restoring:
13494 case MachineState_TeleportingIn:
13495 case MachineState_Paused:
13496 case MachineState_Running:
13497 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13498 * elsewhere... */
13499 alock.release();
13500 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
13501 default: break;
13502 }
13503#else
13504 NOREF(aDevice);
13505 NOREF(aMaskedIfs);
13506#endif
13507 return false;
13508}
13509
13510/**
13511 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13512 */
13513HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
13514 IVirtualBoxErrorInfo *aError,
13515 ULONG aMaskedIfs)
13516{
13517 LogFlowThisFunc(("\n"));
13518
13519 AutoCaller autoCaller(this);
13520
13521 /* This notification may happen after the machine object has been
13522 * uninitialized (the session was closed), so don't assert. */
13523 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13524
13525 ComPtr<IInternalSessionControl> directControl;
13526 {
13527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13528 directControl = mData->mSession.mDirectControl;
13529 }
13530
13531 /* fail on notifications sent after #OnSessionEnd() is called, it is
13532 * expected by the caller */
13533 if (!directControl)
13534 return E_FAIL;
13535
13536 /* No locks should be held at this point. */
13537 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13538 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13539
13540 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13541}
13542
13543/**
13544 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13545 */
13546HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
13547 IVirtualBoxErrorInfo *aError)
13548{
13549 LogFlowThisFunc(("\n"));
13550
13551 AutoCaller autoCaller(this);
13552
13553 /* This notification may happen after the machine object has been
13554 * uninitialized (the session was closed), so don't assert. */
13555 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13556
13557 ComPtr<IInternalSessionControl> directControl;
13558 {
13559 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13560 directControl = mData->mSession.mDirectControl;
13561 }
13562
13563 /* fail on notifications sent after #OnSessionEnd() is called, it is
13564 * expected by the caller */
13565 if (!directControl)
13566 return E_FAIL;
13567
13568 /* No locks should be held at this point. */
13569 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13570 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13571
13572 return directControl->OnUSBDeviceDetach(aId, aError);
13573}
13574
13575// protected methods
13576/////////////////////////////////////////////////////////////////////////////
13577
13578/**
13579 * Helper method to finalize saving the state.
13580 *
13581 * @note Must be called from under this object's lock.
13582 *
13583 * @param aRc S_OK if the snapshot has been taken successfully
13584 * @param aErrMsg human readable error message for failure
13585 *
13586 * @note Locks mParent + this objects for writing.
13587 */
13588HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13589{
13590 LogFlowThisFuncEnter();
13591
13592 AutoCaller autoCaller(this);
13593 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13594
13595 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13596
13597 HRESULT rc = S_OK;
13598
13599 if (SUCCEEDED(aRc))
13600 {
13601 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13602
13603 /* save all VM settings */
13604 rc = saveSettings(NULL);
13605 // no need to check whether VirtualBox.xml needs saving also since
13606 // we can't have a name change pending at this point
13607 }
13608 else
13609 {
13610 // delete the saved state file (it might have been already created);
13611 // we need not check whether this is shared with a snapshot here because
13612 // we certainly created this saved state file here anew
13613 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13614 }
13615
13616 /* notify the progress object about operation completion */
13617 Assert(mConsoleTaskData.mProgress);
13618 if (SUCCEEDED(aRc))
13619 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13620 else
13621 {
13622 if (aErrMsg.length())
13623 mConsoleTaskData.mProgress->notifyComplete(aRc,
13624 COM_IIDOF(ISession),
13625 getComponentName(),
13626 aErrMsg.c_str());
13627 else
13628 mConsoleTaskData.mProgress->notifyComplete(aRc);
13629 }
13630
13631 /* clear out the temporary saved state data */
13632 mConsoleTaskData.mLastState = MachineState_Null;
13633 mConsoleTaskData.strStateFilePath.setNull();
13634 mConsoleTaskData.mProgress.setNull();
13635
13636 LogFlowThisFuncLeave();
13637 return rc;
13638}
13639
13640/**
13641 * Deletes the given file if it is no longer in use by either the current machine state
13642 * (if the machine is "saved") or any of the machine's snapshots.
13643 *
13644 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13645 * but is different for each SnapshotMachine. When calling this, the order of calling this
13646 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13647 * is therefore critical. I know, it's all rather messy.
13648 *
13649 * @param strStateFile
13650 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
13651 */
13652void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
13653 Snapshot *pSnapshotToIgnore)
13654{
13655 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13656 if ( (strStateFile.isNotEmpty())
13657 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13658 )
13659 // ... and it must also not be shared with other snapshots
13660 if ( !mData->mFirstSnapshot
13661 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13662 // this checks the SnapshotMachine's state file paths
13663 )
13664 RTFileDelete(strStateFile.c_str());
13665}
13666
13667/**
13668 * Locks the attached media.
13669 *
13670 * All attached hard disks are locked for writing and DVD/floppy are locked for
13671 * reading. Parents of attached hard disks (if any) are locked for reading.
13672 *
13673 * This method also performs accessibility check of all media it locks: if some
13674 * media is inaccessible, the method will return a failure and a bunch of
13675 * extended error info objects per each inaccessible medium.
13676 *
13677 * Note that this method is atomic: if it returns a success, all media are
13678 * locked as described above; on failure no media is locked at all (all
13679 * succeeded individual locks will be undone).
13680 *
13681 * The caller is responsible for doing the necessary state sanity checks.
13682 *
13683 * The locks made by this method must be undone by calling #unlockMedia() when
13684 * no more needed.
13685 */
13686HRESULT SessionMachine::lockMedia()
13687{
13688 AutoCaller autoCaller(this);
13689 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13690
13691 AutoMultiWriteLock2 alock(this->lockHandle(),
13692 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13693
13694 /* bail out if trying to lock things with already set up locking */
13695 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13696
13697 MultiResult mrc(S_OK);
13698
13699 /* Collect locking information for all medium objects attached to the VM. */
13700 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13701 it != mMediaData->mAttachments.end();
13702 ++it)
13703 {
13704 MediumAttachment* pAtt = *it;
13705 DeviceType_T devType = pAtt->getType();
13706 Medium *pMedium = pAtt->getMedium();
13707
13708 MediumLockList *pMediumLockList(new MediumLockList());
13709 // There can be attachments without a medium (floppy/dvd), and thus
13710 // it's impossible to create a medium lock list. It still makes sense
13711 // to have the empty medium lock list in the map in case a medium is
13712 // attached later.
13713 if (pMedium != NULL)
13714 {
13715 MediumType_T mediumType = pMedium->getType();
13716 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13717 || mediumType == MediumType_Shareable;
13718 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13719
13720 alock.release();
13721 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13722 !fIsReadOnlyLock /* fMediumLockWrite */,
13723 NULL,
13724 *pMediumLockList);
13725 alock.acquire();
13726 if (FAILED(mrc))
13727 {
13728 delete pMediumLockList;
13729 mData->mSession.mLockedMedia.Clear();
13730 break;
13731 }
13732 }
13733
13734 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13735 if (FAILED(rc))
13736 {
13737 mData->mSession.mLockedMedia.Clear();
13738 mrc = setError(rc,
13739 tr("Collecting locking information for all attached media failed"));
13740 break;
13741 }
13742 }
13743
13744 if (SUCCEEDED(mrc))
13745 {
13746 /* Now lock all media. If this fails, nothing is locked. */
13747 alock.release();
13748 HRESULT rc = mData->mSession.mLockedMedia.Lock();
13749 alock.acquire();
13750 if (FAILED(rc))
13751 {
13752 mrc = setError(rc,
13753 tr("Locking of attached media failed"));
13754 }
13755 }
13756
13757 return mrc;
13758}
13759
13760/**
13761 * Undoes the locks made by by #lockMedia().
13762 */
13763void SessionMachine::unlockMedia()
13764{
13765 AutoCaller autoCaller(this);
13766 AssertComRCReturnVoid(autoCaller.rc());
13767
13768 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13769
13770 /* we may be holding important error info on the current thread;
13771 * preserve it */
13772 ErrorInfoKeeper eik;
13773
13774 HRESULT rc = mData->mSession.mLockedMedia.Clear();
13775 AssertComRC(rc);
13776}
13777
13778/**
13779 * Helper to change the machine state (reimplementation).
13780 *
13781 * @note Locks this object for writing.
13782 * @note This method must not call saveSettings or SaveSettings, otherwise
13783 * it can cause crashes in random places due to unexpectedly committing
13784 * the current settings. The caller is responsible for that. The call
13785 * to saveStateSettings is fine, because this method does not commit.
13786 */
13787HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
13788{
13789 LogFlowThisFuncEnter();
13790 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
13791
13792 AutoCaller autoCaller(this);
13793 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13794
13795 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13796
13797 MachineState_T oldMachineState = mData->mMachineState;
13798
13799 AssertMsgReturn(oldMachineState != aMachineState,
13800 ("oldMachineState=%s, aMachineState=%s\n",
13801 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
13802 E_FAIL);
13803
13804 HRESULT rc = S_OK;
13805
13806 int stsFlags = 0;
13807 bool deleteSavedState = false;
13808
13809 /* detect some state transitions */
13810
13811 if ( ( oldMachineState == MachineState_Saved
13812 && aMachineState == MachineState_Restoring)
13813 || ( ( oldMachineState == MachineState_PoweredOff
13814 || oldMachineState == MachineState_Teleported
13815 || oldMachineState == MachineState_Aborted
13816 )
13817 && ( aMachineState == MachineState_TeleportingIn
13818 || aMachineState == MachineState_Starting
13819 )
13820 )
13821 )
13822 {
13823 /* The EMT thread is about to start */
13824
13825 /* Nothing to do here for now... */
13826
13827 /// @todo NEWMEDIA don't let mDVDDrive and other children
13828 /// change anything when in the Starting/Restoring state
13829 }
13830 else if ( ( oldMachineState == MachineState_Running
13831 || oldMachineState == MachineState_Paused
13832 || oldMachineState == MachineState_Teleporting
13833 || oldMachineState == MachineState_LiveSnapshotting
13834 || oldMachineState == MachineState_Stuck
13835 || oldMachineState == MachineState_Starting
13836 || oldMachineState == MachineState_Stopping
13837 || oldMachineState == MachineState_Saving
13838 || oldMachineState == MachineState_Restoring
13839 || oldMachineState == MachineState_TeleportingPausedVM
13840 || oldMachineState == MachineState_TeleportingIn
13841 )
13842 && ( aMachineState == MachineState_PoweredOff
13843 || aMachineState == MachineState_Saved
13844 || aMachineState == MachineState_Teleported
13845 || aMachineState == MachineState_Aborted
13846 )
13847 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
13848 * snapshot */
13849 && ( mConsoleTaskData.mSnapshot.isNull()
13850 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
13851 )
13852 )
13853 {
13854 /* The EMT thread has just stopped, unlock attached media. Note that as
13855 * opposed to locking that is done from Console, we do unlocking here
13856 * because the VM process may have aborted before having a chance to
13857 * properly unlock all media it locked. */
13858
13859 unlockMedia();
13860 }
13861
13862 if (oldMachineState == MachineState_Restoring)
13863 {
13864 if (aMachineState != MachineState_Saved)
13865 {
13866 /*
13867 * delete the saved state file once the machine has finished
13868 * restoring from it (note that Console sets the state from
13869 * Restoring to Saved if the VM couldn't restore successfully,
13870 * to give the user an ability to fix an error and retry --
13871 * we keep the saved state file in this case)
13872 */
13873 deleteSavedState = true;
13874 }
13875 }
13876 else if ( oldMachineState == MachineState_Saved
13877 && ( aMachineState == MachineState_PoweredOff
13878 || aMachineState == MachineState_Aborted
13879 || aMachineState == MachineState_Teleported
13880 )
13881 )
13882 {
13883 /*
13884 * delete the saved state after Console::ForgetSavedState() is called
13885 * or if the VM process (owning a direct VM session) crashed while the
13886 * VM was Saved
13887 */
13888
13889 /// @todo (dmik)
13890 // Not sure that deleting the saved state file just because of the
13891 // client death before it attempted to restore the VM is a good
13892 // thing. But when it crashes we need to go to the Aborted state
13893 // which cannot have the saved state file associated... The only
13894 // way to fix this is to make the Aborted condition not a VM state
13895 // but a bool flag: i.e., when a crash occurs, set it to true and
13896 // change the state to PoweredOff or Saved depending on the
13897 // saved state presence.
13898
13899 deleteSavedState = true;
13900 mData->mCurrentStateModified = TRUE;
13901 stsFlags |= SaveSTS_CurStateModified;
13902 }
13903
13904 if ( aMachineState == MachineState_Starting
13905 || aMachineState == MachineState_Restoring
13906 || aMachineState == MachineState_TeleportingIn
13907 )
13908 {
13909 /* set the current state modified flag to indicate that the current
13910 * state is no more identical to the state in the
13911 * current snapshot */
13912 if (!mData->mCurrentSnapshot.isNull())
13913 {
13914 mData->mCurrentStateModified = TRUE;
13915 stsFlags |= SaveSTS_CurStateModified;
13916 }
13917 }
13918
13919 if (deleteSavedState)
13920 {
13921 if (mRemoveSavedState)
13922 {
13923 Assert(!mSSData->strStateFilePath.isEmpty());
13924
13925 // it is safe to delete the saved state file if ...
13926 if ( !mData->mFirstSnapshot // ... we have no snapshots or
13927 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
13928 // ... none of the snapshots share the saved state file
13929 )
13930 RTFileDelete(mSSData->strStateFilePath.c_str());
13931 }
13932
13933 mSSData->strStateFilePath.setNull();
13934 stsFlags |= SaveSTS_StateFilePath;
13935 }
13936
13937 /* redirect to the underlying peer machine */
13938 mPeer->setMachineState(aMachineState);
13939
13940 if ( aMachineState == MachineState_PoweredOff
13941 || aMachineState == MachineState_Teleported
13942 || aMachineState == MachineState_Aborted
13943 || aMachineState == MachineState_Saved)
13944 {
13945 /* the machine has stopped execution
13946 * (or the saved state file was adopted) */
13947 stsFlags |= SaveSTS_StateTimeStamp;
13948 }
13949
13950 if ( ( oldMachineState == MachineState_PoweredOff
13951 || oldMachineState == MachineState_Aborted
13952 || oldMachineState == MachineState_Teleported
13953 )
13954 && aMachineState == MachineState_Saved)
13955 {
13956 /* the saved state file was adopted */
13957 Assert(!mSSData->strStateFilePath.isEmpty());
13958 stsFlags |= SaveSTS_StateFilePath;
13959 }
13960
13961#ifdef VBOX_WITH_GUEST_PROPS
13962 if ( aMachineState == MachineState_PoweredOff
13963 || aMachineState == MachineState_Aborted
13964 || aMachineState == MachineState_Teleported)
13965 {
13966 /* Make sure any transient guest properties get removed from the
13967 * property store on shutdown. */
13968
13969 HWData::GuestPropertyMap::const_iterator it;
13970 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
13971 if (!fNeedsSaving)
13972 for (it = mHWData->mGuestProperties.begin();
13973 it != mHWData->mGuestProperties.end(); ++it)
13974 if ( (it->second.mFlags & guestProp::TRANSIENT)
13975 || (it->second.mFlags & guestProp::TRANSRESET))
13976 {
13977 fNeedsSaving = true;
13978 break;
13979 }
13980 if (fNeedsSaving)
13981 {
13982 mData->mCurrentStateModified = TRUE;
13983 stsFlags |= SaveSTS_CurStateModified;
13984 }
13985 }
13986#endif
13987
13988 rc = saveStateSettings(stsFlags);
13989
13990 if ( ( oldMachineState != MachineState_PoweredOff
13991 && oldMachineState != MachineState_Aborted
13992 && oldMachineState != MachineState_Teleported
13993 )
13994 && ( aMachineState == MachineState_PoweredOff
13995 || aMachineState == MachineState_Aborted
13996 || aMachineState == MachineState_Teleported
13997 )
13998 )
13999 {
14000 /* we've been shut down for any reason */
14001 /* no special action so far */
14002 }
14003
14004 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14005 LogFlowThisFuncLeave();
14006 return rc;
14007}
14008
14009/**
14010 * Sends the current machine state value to the VM process.
14011 *
14012 * @note Locks this object for reading, then calls a client process.
14013 */
14014HRESULT SessionMachine::updateMachineStateOnClient()
14015{
14016 AutoCaller autoCaller(this);
14017 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14018
14019 ComPtr<IInternalSessionControl> directControl;
14020 {
14021 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14022 AssertReturn(!!mData, E_FAIL);
14023 directControl = mData->mSession.mDirectControl;
14024
14025 /* directControl may be already set to NULL here in #OnSessionEnd()
14026 * called too early by the direct session process while there is still
14027 * some operation (like deleting the snapshot) in progress. The client
14028 * process in this case is waiting inside Session::close() for the
14029 * "end session" process object to complete, while #uninit() called by
14030 * #checkForDeath() on the Watcher thread is waiting for the pending
14031 * operation to complete. For now, we accept this inconsistent behavior
14032 * and simply do nothing here. */
14033
14034 if (mData->mSession.mState == SessionState_Unlocking)
14035 return S_OK;
14036
14037 AssertReturn(!directControl.isNull(), E_FAIL);
14038 }
14039
14040 return directControl->UpdateMachineState(mData->mMachineState);
14041}
Note: See TracBrowser for help on using the repository browser.

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