VirtualBox

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

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

VPX: default 1024x768

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 478.7 KB
Line 
1/* $Id: MachineImpl.cpp 45770 2013-04-26 10:01:43Z 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.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
27# include <errno.h>
28# include <sys/types.h>
29# include <sys/stat.h>
30# include <sys/ipc.h>
31# include <sys/sem.h>
32#endif
33
34#include "Logging.h"
35#include "VirtualBoxImpl.h"
36#include "MachineImpl.h"
37#include "ProgressImpl.h"
38#include "ProgressProxyImpl.h"
39#include "MediumAttachmentImpl.h"
40#include "MediumImpl.h"
41#include "MediumLock.h"
42#include "USBControllerImpl.h"
43#include "HostImpl.h"
44#include "SharedFolderImpl.h"
45#include "GuestOSTypeImpl.h"
46#include "VirtualBoxErrorInfoImpl.h"
47#include "GuestImpl.h"
48#include "StorageControllerImpl.h"
49#include "DisplayImpl.h"
50#include "DisplayUtils.h"
51#include "BandwidthControlImpl.h"
52#include "MachineImplCloneVM.h"
53#include "AutostartDb.h"
54
55// generated header
56#include "VBoxEvents.h"
57
58#ifdef VBOX_WITH_USB
59# include "USBProxyService.h"
60#endif
61
62#include "AutoCaller.h"
63#include "HashedPw.h"
64#include "Performance.h"
65
66#include <iprt/asm.h>
67#include <iprt/path.h>
68#include <iprt/dir.h>
69#include <iprt/env.h>
70#include <iprt/lockvalidator.h>
71#include <iprt/process.h>
72#include <iprt/cpp/utils.h>
73#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
74#include <iprt/sha.h>
75#include <iprt/string.h>
76
77#include <VBox/com/array.h>
78#include <VBox/com/list.h>
79
80#include <VBox/err.h>
81#include <VBox/param.h>
82#include <VBox/settings.h>
83#include <VBox/vmm/ssm.h>
84
85#ifdef VBOX_WITH_GUEST_PROPS
86# include <VBox/HostServices/GuestPropertySvc.h>
87# include <VBox/com/array.h>
88#endif
89
90#include "VBox/com/MultiResult.h"
91
92#include <algorithm>
93
94#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
95# define HOSTSUFF_EXE ".exe"
96#else /* !RT_OS_WINDOWS */
97# define HOSTSUFF_EXE ""
98#endif /* !RT_OS_WINDOWS */
99
100// defines / prototypes
101/////////////////////////////////////////////////////////////////////////////
102
103/////////////////////////////////////////////////////////////////////////////
104// Machine::Data structure
105/////////////////////////////////////////////////////////////////////////////
106
107Machine::Data::Data()
108{
109 mRegistered = FALSE;
110 pMachineConfigFile = NULL;
111 /* Contains hints on what has changed when the user is using the VM (config
112 * changes, running the VM, ...). This is used to decide if a config needs
113 * to be written to disk. */
114 flModifications = 0;
115 /* VM modification usually also trigger setting the current state to
116 * "Modified". Although this is not always the case. An e.g. is the VM
117 * initialization phase or when snapshot related data is changed. The
118 * actually behavior is controlled by the following flag. */
119 m_fAllowStateModification = false;
120 mAccessible = FALSE;
121 /* mUuid is initialized in Machine::init() */
122
123 mMachineState = MachineState_PoweredOff;
124 RTTimeNow(&mLastStateChange);
125
126 mMachineStateDeps = 0;
127 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
128 mMachineStateChangePending = 0;
129
130 mCurrentStateModified = TRUE;
131 mGuestPropertiesModified = FALSE;
132
133 mSession.mPID = NIL_RTPROCESS;
134 mSession.mState = SessionState_Unlocked;
135}
136
137Machine::Data::~Data()
138{
139 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
140 {
141 RTSemEventMultiDestroy(mMachineStateDepsSem);
142 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
143 }
144 if (pMachineConfigFile)
145 {
146 delete pMachineConfigFile;
147 pMachineConfigFile = NULL;
148 }
149}
150
151/////////////////////////////////////////////////////////////////////////////
152// Machine::HWData structure
153/////////////////////////////////////////////////////////////////////////////
154
155Machine::HWData::HWData()
156{
157 /* default values for a newly created machine */
158 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
159 mMemorySize = 128;
160 mCPUCount = 1;
161 mCPUHotPlugEnabled = false;
162 mMemoryBalloonSize = 0;
163 mPageFusionEnabled = false;
164 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
165 mVRAMSize = 8;
166 mAccelerate3DEnabled = false;
167 mAccelerate2DVideoEnabled = false;
168 mMonitorCount = 1;
169 mVideoCaptureFile = "Test.webm";
170 mVideoCaptureWidth = 1024;
171 mVideoCaptureHeight = 768;
172 mVideoCaptureEnabled = false;
173
174 mHWVirtExEnabled = true;
175 mHWVirtExNestedPagingEnabled = true;
176#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
177 mHWVirtExLargePagesEnabled = true;
178#else
179 /* Not supported on 32 bits hosts. */
180 mHWVirtExLargePagesEnabled = false;
181#endif
182 mHWVirtExVPIDEnabled = true;
183 mHWVirtExForceEnabled = false;
184#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
185 mHWVirtExExclusive = false;
186#else
187 mHWVirtExExclusive = true;
188#endif
189#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
190 mPAEEnabled = true;
191#else
192 mPAEEnabled = false;
193#endif
194 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
195 mSyntheticCpu = false;
196 mHPETEnabled = false;
197
198 /* default boot order: floppy - DVD - HDD */
199 mBootOrder[0] = DeviceType_Floppy;
200 mBootOrder[1] = DeviceType_DVD;
201 mBootOrder[2] = DeviceType_HardDisk;
202 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
203 mBootOrder[i] = DeviceType_Null;
204
205 mClipboardMode = ClipboardMode_Disabled;
206 mDragAndDropMode = DragAndDropMode_Disabled;
207 mGuestPropertyNotificationPatterns = "";
208
209 mFirmwareType = FirmwareType_BIOS;
210 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
211 mPointingHIDType = PointingHIDType_PS2Mouse;
212 mChipsetType = ChipsetType_PIIX3;
213 mEmulatedUSBWebcamEnabled = FALSE;
214 mEmulatedUSBCardReaderEnabled = FALSE;
215
216 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
217 mCPUAttached[i] = false;
218
219 mIOCacheEnabled = true;
220 mIOCacheSize = 5; /* 5MB */
221
222 /* Maximum CPU execution cap by default. */
223 mCpuExecutionCap = 100;
224}
225
226Machine::HWData::~HWData()
227{
228}
229
230/////////////////////////////////////////////////////////////////////////////
231// Machine::HDData structure
232/////////////////////////////////////////////////////////////////////////////
233
234Machine::MediaData::MediaData()
235{
236}
237
238Machine::MediaData::~MediaData()
239{
240}
241
242/////////////////////////////////////////////////////////////////////////////
243// Machine class
244/////////////////////////////////////////////////////////////////////////////
245
246// constructor / destructor
247/////////////////////////////////////////////////////////////////////////////
248
249Machine::Machine()
250 : mCollectorGuest(NULL),
251 mPeer(NULL),
252 mParent(NULL),
253 mSerialPorts(),
254 mParallelPorts(),
255 uRegistryNeedsSaving(0)
256{}
257
258Machine::~Machine()
259{}
260
261HRESULT Machine::FinalConstruct()
262{
263 LogFlowThisFunc(("\n"));
264 return BaseFinalConstruct();
265}
266
267void Machine::FinalRelease()
268{
269 LogFlowThisFunc(("\n"));
270 uninit();
271 BaseFinalRelease();
272}
273
274/**
275 * Initializes a new machine instance; this init() variant creates a new, empty machine.
276 * This gets called from VirtualBox::CreateMachine().
277 *
278 * @param aParent Associated parent object
279 * @param strConfigFile Local file system path to the VM settings file (can
280 * be relative to the VirtualBox config directory).
281 * @param strName name for the machine
282 * @param llGroups list of groups for the machine
283 * @param aOsType OS Type of this machine or NULL.
284 * @param aId UUID for the new machine.
285 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
286 *
287 * @return Success indicator. if not S_OK, the machine object is invalid
288 */
289HRESULT Machine::init(VirtualBox *aParent,
290 const Utf8Str &strConfigFile,
291 const Utf8Str &strName,
292 const StringsList &llGroups,
293 GuestOSType *aOsType,
294 const Guid &aId,
295 bool fForceOverwrite,
296 bool fDirectoryIncludesUUID)
297{
298 LogFlowThisFuncEnter();
299 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
300
301 /* Enclose the state transition NotReady->InInit->Ready */
302 AutoInitSpan autoInitSpan(this);
303 AssertReturn(autoInitSpan.isOk(), E_FAIL);
304
305 HRESULT rc = initImpl(aParent, strConfigFile);
306 if (FAILED(rc)) return rc;
307
308 rc = tryCreateMachineConfigFile(fForceOverwrite);
309 if (FAILED(rc)) return rc;
310
311 if (SUCCEEDED(rc))
312 {
313 // create an empty machine config
314 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
315
316 rc = initDataAndChildObjects();
317 }
318
319 if (SUCCEEDED(rc))
320 {
321 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
322 mData->mAccessible = TRUE;
323
324 unconst(mData->mUuid) = aId;
325
326 mUserData->s.strName = strName;
327
328 mUserData->s.llGroups = llGroups;
329
330 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
331 // the "name sync" flag determines whether the machine directory gets renamed along
332 // with the machine file; say so if the settings file name is the same as the
333 // settings file parent directory (machine directory)
334 mUserData->s.fNameSync = isInOwnDir();
335
336 // initialize the default snapshots folder
337 rc = COMSETTER(SnapshotFolder)(NULL);
338 AssertComRC(rc);
339
340 if (aOsType)
341 {
342 /* Store OS type */
343 mUserData->s.strOsType = aOsType->id();
344
345 /* Apply BIOS defaults */
346 mBIOSSettings->applyDefaults(aOsType);
347
348 /* Apply network adapters defaults */
349 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
350 mNetworkAdapters[slot]->applyDefaults(aOsType);
351
352 /* Apply serial port defaults */
353 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
354 mSerialPorts[slot]->applyDefaults(aOsType);
355
356 /* Let the OS type select 64-bit ness. */
357 mHWData->mLongMode = aOsType->is64Bit()
358 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
359 }
360
361 /* At this point the changing of the current state modification
362 * flag is allowed. */
363 allowStateModification();
364
365 /* commit all changes made during the initialization */
366 commit();
367 }
368
369 /* Confirm a successful initialization when it's the case */
370 if (SUCCEEDED(rc))
371 {
372 if (mData->mAccessible)
373 autoInitSpan.setSucceeded();
374 else
375 autoInitSpan.setLimited();
376 }
377
378 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
379 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
380 mData->mRegistered,
381 mData->mAccessible,
382 rc));
383
384 LogFlowThisFuncLeave();
385
386 return rc;
387}
388
389/**
390 * Initializes a new instance with data from machine XML (formerly Init_Registered).
391 * Gets called in two modes:
392 *
393 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
394 * UUID is specified and we mark the machine as "registered";
395 *
396 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
397 * and the machine remains unregistered until RegisterMachine() is called.
398 *
399 * @param aParent Associated parent object
400 * @param aConfigFile Local file system path to the VM settings file (can
401 * be relative to the VirtualBox config directory).
402 * @param aId UUID of the machine or NULL (see above).
403 *
404 * @return Success indicator. if not S_OK, the machine object is invalid
405 */
406HRESULT Machine::initFromSettings(VirtualBox *aParent,
407 const Utf8Str &strConfigFile,
408 const Guid *aId)
409{
410 LogFlowThisFuncEnter();
411 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
412
413 /* Enclose the state transition NotReady->InInit->Ready */
414 AutoInitSpan autoInitSpan(this);
415 AssertReturn(autoInitSpan.isOk(), E_FAIL);
416
417 HRESULT rc = initImpl(aParent, strConfigFile);
418 if (FAILED(rc)) return rc;
419
420 if (aId)
421 {
422 // loading a registered VM:
423 unconst(mData->mUuid) = *aId;
424 mData->mRegistered = TRUE;
425 // now load the settings from XML:
426 rc = registeredInit();
427 // this calls initDataAndChildObjects() and loadSettings()
428 }
429 else
430 {
431 // opening an unregistered VM (VirtualBox::OpenMachine()):
432 rc = initDataAndChildObjects();
433
434 if (SUCCEEDED(rc))
435 {
436 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
437 mData->mAccessible = TRUE;
438
439 try
440 {
441 // load and parse machine XML; this will throw on XML or logic errors
442 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
443
444 // reject VM UUID duplicates, they can happen if someone
445 // tries to register an already known VM config again
446 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
447 true /* fPermitInaccessible */,
448 false /* aDoSetError */,
449 NULL) != VBOX_E_OBJECT_NOT_FOUND)
450 {
451 throw setError(E_FAIL,
452 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
453 mData->m_strConfigFile.c_str());
454 }
455
456 // use UUID from machine config
457 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
458
459 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
460 NULL /* puuidRegistry */);
461 if (FAILED(rc)) throw rc;
462
463 /* At this point the changing of the current state modification
464 * flag is allowed. */
465 allowStateModification();
466
467 commit();
468 }
469 catch (HRESULT err)
470 {
471 /* we assume that error info is set by the thrower */
472 rc = err;
473 }
474 catch (...)
475 {
476 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
477 }
478 }
479 }
480
481 /* Confirm a successful initialization when it's the case */
482 if (SUCCEEDED(rc))
483 {
484 if (mData->mAccessible)
485 autoInitSpan.setSucceeded();
486 else
487 {
488 autoInitSpan.setLimited();
489
490 // uninit media from this machine's media registry, or else
491 // reloading the settings will fail
492 mParent->unregisterMachineMedia(getId());
493 }
494 }
495
496 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
497 "rc=%08X\n",
498 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
499 mData->mRegistered, mData->mAccessible, rc));
500
501 LogFlowThisFuncLeave();
502
503 return rc;
504}
505
506/**
507 * Initializes a new instance from a machine config that is already in memory
508 * (import OVF case). Since we are importing, the UUID in the machine
509 * config is ignored and we always generate a fresh one.
510 *
511 * @param strName Name for the new machine; this overrides what is specified in config and is used
512 * for the settings file as well.
513 * @param config Machine configuration loaded and parsed from XML.
514 *
515 * @return Success indicator. if not S_OK, the machine object is invalid
516 */
517HRESULT Machine::init(VirtualBox *aParent,
518 const Utf8Str &strName,
519 const settings::MachineConfigFile &config)
520{
521 LogFlowThisFuncEnter();
522
523 /* Enclose the state transition NotReady->InInit->Ready */
524 AutoInitSpan autoInitSpan(this);
525 AssertReturn(autoInitSpan.isOk(), E_FAIL);
526
527 Utf8Str strConfigFile;
528 aParent->getDefaultMachineFolder(strConfigFile);
529 strConfigFile.append(RTPATH_DELIMITER);
530 strConfigFile.append(strName);
531 strConfigFile.append(RTPATH_DELIMITER);
532 strConfigFile.append(strName);
533 strConfigFile.append(".vbox");
534
535 HRESULT rc = initImpl(aParent, strConfigFile);
536 if (FAILED(rc)) return rc;
537
538 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
539 if (FAILED(rc)) return rc;
540
541 rc = initDataAndChildObjects();
542
543 if (SUCCEEDED(rc))
544 {
545 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
546 mData->mAccessible = TRUE;
547
548 // create empty machine config for instance data
549 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
550
551 // generate fresh UUID, ignore machine config
552 unconst(mData->mUuid).create();
553
554 rc = loadMachineDataFromSettings(config,
555 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
556
557 // override VM name as well, it may be different
558 mUserData->s.strName = strName;
559
560 if (SUCCEEDED(rc))
561 {
562 /* At this point the changing of the current state modification
563 * flag is allowed. */
564 allowStateModification();
565
566 /* commit all changes made during the initialization */
567 commit();
568 }
569 }
570
571 /* Confirm a successful initialization when it's the case */
572 if (SUCCEEDED(rc))
573 {
574 if (mData->mAccessible)
575 autoInitSpan.setSucceeded();
576 else
577 {
578 autoInitSpan.setLimited();
579
580 // uninit media from this machine's media registry, or else
581 // reloading the settings will fail
582 mParent->unregisterMachineMedia(getId());
583 }
584 }
585
586 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
587 "rc=%08X\n",
588 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
589 mData->mRegistered, mData->mAccessible, rc));
590
591 LogFlowThisFuncLeave();
592
593 return rc;
594}
595
596/**
597 * Shared code between the various init() implementations.
598 * @param aParent
599 * @return
600 */
601HRESULT Machine::initImpl(VirtualBox *aParent,
602 const Utf8Str &strConfigFile)
603{
604 LogFlowThisFuncEnter();
605
606 AssertReturn(aParent, E_INVALIDARG);
607 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
608
609 HRESULT rc = S_OK;
610
611 /* share the parent weakly */
612 unconst(mParent) = aParent;
613
614 /* allocate the essential machine data structure (the rest will be
615 * allocated later by initDataAndChildObjects() */
616 mData.allocate();
617
618 /* memorize the config file name (as provided) */
619 mData->m_strConfigFile = strConfigFile;
620
621 /* get the full file name */
622 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
623 if (RT_FAILURE(vrc1))
624 return setError(VBOX_E_FILE_ERROR,
625 tr("Invalid machine settings file name '%s' (%Rrc)"),
626 strConfigFile.c_str(),
627 vrc1);
628
629 LogFlowThisFuncLeave();
630
631 return rc;
632}
633
634/**
635 * Tries to create a machine settings file in the path stored in the machine
636 * instance data. Used when a new machine is created to fail gracefully if
637 * the settings file could not be written (e.g. because machine dir is read-only).
638 * @return
639 */
640HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
641{
642 HRESULT rc = S_OK;
643
644 // when we create a new machine, we must be able to create the settings file
645 RTFILE f = NIL_RTFILE;
646 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
647 if ( RT_SUCCESS(vrc)
648 || vrc == VERR_SHARING_VIOLATION
649 )
650 {
651 if (RT_SUCCESS(vrc))
652 RTFileClose(f);
653 if (!fForceOverwrite)
654 rc = setError(VBOX_E_FILE_ERROR,
655 tr("Machine settings file '%s' already exists"),
656 mData->m_strConfigFileFull.c_str());
657 else
658 {
659 /* try to delete the config file, as otherwise the creation
660 * of a new settings file will fail. */
661 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
662 if (RT_FAILURE(vrc2))
663 rc = setError(VBOX_E_FILE_ERROR,
664 tr("Could not delete the existing settings file '%s' (%Rrc)"),
665 mData->m_strConfigFileFull.c_str(), vrc2);
666 }
667 }
668 else if ( vrc != VERR_FILE_NOT_FOUND
669 && vrc != VERR_PATH_NOT_FOUND
670 )
671 rc = setError(VBOX_E_FILE_ERROR,
672 tr("Invalid machine settings file name '%s' (%Rrc)"),
673 mData->m_strConfigFileFull.c_str(),
674 vrc);
675 return rc;
676}
677
678/**
679 * Initializes the registered machine by loading the settings file.
680 * This method is separated from #init() in order to make it possible to
681 * retry the operation after VirtualBox startup instead of refusing to
682 * startup the whole VirtualBox server in case if the settings file of some
683 * registered VM is invalid or inaccessible.
684 *
685 * @note Must be always called from this object's write lock
686 * (unless called from #init() that doesn't need any locking).
687 * @note Locks the mUSBController method for writing.
688 * @note Subclasses must not call this method.
689 */
690HRESULT Machine::registeredInit()
691{
692 AssertReturn(!isSessionMachine(), E_FAIL);
693 AssertReturn(!isSnapshotMachine(), E_FAIL);
694 AssertReturn(mData->mUuid.isValid(), E_FAIL);
695 AssertReturn(!mData->mAccessible, E_FAIL);
696
697 HRESULT rc = initDataAndChildObjects();
698
699 if (SUCCEEDED(rc))
700 {
701 /* Temporarily reset the registered flag in order to let setters
702 * potentially called from loadSettings() succeed (isMutable() used in
703 * all setters will return FALSE for a Machine instance if mRegistered
704 * is TRUE). */
705 mData->mRegistered = FALSE;
706
707 try
708 {
709 // load and parse machine XML; this will throw on XML or logic errors
710 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
711
712 if (mData->mUuid != mData->pMachineConfigFile->uuid)
713 throw setError(E_FAIL,
714 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
715 mData->pMachineConfigFile->uuid.raw(),
716 mData->m_strConfigFileFull.c_str(),
717 mData->mUuid.toString().c_str(),
718 mParent->settingsFilePath().c_str());
719
720 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
721 NULL /* const Guid *puuidRegistry */);
722 if (FAILED(rc)) throw rc;
723 }
724 catch (HRESULT err)
725 {
726 /* we assume that error info is set by the thrower */
727 rc = err;
728 }
729 catch (...)
730 {
731 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
732 }
733
734 /* Restore the registered flag (even on failure) */
735 mData->mRegistered = TRUE;
736 }
737
738 if (SUCCEEDED(rc))
739 {
740 /* Set mAccessible to TRUE only if we successfully locked and loaded
741 * the settings file */
742 mData->mAccessible = TRUE;
743
744 /* commit all changes made during loading the settings file */
745 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
746 /// @todo r=klaus for some reason the settings loading logic backs up
747 // the settings, and therefore a commit is needed. Should probably be changed.
748 }
749 else
750 {
751 /* If the machine is registered, then, instead of returning a
752 * failure, we mark it as inaccessible and set the result to
753 * success to give it a try later */
754
755 /* fetch the current error info */
756 mData->mAccessError = com::ErrorInfo();
757 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
758 mData->mUuid.raw(),
759 mData->mAccessError.getText().raw()));
760
761 /* rollback all changes */
762 rollback(false /* aNotify */);
763
764 // uninit media from this machine's media registry, or else
765 // reloading the settings will fail
766 mParent->unregisterMachineMedia(getId());
767
768 /* uninitialize the common part to make sure all data is reset to
769 * default (null) values */
770 uninitDataAndChildObjects();
771
772 rc = S_OK;
773 }
774
775 return rc;
776}
777
778/**
779 * Uninitializes the instance.
780 * Called either from FinalRelease() or by the parent when it gets destroyed.
781 *
782 * @note The caller of this method must make sure that this object
783 * a) doesn't have active callers on the current thread and b) is not locked
784 * by the current thread; otherwise uninit() will hang either a) due to
785 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
786 * a dead-lock caused by this thread waiting for all callers on the other
787 * threads are done but preventing them from doing so by holding a lock.
788 */
789void Machine::uninit()
790{
791 LogFlowThisFuncEnter();
792
793 Assert(!isWriteLockOnCurrentThread());
794
795 Assert(!uRegistryNeedsSaving);
796 if (uRegistryNeedsSaving)
797 {
798 AutoCaller autoCaller(this);
799 if (SUCCEEDED(autoCaller.rc()))
800 {
801 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
802 saveSettings(NULL, Machine::SaveS_Force);
803 }
804 }
805
806 /* Enclose the state transition Ready->InUninit->NotReady */
807 AutoUninitSpan autoUninitSpan(this);
808 if (autoUninitSpan.uninitDone())
809 return;
810
811 Assert(!isSnapshotMachine());
812 Assert(!isSessionMachine());
813 Assert(!!mData);
814
815 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
816 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
817
818 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
819
820 if (!mData->mSession.mMachine.isNull())
821 {
822 /* Theoretically, this can only happen if the VirtualBox server has been
823 * terminated while there were clients running that owned open direct
824 * sessions. Since in this case we are definitely called by
825 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
826 * won't happen on the client watcher thread (because it does
827 * VirtualBox::addCaller() for the duration of the
828 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
829 * cannot happen until the VirtualBox caller is released). This is
830 * important, because SessionMachine::uninit() cannot correctly operate
831 * after we return from this method (it expects the Machine instance is
832 * still valid). We'll call it ourselves below.
833 */
834 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
835 (SessionMachine*)mData->mSession.mMachine));
836
837 if (Global::IsOnlineOrTransient(mData->mMachineState))
838 {
839 LogWarningThisFunc(("Setting state to Aborted!\n"));
840 /* set machine state using SessionMachine reimplementation */
841 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
842 }
843
844 /*
845 * Uninitialize SessionMachine using public uninit() to indicate
846 * an unexpected uninitialization.
847 */
848 mData->mSession.mMachine->uninit();
849 /* SessionMachine::uninit() must set mSession.mMachine to null */
850 Assert(mData->mSession.mMachine.isNull());
851 }
852
853 // uninit media from this machine's media registry, if they're still there
854 Guid uuidMachine(getId());
855
856 /* the lock is no more necessary (SessionMachine is uninitialized) */
857 alock.release();
858
859 /* XXX This will fail with
860 * "cannot be closed because it is still attached to 1 virtual machines"
861 * because at this point we did not call uninitDataAndChildObjects() yet
862 * and therefore also removeBackReference() for all these mediums was not called! */
863
864 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
865 mParent->unregisterMachineMedia(uuidMachine);
866
867 // has machine been modified?
868 if (mData->flModifications)
869 {
870 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
871 rollback(false /* aNotify */);
872 }
873
874 if (mData->mAccessible)
875 uninitDataAndChildObjects();
876
877 /* free the essential data structure last */
878 mData.free();
879
880 LogFlowThisFuncLeave();
881}
882
883// IMachine properties
884/////////////////////////////////////////////////////////////////////////////
885
886STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
887{
888 CheckComArgOutPointerValid(aParent);
889
890 AutoLimitedCaller autoCaller(this);
891 if (FAILED(autoCaller.rc())) return autoCaller.rc();
892
893 /* mParent is constant during life time, no need to lock */
894 ComObjPtr<VirtualBox> pVirtualBox(mParent);
895 pVirtualBox.queryInterfaceTo(aParent);
896
897 return S_OK;
898}
899
900STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
901{
902 CheckComArgOutPointerValid(aAccessible);
903
904 AutoLimitedCaller autoCaller(this);
905 if (FAILED(autoCaller.rc())) return autoCaller.rc();
906
907 LogFlowThisFunc(("ENTER\n"));
908
909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
910
911 HRESULT rc = S_OK;
912
913 if (!mData->mAccessible)
914 {
915 /* try to initialize the VM once more if not accessible */
916
917 AutoReinitSpan autoReinitSpan(this);
918 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
919
920#ifdef DEBUG
921 LogFlowThisFunc(("Dumping media backreferences\n"));
922 mParent->dumpAllBackRefs();
923#endif
924
925 if (mData->pMachineConfigFile)
926 {
927 // reset the XML file to force loadSettings() (called from registeredInit())
928 // to parse it again; the file might have changed
929 delete mData->pMachineConfigFile;
930 mData->pMachineConfigFile = NULL;
931 }
932
933 rc = registeredInit();
934
935 if (SUCCEEDED(rc) && mData->mAccessible)
936 {
937 autoReinitSpan.setSucceeded();
938
939 /* make sure interesting parties will notice the accessibility
940 * state change */
941 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
942 mParent->onMachineDataChange(mData->mUuid);
943 }
944 }
945
946 if (SUCCEEDED(rc))
947 *aAccessible = mData->mAccessible;
948
949 LogFlowThisFuncLeave();
950
951 return rc;
952}
953
954STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
955{
956 CheckComArgOutPointerValid(aAccessError);
957
958 AutoLimitedCaller autoCaller(this);
959 if (FAILED(autoCaller.rc())) return autoCaller.rc();
960
961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
962
963 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
964 {
965 /* return shortly */
966 aAccessError = NULL;
967 return S_OK;
968 }
969
970 HRESULT rc = S_OK;
971
972 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
973 rc = errorInfo.createObject();
974 if (SUCCEEDED(rc))
975 {
976 errorInfo->init(mData->mAccessError.getResultCode(),
977 mData->mAccessError.getInterfaceID().ref(),
978 Utf8Str(mData->mAccessError.getComponent()).c_str(),
979 Utf8Str(mData->mAccessError.getText()));
980 rc = errorInfo.queryInterfaceTo(aAccessError);
981 }
982
983 return rc;
984}
985
986STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
987{
988 CheckComArgOutPointerValid(aName);
989
990 AutoCaller autoCaller(this);
991 if (FAILED(autoCaller.rc())) return autoCaller.rc();
992
993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
994
995 mUserData->s.strName.cloneTo(aName);
996
997 return S_OK;
998}
999
1000STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
1001{
1002 CheckComArgStrNotEmptyOrNull(aName);
1003
1004 AutoCaller autoCaller(this);
1005 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1006
1007 // prohibit setting a UUID only as the machine name, or else it can
1008 // never be found by findMachine()
1009 Guid test(aName);
1010
1011 if (test.isValid())
1012 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1013
1014 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1015
1016 HRESULT rc = checkStateDependency(MutableStateDep);
1017 if (FAILED(rc)) return rc;
1018
1019 setModified(IsModified_MachineData);
1020 mUserData.backup();
1021 mUserData->s.strName = aName;
1022
1023 return S_OK;
1024}
1025
1026STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1027{
1028 CheckComArgOutPointerValid(aDescription);
1029
1030 AutoCaller autoCaller(this);
1031 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1032
1033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1034
1035 mUserData->s.strDescription.cloneTo(aDescription);
1036
1037 return S_OK;
1038}
1039
1040STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1041{
1042 AutoCaller autoCaller(this);
1043 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1044
1045 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1046
1047 // this can be done in principle in any state as it doesn't affect the VM
1048 // significantly, but play safe by not messing around while complex
1049 // activities are going on
1050 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1051 if (FAILED(rc)) return rc;
1052
1053 setModified(IsModified_MachineData);
1054 mUserData.backup();
1055 mUserData->s.strDescription = aDescription;
1056
1057 return S_OK;
1058}
1059
1060STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1061{
1062 CheckComArgOutPointerValid(aId);
1063
1064 AutoLimitedCaller autoCaller(this);
1065 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1066
1067 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1068
1069 mData->mUuid.toUtf16().cloneTo(aId);
1070
1071 return S_OK;
1072}
1073
1074STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1075{
1076 CheckComArgOutSafeArrayPointerValid(aGroups);
1077
1078 AutoCaller autoCaller(this);
1079 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1080
1081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1082 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1083 size_t i = 0;
1084 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1085 it != mUserData->s.llGroups.end();
1086 ++it, i++)
1087 {
1088 Bstr tmp = *it;
1089 tmp.cloneTo(&groups[i]);
1090 }
1091 groups.detachTo(ComSafeArrayOutArg(aGroups));
1092
1093 return S_OK;
1094}
1095
1096STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1097{
1098 AutoCaller autoCaller(this);
1099 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1100
1101 StringsList llGroups;
1102 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1103 if (FAILED(rc))
1104 return rc;
1105
1106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1107
1108 // changing machine groups is possible while the VM is offline
1109 rc = checkStateDependency(OfflineStateDep);
1110 if (FAILED(rc)) return rc;
1111
1112 setModified(IsModified_MachineData);
1113 mUserData.backup();
1114 mUserData->s.llGroups = llGroups;
1115
1116 return S_OK;
1117}
1118
1119STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1120{
1121 CheckComArgOutPointerValid(aOSTypeId);
1122
1123 AutoCaller autoCaller(this);
1124 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1125
1126 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1127
1128 mUserData->s.strOsType.cloneTo(aOSTypeId);
1129
1130 return S_OK;
1131}
1132
1133STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1134{
1135 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1136
1137 AutoCaller autoCaller(this);
1138 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1139
1140 /* look up the object by Id to check it is valid */
1141 ComPtr<IGuestOSType> guestOSType;
1142 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1143 if (FAILED(rc)) return rc;
1144
1145 /* when setting, always use the "etalon" value for consistency -- lookup
1146 * by ID is case-insensitive and the input value may have different case */
1147 Bstr osTypeId;
1148 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1149 if (FAILED(rc)) return rc;
1150
1151 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1152
1153 rc = checkStateDependency(MutableStateDep);
1154 if (FAILED(rc)) return rc;
1155
1156 setModified(IsModified_MachineData);
1157 mUserData.backup();
1158 mUserData->s.strOsType = osTypeId;
1159
1160 return S_OK;
1161}
1162
1163
1164STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1165{
1166 CheckComArgOutPointerValid(aFirmwareType);
1167
1168 AutoCaller autoCaller(this);
1169 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1170
1171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1172
1173 *aFirmwareType = mHWData->mFirmwareType;
1174
1175 return S_OK;
1176}
1177
1178STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1179{
1180 AutoCaller autoCaller(this);
1181 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1183
1184 HRESULT rc = checkStateDependency(MutableStateDep);
1185 if (FAILED(rc)) return rc;
1186
1187 setModified(IsModified_MachineData);
1188 mHWData.backup();
1189 mHWData->mFirmwareType = aFirmwareType;
1190
1191 return S_OK;
1192}
1193
1194STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1195{
1196 CheckComArgOutPointerValid(aKeyboardHIDType);
1197
1198 AutoCaller autoCaller(this);
1199 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1200
1201 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1202
1203 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1204
1205 return S_OK;
1206}
1207
1208STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1209{
1210 AutoCaller autoCaller(this);
1211 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1212 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1213
1214 HRESULT rc = checkStateDependency(MutableStateDep);
1215 if (FAILED(rc)) return rc;
1216
1217 setModified(IsModified_MachineData);
1218 mHWData.backup();
1219 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1220
1221 return S_OK;
1222}
1223
1224STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1225{
1226 CheckComArgOutPointerValid(aPointingHIDType);
1227
1228 AutoCaller autoCaller(this);
1229 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1230
1231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1232
1233 *aPointingHIDType = mHWData->mPointingHIDType;
1234
1235 return S_OK;
1236}
1237
1238STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1239{
1240 AutoCaller autoCaller(this);
1241 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1243
1244 HRESULT rc = checkStateDependency(MutableStateDep);
1245 if (FAILED(rc)) return rc;
1246
1247 setModified(IsModified_MachineData);
1248 mHWData.backup();
1249 mHWData->mPointingHIDType = aPointingHIDType;
1250
1251 return S_OK;
1252}
1253
1254STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1255{
1256 CheckComArgOutPointerValid(aChipsetType);
1257
1258 AutoCaller autoCaller(this);
1259 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1260
1261 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1262
1263 *aChipsetType = mHWData->mChipsetType;
1264
1265 return S_OK;
1266}
1267
1268STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1269{
1270 AutoCaller autoCaller(this);
1271 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1272 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1273
1274 HRESULT rc = checkStateDependency(MutableStateDep);
1275 if (FAILED(rc)) return rc;
1276
1277 if (aChipsetType != mHWData->mChipsetType)
1278 {
1279 setModified(IsModified_MachineData);
1280 mHWData.backup();
1281 mHWData->mChipsetType = aChipsetType;
1282
1283 // Resize network adapter array, to be finalized on commit/rollback.
1284 // We must not throw away entries yet, otherwise settings are lost
1285 // without a way to roll back.
1286 uint32_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1287 uint32_t oldCount = mNetworkAdapters.size();
1288 if (newCount > oldCount)
1289 {
1290 mNetworkAdapters.resize(newCount);
1291 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1292 {
1293 unconst(mNetworkAdapters[slot]).createObject();
1294 mNetworkAdapters[slot]->init(this, slot);
1295 }
1296 }
1297 }
1298
1299 return S_OK;
1300}
1301
1302STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1303{
1304 CheckComArgOutPointerValid(aHWVersion);
1305
1306 AutoCaller autoCaller(this);
1307 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1308
1309 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1310
1311 mHWData->mHWVersion.cloneTo(aHWVersion);
1312
1313 return S_OK;
1314}
1315
1316STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1317{
1318 /* check known version */
1319 Utf8Str hwVersion = aHWVersion;
1320 if ( hwVersion.compare("1") != 0
1321 && hwVersion.compare("2") != 0)
1322 return setError(E_INVALIDARG,
1323 tr("Invalid hardware version: %ls\n"), aHWVersion);
1324
1325 AutoCaller autoCaller(this);
1326 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1327
1328 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1329
1330 HRESULT rc = checkStateDependency(MutableStateDep);
1331 if (FAILED(rc)) return rc;
1332
1333 setModified(IsModified_MachineData);
1334 mHWData.backup();
1335 mHWData->mHWVersion = hwVersion;
1336
1337 return S_OK;
1338}
1339
1340STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1341{
1342 CheckComArgOutPointerValid(aUUID);
1343
1344 AutoCaller autoCaller(this);
1345 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1346
1347 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1348
1349 if (mHWData->mHardwareUUID.isValid())
1350 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1351 else
1352 mData->mUuid.toUtf16().cloneTo(aUUID);
1353
1354 return S_OK;
1355}
1356
1357STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1358{
1359 Guid hardwareUUID(aUUID);
1360 if (!hardwareUUID.isValid())
1361 return E_INVALIDARG;
1362
1363 AutoCaller autoCaller(this);
1364 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1365
1366 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1367
1368 HRESULT rc = checkStateDependency(MutableStateDep);
1369 if (FAILED(rc)) return rc;
1370
1371 setModified(IsModified_MachineData);
1372 mHWData.backup();
1373 if (hardwareUUID == mData->mUuid)
1374 mHWData->mHardwareUUID.clear();
1375 else
1376 mHWData->mHardwareUUID = hardwareUUID;
1377
1378 return S_OK;
1379}
1380
1381STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1382{
1383 CheckComArgOutPointerValid(memorySize);
1384
1385 AutoCaller autoCaller(this);
1386 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1387
1388 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1389
1390 *memorySize = mHWData->mMemorySize;
1391
1392 return S_OK;
1393}
1394
1395STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1396{
1397 /* check RAM limits */
1398 if ( memorySize < MM_RAM_MIN_IN_MB
1399 || memorySize > MM_RAM_MAX_IN_MB
1400 )
1401 return setError(E_INVALIDARG,
1402 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1403 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1404
1405 AutoCaller autoCaller(this);
1406 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1407
1408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1409
1410 HRESULT rc = checkStateDependency(MutableStateDep);
1411 if (FAILED(rc)) return rc;
1412
1413 setModified(IsModified_MachineData);
1414 mHWData.backup();
1415 mHWData->mMemorySize = memorySize;
1416
1417 return S_OK;
1418}
1419
1420STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1421{
1422 CheckComArgOutPointerValid(CPUCount);
1423
1424 AutoCaller autoCaller(this);
1425 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1426
1427 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1428
1429 *CPUCount = mHWData->mCPUCount;
1430
1431 return S_OK;
1432}
1433
1434STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1435{
1436 /* check CPU limits */
1437 if ( CPUCount < SchemaDefs::MinCPUCount
1438 || CPUCount > SchemaDefs::MaxCPUCount
1439 )
1440 return setError(E_INVALIDARG,
1441 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1442 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1443
1444 AutoCaller autoCaller(this);
1445 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1446
1447 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1448
1449 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1450 if (mHWData->mCPUHotPlugEnabled)
1451 {
1452 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1453 {
1454 if (mHWData->mCPUAttached[idx])
1455 return setError(E_INVALIDARG,
1456 tr("There is still a CPU attached to socket %lu."
1457 "Detach the CPU before removing the socket"),
1458 CPUCount, idx+1);
1459 }
1460 }
1461
1462 HRESULT rc = checkStateDependency(MutableStateDep);
1463 if (FAILED(rc)) return rc;
1464
1465 setModified(IsModified_MachineData);
1466 mHWData.backup();
1467 mHWData->mCPUCount = CPUCount;
1468
1469 return S_OK;
1470}
1471
1472STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1473{
1474 CheckComArgOutPointerValid(aExecutionCap);
1475
1476 AutoCaller autoCaller(this);
1477 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1478
1479 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1480
1481 *aExecutionCap = mHWData->mCpuExecutionCap;
1482
1483 return S_OK;
1484}
1485
1486STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1487{
1488 HRESULT rc = S_OK;
1489
1490 /* check throttle limits */
1491 if ( aExecutionCap < 1
1492 || aExecutionCap > 100
1493 )
1494 return setError(E_INVALIDARG,
1495 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1496 aExecutionCap, 1, 100);
1497
1498 AutoCaller autoCaller(this);
1499 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1500
1501 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1502
1503 alock.release();
1504 rc = onCPUExecutionCapChange(aExecutionCap);
1505 alock.acquire();
1506 if (FAILED(rc)) return rc;
1507
1508 setModified(IsModified_MachineData);
1509 mHWData.backup();
1510 mHWData->mCpuExecutionCap = aExecutionCap;
1511
1512 /* Save settings if online - todo why is this required?? */
1513 if (Global::IsOnline(mData->mMachineState))
1514 saveSettings(NULL);
1515
1516 return S_OK;
1517}
1518
1519
1520STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *enabled)
1521{
1522 CheckComArgOutPointerValid(enabled);
1523
1524 AutoCaller autoCaller(this);
1525 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1526
1527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1528
1529 *enabled = mHWData->mCPUHotPlugEnabled;
1530
1531 return S_OK;
1532}
1533
1534STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled)
1535{
1536 HRESULT rc = S_OK;
1537
1538 AutoCaller autoCaller(this);
1539 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1540
1541 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1542
1543 rc = checkStateDependency(MutableStateDep);
1544 if (FAILED(rc)) return rc;
1545
1546 if (mHWData->mCPUHotPlugEnabled != enabled)
1547 {
1548 if (enabled)
1549 {
1550 setModified(IsModified_MachineData);
1551 mHWData.backup();
1552
1553 /* Add the amount of CPUs currently attached */
1554 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1555 {
1556 mHWData->mCPUAttached[i] = true;
1557 }
1558 }
1559 else
1560 {
1561 /*
1562 * We can disable hotplug only if the amount of maximum CPUs is equal
1563 * to the amount of attached CPUs
1564 */
1565 unsigned cCpusAttached = 0;
1566 unsigned iHighestId = 0;
1567
1568 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1569 {
1570 if (mHWData->mCPUAttached[i])
1571 {
1572 cCpusAttached++;
1573 iHighestId = i;
1574 }
1575 }
1576
1577 if ( (cCpusAttached != mHWData->mCPUCount)
1578 || (iHighestId >= mHWData->mCPUCount))
1579 return setError(E_INVALIDARG,
1580 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1581
1582 setModified(IsModified_MachineData);
1583 mHWData.backup();
1584 }
1585 }
1586
1587 mHWData->mCPUHotPlugEnabled = enabled;
1588
1589 return rc;
1590}
1591
1592STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *enabled)
1593{
1594#ifdef VBOX_WITH_USB_CARDREADER
1595 CheckComArgOutPointerValid(enabled);
1596
1597 AutoCaller autoCaller(this);
1598 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1599
1600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1601
1602 *enabled = mHWData->mEmulatedUSBCardReaderEnabled;
1603
1604 return S_OK;
1605#else
1606 NOREF(enabled);
1607 return E_NOTIMPL;
1608#endif
1609}
1610
1611STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL enabled)
1612{
1613#ifdef VBOX_WITH_USB_CARDREADER
1614 AutoCaller autoCaller(this);
1615 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1616 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1617
1618 HRESULT rc = checkStateDependency(MutableStateDep);
1619 if (FAILED(rc)) return rc;
1620
1621 setModified(IsModified_MachineData);
1622 mHWData.backup();
1623 mHWData->mEmulatedUSBCardReaderEnabled = enabled;
1624
1625 return S_OK;
1626#else
1627 NOREF(enabled);
1628 return E_NOTIMPL;
1629#endif
1630}
1631
1632STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *enabled)
1633{
1634#ifdef VBOX_WITH_USB_VIDEO
1635 CheckComArgOutPointerValid(enabled);
1636
1637 AutoCaller autoCaller(this);
1638 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1639
1640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1641
1642 *enabled = mHWData->mEmulatedUSBWebcamEnabled;
1643
1644 return S_OK;
1645#else
1646 NOREF(enabled);
1647 return E_NOTIMPL;
1648#endif
1649}
1650
1651STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL enabled)
1652{
1653#ifdef VBOX_WITH_USB_VIDEO
1654 AutoCaller autoCaller(this);
1655 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1656 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1657
1658 HRESULT rc = checkStateDependency(MutableStateDep);
1659 if (FAILED(rc)) return rc;
1660
1661 setModified(IsModified_MachineData);
1662 mHWData.backup();
1663 mHWData->mEmulatedUSBWebcamEnabled = enabled;
1664
1665 return S_OK;
1666#else
1667 NOREF(enabled);
1668 return E_NOTIMPL;
1669#endif
1670}
1671
1672STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *enabled)
1673{
1674 CheckComArgOutPointerValid(enabled);
1675
1676 AutoCaller autoCaller(this);
1677 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1678 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1679
1680 *enabled = mHWData->mHPETEnabled;
1681
1682 return S_OK;
1683}
1684
1685STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL enabled)
1686{
1687 HRESULT rc = S_OK;
1688
1689 AutoCaller autoCaller(this);
1690 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1691 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1692
1693 rc = checkStateDependency(MutableStateDep);
1694 if (FAILED(rc)) return rc;
1695
1696 setModified(IsModified_MachineData);
1697 mHWData.backup();
1698
1699 mHWData->mHPETEnabled = enabled;
1700
1701 return rc;
1702}
1703
1704STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
1705{
1706 AutoCaller autoCaller(this);
1707 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1708
1709 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1710
1711 *fEnabled = mHWData->mVideoCaptureEnabled;
1712 return S_OK;
1713}
1714
1715STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1716{
1717 AutoCaller autoCaller(this);
1718 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1719
1720 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1721 mHWData->mVideoCaptureEnabled = fEnabled;
1722 return S_OK;
1723}
1724
1725STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR * apFile)
1726{
1727 AutoCaller autoCaller(this);
1728 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1729
1730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1731 mHWData->mVideoCaptureFile.cloneTo(apFile);
1732 return S_OK;
1733}
1734
1735STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1736{
1737 Utf8Str strFile(aFile);
1738 AutoCaller autoCaller(this);
1739 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1740
1741 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1742 if (strFile.isEmpty())
1743 strFile = "VideoCap.webm";
1744 mHWData->mVideoCaptureFile = strFile;
1745 return S_OK;
1746}
1747
1748
1749STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *ulHorzRes)
1750{
1751 AutoCaller autoCaller(this);
1752 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1753
1754 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1755 *ulHorzRes = mHWData->mVideoCaptureWidth;
1756 return S_OK;
1757}
1758
1759STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG ulHorzRes)
1760{
1761 AutoCaller autoCaller(this);
1762 if (FAILED(autoCaller.rc()))
1763 {
1764 LogFlow(("Autolocked failed\n"));
1765 return autoCaller.rc();
1766 }
1767
1768 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1769 mHWData->mVideoCaptureWidth = ulHorzRes;
1770 return S_OK;
1771}
1772
1773STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *ulVertRes)
1774{
1775 AutoCaller autoCaller(this);
1776 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1777
1778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1779 *ulVertRes = mHWData->mVideoCaptureHeight;
1780 return S_OK;
1781}
1782
1783STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG ulVertRes)
1784{
1785 AutoCaller autoCaller(this);
1786 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1787
1788 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1789 mHWData->mVideoCaptureHeight = ulVertRes;
1790 return S_OK;
1791}
1792
1793STDMETHODIMP Machine::COMGETTER(GraphicsControllerType)(GraphicsControllerType_T *aGraphicsControllerType)
1794{
1795 CheckComArgOutPointerValid(aGraphicsControllerType);
1796
1797 AutoCaller autoCaller(this);
1798 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1799
1800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1801
1802 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1803
1804 return S_OK;
1805}
1806
1807STDMETHODIMP Machine::COMSETTER(GraphicsControllerType)(GraphicsControllerType_T aGraphicsControllerType)
1808{
1809 switch (aGraphicsControllerType)
1810 {
1811 case GraphicsControllerType_Null:
1812 case GraphicsControllerType_VBoxVGA:
1813 break;
1814 default:
1815 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1816 }
1817
1818 AutoCaller autoCaller(this);
1819 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1820
1821 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1822
1823 HRESULT rc = checkStateDependency(MutableStateDep);
1824 if (FAILED(rc)) return rc;
1825
1826 setModified(IsModified_MachineData);
1827 mHWData.backup();
1828 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1829
1830 return S_OK;
1831}
1832
1833STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1834{
1835 CheckComArgOutPointerValid(memorySize);
1836
1837 AutoCaller autoCaller(this);
1838 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1839
1840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1841
1842 *memorySize = mHWData->mVRAMSize;
1843
1844 return S_OK;
1845}
1846
1847STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1848{
1849 /* check VRAM limits */
1850 if (memorySize < SchemaDefs::MinGuestVRAM ||
1851 memorySize > SchemaDefs::MaxGuestVRAM)
1852 return setError(E_INVALIDARG,
1853 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1854 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1855
1856 AutoCaller autoCaller(this);
1857 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1858
1859 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1860
1861 HRESULT rc = checkStateDependency(MutableStateDep);
1862 if (FAILED(rc)) return rc;
1863
1864 setModified(IsModified_MachineData);
1865 mHWData.backup();
1866 mHWData->mVRAMSize = memorySize;
1867
1868 return S_OK;
1869}
1870
1871/** @todo this method should not be public */
1872STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1873{
1874 CheckComArgOutPointerValid(memoryBalloonSize);
1875
1876 AutoCaller autoCaller(this);
1877 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1878
1879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1880
1881 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
1882
1883 return S_OK;
1884}
1885
1886/**
1887 * Set the memory balloon size.
1888 *
1889 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1890 * we have to make sure that we never call IGuest from here.
1891 */
1892STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
1893{
1894 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1895#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1896 /* check limits */
1897 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1898 return setError(E_INVALIDARG,
1899 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1900 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1901
1902 AutoCaller autoCaller(this);
1903 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1904
1905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1906
1907 setModified(IsModified_MachineData);
1908 mHWData.backup();
1909 mHWData->mMemoryBalloonSize = memoryBalloonSize;
1910
1911 return S_OK;
1912#else
1913 NOREF(memoryBalloonSize);
1914 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1915#endif
1916}
1917
1918STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *enabled)
1919{
1920 CheckComArgOutPointerValid(enabled);
1921
1922 AutoCaller autoCaller(this);
1923 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1924
1925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1926
1927 *enabled = mHWData->mPageFusionEnabled;
1928 return S_OK;
1929}
1930
1931STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL enabled)
1932{
1933#ifdef VBOX_WITH_PAGE_SHARING
1934 AutoCaller autoCaller(this);
1935 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1936
1937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1938
1939 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1940 setModified(IsModified_MachineData);
1941 mHWData.backup();
1942 mHWData->mPageFusionEnabled = enabled;
1943 return S_OK;
1944#else
1945 NOREF(enabled);
1946 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1947#endif
1948}
1949
1950STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *enabled)
1951{
1952 CheckComArgOutPointerValid(enabled);
1953
1954 AutoCaller autoCaller(this);
1955 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1956
1957 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1958
1959 *enabled = mHWData->mAccelerate3DEnabled;
1960
1961 return S_OK;
1962}
1963
1964STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
1965{
1966 AutoCaller autoCaller(this);
1967 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1968
1969 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1970
1971 HRESULT rc = checkStateDependency(MutableStateDep);
1972 if (FAILED(rc)) return rc;
1973
1974 /** @todo check validity! */
1975
1976 setModified(IsModified_MachineData);
1977 mHWData.backup();
1978 mHWData->mAccelerate3DEnabled = enable;
1979
1980 return S_OK;
1981}
1982
1983
1984STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *enabled)
1985{
1986 CheckComArgOutPointerValid(enabled);
1987
1988 AutoCaller autoCaller(this);
1989 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1990
1991 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1992
1993 *enabled = mHWData->mAccelerate2DVideoEnabled;
1994
1995 return S_OK;
1996}
1997
1998STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
1999{
2000 AutoCaller autoCaller(this);
2001 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2002
2003 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2004
2005 HRESULT rc = checkStateDependency(MutableStateDep);
2006 if (FAILED(rc)) return rc;
2007
2008 /** @todo check validity! */
2009
2010 setModified(IsModified_MachineData);
2011 mHWData.backup();
2012 mHWData->mAccelerate2DVideoEnabled = enable;
2013
2014 return S_OK;
2015}
2016
2017STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
2018{
2019 CheckComArgOutPointerValid(monitorCount);
2020
2021 AutoCaller autoCaller(this);
2022 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2023
2024 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2025
2026 *monitorCount = mHWData->mMonitorCount;
2027
2028 return S_OK;
2029}
2030
2031STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
2032{
2033 /* make sure monitor count is a sensible number */
2034 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
2035 return setError(E_INVALIDARG,
2036 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2037 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
2038
2039 AutoCaller autoCaller(this);
2040 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2041
2042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2043
2044 HRESULT rc = checkStateDependency(MutableStateDep);
2045 if (FAILED(rc)) return rc;
2046
2047 setModified(IsModified_MachineData);
2048 mHWData.backup();
2049 mHWData->mMonitorCount = monitorCount;
2050
2051 return S_OK;
2052}
2053
2054STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2055{
2056 CheckComArgOutPointerValid(biosSettings);
2057
2058 AutoCaller autoCaller(this);
2059 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2060
2061 /* mBIOSSettings is constant during life time, no need to lock */
2062 mBIOSSettings.queryInterfaceTo(biosSettings);
2063
2064 return S_OK;
2065}
2066
2067STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2068{
2069 CheckComArgOutPointerValid(aVal);
2070
2071 AutoCaller autoCaller(this);
2072 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2073
2074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2075
2076 switch (property)
2077 {
2078 case CPUPropertyType_PAE:
2079 *aVal = mHWData->mPAEEnabled;
2080 break;
2081
2082 case CPUPropertyType_Synthetic:
2083 *aVal = mHWData->mSyntheticCpu;
2084 break;
2085
2086 case CPUPropertyType_LongMode:
2087 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2088 *aVal = TRUE;
2089 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2090 *aVal = FALSE;
2091#if HC_ARCH_BITS == 64
2092 else
2093 *aVal = TRUE;
2094#else
2095 else
2096 {
2097 *aVal = FALSE;
2098
2099 ComPtr<IGuestOSType> ptrGuestOSType;
2100 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2101 if (SUCCEEDED(hrc2))
2102 {
2103 BOOL fIs64Bit = FALSE;
2104 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2105 if (SUCCEEDED(hrc2) && fIs64Bit)
2106 {
2107 ComObjPtr<Host> ptrHost = mParent->host();
2108 alock.release();
2109
2110 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal); AssertComRC(hrc2);
2111 if (FAILED(hrc2))
2112 *aVal = FALSE;
2113 }
2114 }
2115 }
2116#endif
2117 break;
2118
2119 default:
2120 return E_INVALIDARG;
2121 }
2122 return S_OK;
2123}
2124
2125STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2126{
2127 AutoCaller autoCaller(this);
2128 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2129
2130 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2131
2132 HRESULT rc = checkStateDependency(MutableStateDep);
2133 if (FAILED(rc)) return rc;
2134
2135 switch (property)
2136 {
2137 case CPUPropertyType_PAE:
2138 setModified(IsModified_MachineData);
2139 mHWData.backup();
2140 mHWData->mPAEEnabled = !!aVal;
2141 break;
2142
2143 case CPUPropertyType_Synthetic:
2144 setModified(IsModified_MachineData);
2145 mHWData.backup();
2146 mHWData->mSyntheticCpu = !!aVal;
2147 break;
2148
2149 case CPUPropertyType_LongMode:
2150 setModified(IsModified_MachineData);
2151 mHWData.backup();
2152 mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2153 break;
2154
2155 default:
2156 return E_INVALIDARG;
2157 }
2158 return S_OK;
2159}
2160
2161STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2162{
2163 CheckComArgOutPointerValid(aValEax);
2164 CheckComArgOutPointerValid(aValEbx);
2165 CheckComArgOutPointerValid(aValEcx);
2166 CheckComArgOutPointerValid(aValEdx);
2167
2168 AutoCaller autoCaller(this);
2169 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2170
2171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2172
2173 switch(aId)
2174 {
2175 case 0x0:
2176 case 0x1:
2177 case 0x2:
2178 case 0x3:
2179 case 0x4:
2180 case 0x5:
2181 case 0x6:
2182 case 0x7:
2183 case 0x8:
2184 case 0x9:
2185 case 0xA:
2186 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2187 return E_INVALIDARG;
2188
2189 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2190 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2191 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2192 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2193 break;
2194
2195 case 0x80000000:
2196 case 0x80000001:
2197 case 0x80000002:
2198 case 0x80000003:
2199 case 0x80000004:
2200 case 0x80000005:
2201 case 0x80000006:
2202 case 0x80000007:
2203 case 0x80000008:
2204 case 0x80000009:
2205 case 0x8000000A:
2206 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2207 return E_INVALIDARG;
2208
2209 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2210 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2211 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2212 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2213 break;
2214
2215 default:
2216 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2217 }
2218 return S_OK;
2219}
2220
2221STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2222{
2223 AutoCaller autoCaller(this);
2224 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2225
2226 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2227
2228 HRESULT rc = checkStateDependency(MutableStateDep);
2229 if (FAILED(rc)) return rc;
2230
2231 switch(aId)
2232 {
2233 case 0x0:
2234 case 0x1:
2235 case 0x2:
2236 case 0x3:
2237 case 0x4:
2238 case 0x5:
2239 case 0x6:
2240 case 0x7:
2241 case 0x8:
2242 case 0x9:
2243 case 0xA:
2244 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2245 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2246 setModified(IsModified_MachineData);
2247 mHWData.backup();
2248 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2249 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2250 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2251 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2252 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2253 break;
2254
2255 case 0x80000000:
2256 case 0x80000001:
2257 case 0x80000002:
2258 case 0x80000003:
2259 case 0x80000004:
2260 case 0x80000005:
2261 case 0x80000006:
2262 case 0x80000007:
2263 case 0x80000008:
2264 case 0x80000009:
2265 case 0x8000000A:
2266 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2267 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2268 setModified(IsModified_MachineData);
2269 mHWData.backup();
2270 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2271 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2272 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2273 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2274 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2275 break;
2276
2277 default:
2278 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2279 }
2280 return S_OK;
2281}
2282
2283STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2284{
2285 AutoCaller autoCaller(this);
2286 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2287
2288 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2289
2290 HRESULT rc = checkStateDependency(MutableStateDep);
2291 if (FAILED(rc)) return rc;
2292
2293 switch(aId)
2294 {
2295 case 0x0:
2296 case 0x1:
2297 case 0x2:
2298 case 0x3:
2299 case 0x4:
2300 case 0x5:
2301 case 0x6:
2302 case 0x7:
2303 case 0x8:
2304 case 0x9:
2305 case 0xA:
2306 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2307 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2308 setModified(IsModified_MachineData);
2309 mHWData.backup();
2310 /* Invalidate leaf. */
2311 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2312 break;
2313
2314 case 0x80000000:
2315 case 0x80000001:
2316 case 0x80000002:
2317 case 0x80000003:
2318 case 0x80000004:
2319 case 0x80000005:
2320 case 0x80000006:
2321 case 0x80000007:
2322 case 0x80000008:
2323 case 0x80000009:
2324 case 0x8000000A:
2325 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2326 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2327 setModified(IsModified_MachineData);
2328 mHWData.backup();
2329 /* Invalidate leaf. */
2330 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2331 break;
2332
2333 default:
2334 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2335 }
2336 return S_OK;
2337}
2338
2339STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2340{
2341 AutoCaller autoCaller(this);
2342 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2343
2344 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2345
2346 HRESULT rc = checkStateDependency(MutableStateDep);
2347 if (FAILED(rc)) return rc;
2348
2349 setModified(IsModified_MachineData);
2350 mHWData.backup();
2351
2352 /* Invalidate all standard leafs. */
2353 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2354 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2355
2356 /* Invalidate all extended leafs. */
2357 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2358 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2359
2360 return S_OK;
2361}
2362
2363STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2364{
2365 CheckComArgOutPointerValid(aVal);
2366
2367 AutoCaller autoCaller(this);
2368 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2369
2370 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2371
2372 switch(property)
2373 {
2374 case HWVirtExPropertyType_Enabled:
2375 *aVal = mHWData->mHWVirtExEnabled;
2376 break;
2377
2378 case HWVirtExPropertyType_Exclusive:
2379 *aVal = mHWData->mHWVirtExExclusive;
2380 break;
2381
2382 case HWVirtExPropertyType_VPID:
2383 *aVal = mHWData->mHWVirtExVPIDEnabled;
2384 break;
2385
2386 case HWVirtExPropertyType_NestedPaging:
2387 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2388 break;
2389
2390 case HWVirtExPropertyType_LargePages:
2391 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2392#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2393 *aVal = FALSE;
2394#endif
2395 break;
2396
2397 case HWVirtExPropertyType_Force:
2398 *aVal = mHWData->mHWVirtExForceEnabled;
2399 break;
2400
2401 default:
2402 return E_INVALIDARG;
2403 }
2404 return S_OK;
2405}
2406
2407STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2408{
2409 AutoCaller autoCaller(this);
2410 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2411
2412 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2413
2414 HRESULT rc = checkStateDependency(MutableStateDep);
2415 if (FAILED(rc)) return rc;
2416
2417 switch(property)
2418 {
2419 case HWVirtExPropertyType_Enabled:
2420 setModified(IsModified_MachineData);
2421 mHWData.backup();
2422 mHWData->mHWVirtExEnabled = !!aVal;
2423 break;
2424
2425 case HWVirtExPropertyType_Exclusive:
2426 setModified(IsModified_MachineData);
2427 mHWData.backup();
2428 mHWData->mHWVirtExExclusive = !!aVal;
2429 break;
2430
2431 case HWVirtExPropertyType_VPID:
2432 setModified(IsModified_MachineData);
2433 mHWData.backup();
2434 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2435 break;
2436
2437 case HWVirtExPropertyType_NestedPaging:
2438 setModified(IsModified_MachineData);
2439 mHWData.backup();
2440 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2441 break;
2442
2443 case HWVirtExPropertyType_LargePages:
2444 setModified(IsModified_MachineData);
2445 mHWData.backup();
2446 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2447 break;
2448
2449 case HWVirtExPropertyType_Force:
2450 setModified(IsModified_MachineData);
2451 mHWData.backup();
2452 mHWData->mHWVirtExForceEnabled = !!aVal;
2453 break;
2454
2455 default:
2456 return E_INVALIDARG;
2457 }
2458
2459 return S_OK;
2460}
2461
2462STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2463{
2464 CheckComArgOutPointerValid(aSnapshotFolder);
2465
2466 AutoCaller autoCaller(this);
2467 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2468
2469 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2470
2471 Utf8Str strFullSnapshotFolder;
2472 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2473 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2474
2475 return S_OK;
2476}
2477
2478STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2479{
2480 /* @todo (r=dmik):
2481 * 1. Allow to change the name of the snapshot folder containing snapshots
2482 * 2. Rename the folder on disk instead of just changing the property
2483 * value (to be smart and not to leave garbage). Note that it cannot be
2484 * done here because the change may be rolled back. Thus, the right
2485 * place is #saveSettings().
2486 */
2487
2488 AutoCaller autoCaller(this);
2489 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2490
2491 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2492
2493 HRESULT rc = checkStateDependency(MutableStateDep);
2494 if (FAILED(rc)) return rc;
2495
2496 if (!mData->mCurrentSnapshot.isNull())
2497 return setError(E_FAIL,
2498 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2499
2500 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2501
2502 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2503 if (strSnapshotFolder.isEmpty())
2504 strSnapshotFolder = "Snapshots";
2505 int vrc = calculateFullPath(strSnapshotFolder,
2506 strSnapshotFolder);
2507 if (RT_FAILURE(vrc))
2508 return setError(E_FAIL,
2509 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2510 aSnapshotFolder, vrc);
2511
2512 setModified(IsModified_MachineData);
2513 mUserData.backup();
2514
2515 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2516
2517 return S_OK;
2518}
2519
2520STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2521{
2522 CheckComArgOutSafeArrayPointerValid(aAttachments);
2523
2524 AutoCaller autoCaller(this);
2525 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2526
2527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2528
2529 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2530 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2531
2532 return S_OK;
2533}
2534
2535STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2536{
2537 CheckComArgOutPointerValid(vrdeServer);
2538
2539 AutoCaller autoCaller(this);
2540 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2541
2542 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2543
2544 Assert(!!mVRDEServer);
2545 mVRDEServer.queryInterfaceTo(vrdeServer);
2546
2547 return S_OK;
2548}
2549
2550STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2551{
2552 CheckComArgOutPointerValid(audioAdapter);
2553
2554 AutoCaller autoCaller(this);
2555 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2556
2557 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2558
2559 mAudioAdapter.queryInterfaceTo(audioAdapter);
2560 return S_OK;
2561}
2562
2563STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2564{
2565#ifdef VBOX_WITH_VUSB
2566 CheckComArgOutPointerValid(aUSBController);
2567
2568 AutoCaller autoCaller(this);
2569 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2570
2571 clearError();
2572 MultiResult rc(S_OK);
2573
2574# ifdef VBOX_WITH_USB
2575 rc = mParent->host()->checkUSBProxyService();
2576 if (FAILED(rc)) return rc;
2577# endif
2578
2579 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2580
2581 return rc = mUSBController.queryInterfaceTo(aUSBController);
2582#else
2583 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2584 * extended error info to indicate that USB is simply not available
2585 * (w/o treating it as a failure), for example, as in OSE */
2586 NOREF(aUSBController);
2587 ReturnComNotImplemented();
2588#endif /* VBOX_WITH_VUSB */
2589}
2590
2591STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2592{
2593 CheckComArgOutPointerValid(aFilePath);
2594
2595 AutoLimitedCaller autoCaller(this);
2596 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2597
2598 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2599
2600 mData->m_strConfigFileFull.cloneTo(aFilePath);
2601 return S_OK;
2602}
2603
2604STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2605{
2606 CheckComArgOutPointerValid(aModified);
2607
2608 AutoCaller autoCaller(this);
2609 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2610
2611 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2612
2613 HRESULT rc = checkStateDependency(MutableStateDep);
2614 if (FAILED(rc)) return rc;
2615
2616 if (!mData->pMachineConfigFile->fileExists())
2617 // this is a new machine, and no config file exists yet:
2618 *aModified = TRUE;
2619 else
2620 *aModified = (mData->flModifications != 0);
2621
2622 return S_OK;
2623}
2624
2625STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2626{
2627 CheckComArgOutPointerValid(aSessionState);
2628
2629 AutoCaller autoCaller(this);
2630 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2631
2632 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2633
2634 *aSessionState = mData->mSession.mState;
2635
2636 return S_OK;
2637}
2638
2639STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2640{
2641 CheckComArgOutPointerValid(aSessionType);
2642
2643 AutoCaller autoCaller(this);
2644 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2645
2646 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2647
2648 mData->mSession.mType.cloneTo(aSessionType);
2649
2650 return S_OK;
2651}
2652
2653STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2654{
2655 CheckComArgOutPointerValid(aSessionPID);
2656
2657 AutoCaller autoCaller(this);
2658 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2659
2660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2661
2662 *aSessionPID = mData->mSession.mPID;
2663
2664 return S_OK;
2665}
2666
2667STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2668{
2669 CheckComArgOutPointerValid(machineState);
2670
2671 AutoCaller autoCaller(this);
2672 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2673
2674 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2675
2676 *machineState = mData->mMachineState;
2677
2678 return S_OK;
2679}
2680
2681STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2682{
2683 CheckComArgOutPointerValid(aLastStateChange);
2684
2685 AutoCaller autoCaller(this);
2686 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2687
2688 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2689
2690 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2691
2692 return S_OK;
2693}
2694
2695STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2696{
2697 CheckComArgOutPointerValid(aStateFilePath);
2698
2699 AutoCaller autoCaller(this);
2700 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2701
2702 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2703
2704 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2705
2706 return S_OK;
2707}
2708
2709STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2710{
2711 CheckComArgOutPointerValid(aLogFolder);
2712
2713 AutoCaller autoCaller(this);
2714 AssertComRCReturnRC(autoCaller.rc());
2715
2716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2717
2718 Utf8Str logFolder;
2719 getLogFolder(logFolder);
2720 logFolder.cloneTo(aLogFolder);
2721
2722 return S_OK;
2723}
2724
2725STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2726{
2727 CheckComArgOutPointerValid(aCurrentSnapshot);
2728
2729 AutoCaller autoCaller(this);
2730 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2731
2732 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2733
2734 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2735
2736 return S_OK;
2737}
2738
2739STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2740{
2741 CheckComArgOutPointerValid(aSnapshotCount);
2742
2743 AutoCaller autoCaller(this);
2744 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2745
2746 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2747
2748 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2749 ? 0
2750 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2751
2752 return S_OK;
2753}
2754
2755STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2756{
2757 CheckComArgOutPointerValid(aCurrentStateModified);
2758
2759 AutoCaller autoCaller(this);
2760 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2761
2762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2763
2764 /* Note: for machines with no snapshots, we always return FALSE
2765 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2766 * reasons :) */
2767
2768 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2769 ? FALSE
2770 : mData->mCurrentStateModified;
2771
2772 return S_OK;
2773}
2774
2775STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2776{
2777 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2778
2779 AutoCaller autoCaller(this);
2780 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2781
2782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2783
2784 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2785 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2786
2787 return S_OK;
2788}
2789
2790STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2791{
2792 CheckComArgOutPointerValid(aClipboardMode);
2793
2794 AutoCaller autoCaller(this);
2795 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2796
2797 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2798
2799 *aClipboardMode = mHWData->mClipboardMode;
2800
2801 return S_OK;
2802}
2803
2804STDMETHODIMP
2805Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2806{
2807 HRESULT rc = S_OK;
2808
2809 AutoCaller autoCaller(this);
2810 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2811
2812 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2813
2814 alock.release();
2815 rc = onClipboardModeChange(aClipboardMode);
2816 alock.acquire();
2817 if (FAILED(rc)) return rc;
2818
2819 setModified(IsModified_MachineData);
2820 mHWData.backup();
2821 mHWData->mClipboardMode = aClipboardMode;
2822
2823 /* Save settings if online - todo why is this required?? */
2824 if (Global::IsOnline(mData->mMachineState))
2825 saveSettings(NULL);
2826
2827 return S_OK;
2828}
2829
2830STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
2831{
2832 CheckComArgOutPointerValid(aDragAndDropMode);
2833
2834 AutoCaller autoCaller(this);
2835 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2836
2837 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2838
2839 *aDragAndDropMode = mHWData->mDragAndDropMode;
2840
2841 return S_OK;
2842}
2843
2844STDMETHODIMP
2845Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
2846{
2847 HRESULT rc = S_OK;
2848
2849 AutoCaller autoCaller(this);
2850 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2851
2852 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2853
2854 alock.release();
2855 rc = onDragAndDropModeChange(aDragAndDropMode);
2856 alock.acquire();
2857 if (FAILED(rc)) return rc;
2858
2859 setModified(IsModified_MachineData);
2860 mHWData.backup();
2861 mHWData->mDragAndDropMode = aDragAndDropMode;
2862
2863 /* Save settings if online - todo why is this required?? */
2864 if (Global::IsOnline(mData->mMachineState))
2865 saveSettings(NULL);
2866
2867 return S_OK;
2868}
2869
2870STDMETHODIMP
2871Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
2872{
2873 CheckComArgOutPointerValid(aPatterns);
2874
2875 AutoCaller autoCaller(this);
2876 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2877
2878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2879
2880 try
2881 {
2882 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
2883 }
2884 catch (...)
2885 {
2886 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2887 }
2888
2889 return S_OK;
2890}
2891
2892STDMETHODIMP
2893Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
2894{
2895 AutoCaller autoCaller(this);
2896 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2897
2898 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2899
2900 HRESULT rc = checkStateDependency(MutableStateDep);
2901 if (FAILED(rc)) return rc;
2902
2903 setModified(IsModified_MachineData);
2904 mHWData.backup();
2905 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
2906 return rc;
2907}
2908
2909STDMETHODIMP
2910Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
2911{
2912 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
2913
2914 AutoCaller autoCaller(this);
2915 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2916
2917 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2918
2919 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
2920 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
2921
2922 return S_OK;
2923}
2924
2925STDMETHODIMP
2926Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
2927{
2928 CheckComArgOutPointerValid(aEnabled);
2929
2930 AutoCaller autoCaller(this);
2931 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2932
2933 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2934
2935 *aEnabled = mUserData->s.fTeleporterEnabled;
2936
2937 return S_OK;
2938}
2939
2940STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
2941{
2942 AutoCaller autoCaller(this);
2943 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2944
2945 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2946
2947 /* Only allow it to be set to true when PoweredOff or Aborted.
2948 (Clearing it is always permitted.) */
2949 if ( aEnabled
2950 && mData->mRegistered
2951 && ( !isSessionMachine()
2952 || ( mData->mMachineState != MachineState_PoweredOff
2953 && mData->mMachineState != MachineState_Teleported
2954 && mData->mMachineState != MachineState_Aborted
2955 )
2956 )
2957 )
2958 return setError(VBOX_E_INVALID_VM_STATE,
2959 tr("The machine is not powered off (state is %s)"),
2960 Global::stringifyMachineState(mData->mMachineState));
2961
2962 setModified(IsModified_MachineData);
2963 mUserData.backup();
2964 mUserData->s.fTeleporterEnabled = !!aEnabled;
2965
2966 return S_OK;
2967}
2968
2969STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
2970{
2971 CheckComArgOutPointerValid(aPort);
2972
2973 AutoCaller autoCaller(this);
2974 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2975
2976 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2977
2978 *aPort = (ULONG)mUserData->s.uTeleporterPort;
2979
2980 return S_OK;
2981}
2982
2983STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
2984{
2985 if (aPort >= _64K)
2986 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
2987
2988 AutoCaller autoCaller(this);
2989 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2990
2991 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2992
2993 HRESULT rc = checkStateDependency(MutableStateDep);
2994 if (FAILED(rc)) return rc;
2995
2996 setModified(IsModified_MachineData);
2997 mUserData.backup();
2998 mUserData->s.uTeleporterPort = (uint32_t)aPort;
2999
3000 return S_OK;
3001}
3002
3003STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
3004{
3005 CheckComArgOutPointerValid(aAddress);
3006
3007 AutoCaller autoCaller(this);
3008 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3009
3010 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3011
3012 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
3013
3014 return S_OK;
3015}
3016
3017STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
3018{
3019 AutoCaller autoCaller(this);
3020 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3021
3022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3023
3024 HRESULT rc = checkStateDependency(MutableStateDep);
3025 if (FAILED(rc)) return rc;
3026
3027 setModified(IsModified_MachineData);
3028 mUserData.backup();
3029 mUserData->s.strTeleporterAddress = aAddress;
3030
3031 return S_OK;
3032}
3033
3034STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
3035{
3036 CheckComArgOutPointerValid(aPassword);
3037
3038 AutoCaller autoCaller(this);
3039 HRESULT hrc = autoCaller.rc();
3040 if (SUCCEEDED(hrc))
3041 {
3042 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3043 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
3044 }
3045
3046 return hrc;
3047}
3048
3049STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
3050{
3051 /*
3052 * Hash the password first.
3053 */
3054 Utf8Str strPassword(aPassword);
3055 if (!strPassword.isEmpty())
3056 {
3057 if (VBoxIsPasswordHashed(&strPassword))
3058 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3059 VBoxHashPassword(&strPassword);
3060 }
3061
3062 /*
3063 * Do the update.
3064 */
3065 AutoCaller autoCaller(this);
3066 HRESULT hrc = autoCaller.rc();
3067 if (SUCCEEDED(hrc))
3068 {
3069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3070 hrc = checkStateDependency(MutableStateDep);
3071 if (SUCCEEDED(hrc))
3072 {
3073 setModified(IsModified_MachineData);
3074 mUserData.backup();
3075 mUserData->s.strTeleporterPassword = strPassword;
3076 }
3077 }
3078
3079 return hrc;
3080}
3081
3082STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
3083{
3084 CheckComArgOutPointerValid(aState);
3085
3086 AutoCaller autoCaller(this);
3087 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3088
3089 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3090
3091 *aState = mUserData->s.enmFaultToleranceState;
3092 return S_OK;
3093}
3094
3095STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
3096{
3097 AutoCaller autoCaller(this);
3098 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3099
3100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3101
3102 /* @todo deal with running state change. */
3103 HRESULT rc = checkStateDependency(MutableStateDep);
3104 if (FAILED(rc)) return rc;
3105
3106 setModified(IsModified_MachineData);
3107 mUserData.backup();
3108 mUserData->s.enmFaultToleranceState = aState;
3109 return S_OK;
3110}
3111
3112STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3113{
3114 CheckComArgOutPointerValid(aAddress);
3115
3116 AutoCaller autoCaller(this);
3117 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3118
3119 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3120
3121 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3122 return S_OK;
3123}
3124
3125STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3126{
3127 AutoCaller autoCaller(this);
3128 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3129
3130 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3131
3132 /* @todo deal with running state change. */
3133 HRESULT rc = checkStateDependency(MutableStateDep);
3134 if (FAILED(rc)) return rc;
3135
3136 setModified(IsModified_MachineData);
3137 mUserData.backup();
3138 mUserData->s.strFaultToleranceAddress = aAddress;
3139 return S_OK;
3140}
3141
3142STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3143{
3144 CheckComArgOutPointerValid(aPort);
3145
3146 AutoCaller autoCaller(this);
3147 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3148
3149 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3150
3151 *aPort = mUserData->s.uFaultTolerancePort;
3152 return S_OK;
3153}
3154
3155STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3156{
3157 AutoCaller autoCaller(this);
3158 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3159
3160 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3161
3162 /* @todo deal with running state change. */
3163 HRESULT rc = checkStateDependency(MutableStateDep);
3164 if (FAILED(rc)) return rc;
3165
3166 setModified(IsModified_MachineData);
3167 mUserData.backup();
3168 mUserData->s.uFaultTolerancePort = aPort;
3169 return S_OK;
3170}
3171
3172STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3173{
3174 CheckComArgOutPointerValid(aPassword);
3175
3176 AutoCaller autoCaller(this);
3177 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3178
3179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3180
3181 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3182
3183 return S_OK;
3184}
3185
3186STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3187{
3188 AutoCaller autoCaller(this);
3189 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3190
3191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3192
3193 /* @todo deal with running state change. */
3194 HRESULT rc = checkStateDependency(MutableStateDep);
3195 if (FAILED(rc)) return rc;
3196
3197 setModified(IsModified_MachineData);
3198 mUserData.backup();
3199 mUserData->s.strFaultTolerancePassword = aPassword;
3200
3201 return S_OK;
3202}
3203
3204STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3205{
3206 CheckComArgOutPointerValid(aInterval);
3207
3208 AutoCaller autoCaller(this);
3209 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3210
3211 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3212
3213 *aInterval = mUserData->s.uFaultToleranceInterval;
3214 return S_OK;
3215}
3216
3217STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3218{
3219 AutoCaller autoCaller(this);
3220 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3221
3222 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3223
3224 /* @todo deal with running state change. */
3225 HRESULT rc = checkStateDependency(MutableStateDep);
3226 if (FAILED(rc)) return rc;
3227
3228 setModified(IsModified_MachineData);
3229 mUserData.backup();
3230 mUserData->s.uFaultToleranceInterval = aInterval;
3231 return S_OK;
3232}
3233
3234STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3235{
3236 CheckComArgOutPointerValid(aEnabled);
3237
3238 AutoCaller autoCaller(this);
3239 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3240
3241 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3242
3243 *aEnabled = mUserData->s.fRTCUseUTC;
3244
3245 return S_OK;
3246}
3247
3248STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3249{
3250 AutoCaller autoCaller(this);
3251 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3252
3253 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3254
3255 /* Only allow it to be set to true when PoweredOff or Aborted.
3256 (Clearing it is always permitted.) */
3257 if ( aEnabled
3258 && mData->mRegistered
3259 && ( !isSessionMachine()
3260 || ( mData->mMachineState != MachineState_PoweredOff
3261 && mData->mMachineState != MachineState_Teleported
3262 && mData->mMachineState != MachineState_Aborted
3263 )
3264 )
3265 )
3266 return setError(VBOX_E_INVALID_VM_STATE,
3267 tr("The machine is not powered off (state is %s)"),
3268 Global::stringifyMachineState(mData->mMachineState));
3269
3270 setModified(IsModified_MachineData);
3271 mUserData.backup();
3272 mUserData->s.fRTCUseUTC = !!aEnabled;
3273
3274 return S_OK;
3275}
3276
3277STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3278{
3279 CheckComArgOutPointerValid(aEnabled);
3280
3281 AutoCaller autoCaller(this);
3282 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3283
3284 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3285
3286 *aEnabled = mHWData->mIOCacheEnabled;
3287
3288 return S_OK;
3289}
3290
3291STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3292{
3293 AutoCaller autoCaller(this);
3294 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3295
3296 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3297
3298 HRESULT rc = checkStateDependency(MutableStateDep);
3299 if (FAILED(rc)) return rc;
3300
3301 setModified(IsModified_MachineData);
3302 mHWData.backup();
3303 mHWData->mIOCacheEnabled = aEnabled;
3304
3305 return S_OK;
3306}
3307
3308STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3309{
3310 CheckComArgOutPointerValid(aIOCacheSize);
3311
3312 AutoCaller autoCaller(this);
3313 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3314
3315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3316
3317 *aIOCacheSize = mHWData->mIOCacheSize;
3318
3319 return S_OK;
3320}
3321
3322STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3323{
3324 AutoCaller autoCaller(this);
3325 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3326
3327 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3328
3329 HRESULT rc = checkStateDependency(MutableStateDep);
3330 if (FAILED(rc)) return rc;
3331
3332 setModified(IsModified_MachineData);
3333 mHWData.backup();
3334 mHWData->mIOCacheSize = aIOCacheSize;
3335
3336 return S_OK;
3337}
3338
3339
3340/**
3341 * @note Locks objects!
3342 */
3343STDMETHODIMP Machine::LockMachine(ISession *aSession,
3344 LockType_T lockType)
3345{
3346 CheckComArgNotNull(aSession);
3347
3348 AutoCaller autoCaller(this);
3349 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3350
3351 /* check the session state */
3352 SessionState_T state;
3353 HRESULT rc = aSession->COMGETTER(State)(&state);
3354 if (FAILED(rc)) return rc;
3355
3356 if (state != SessionState_Unlocked)
3357 return setError(VBOX_E_INVALID_OBJECT_STATE,
3358 tr("The given session is busy"));
3359
3360 // get the client's IInternalSessionControl interface
3361 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3362 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3363 E_INVALIDARG);
3364
3365 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3366
3367 if (!mData->mRegistered)
3368 return setError(E_UNEXPECTED,
3369 tr("The machine '%s' is not registered"),
3370 mUserData->s.strName.c_str());
3371
3372 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3373
3374 SessionState_T oldState = mData->mSession.mState;
3375 /* Hack: in case the session is closing and there is a progress object
3376 * which allows waiting for the session to be closed, take the opportunity
3377 * and do a limited wait (max. 1 second). This helps a lot when the system
3378 * is busy and thus session closing can take a little while. */
3379 if ( mData->mSession.mState == SessionState_Unlocking
3380 && mData->mSession.mProgress)
3381 {
3382 alock.release();
3383 mData->mSession.mProgress->WaitForCompletion(1000);
3384 alock.acquire();
3385 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3386 }
3387
3388 // try again now
3389 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3390 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3391 )
3392 {
3393 // OK, share the session... we are now dealing with three processes:
3394 // 1) VBoxSVC (where this code runs);
3395 // 2) process C: the caller's client process (who wants a shared session);
3396 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3397
3398 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3399 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3400 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3401 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3402 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3403
3404 /*
3405 * Release the lock before calling the client process. It's safe here
3406 * since the only thing to do after we get the lock again is to add
3407 * the remote control to the list (which doesn't directly influence
3408 * anything).
3409 */
3410 alock.release();
3411
3412 // get the console of the session holding the write lock (this is a remote call)
3413 ComPtr<IConsole> pConsoleW;
3414 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3415 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3416 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3417 if (FAILED(rc))
3418 // the failure may occur w/o any error info (from RPC), so provide one
3419 return setError(VBOX_E_VM_ERROR,
3420 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3421
3422 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3423
3424 // share the session machine and W's console with the caller's session
3425 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3426 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3427 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3428
3429 if (FAILED(rc))
3430 // the failure may occur w/o any error info (from RPC), so provide one
3431 return setError(VBOX_E_VM_ERROR,
3432 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3433 alock.acquire();
3434
3435 // need to revalidate the state after acquiring the lock again
3436 if (mData->mSession.mState != SessionState_Locked)
3437 {
3438 pSessionControl->Uninitialize();
3439 return setError(VBOX_E_INVALID_SESSION_STATE,
3440 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3441 mUserData->s.strName.c_str());
3442 }
3443
3444 // add the caller's session to the list
3445 mData->mSession.mRemoteControls.push_back(pSessionControl);
3446 }
3447 else if ( mData->mSession.mState == SessionState_Locked
3448 || mData->mSession.mState == SessionState_Unlocking
3449 )
3450 {
3451 // sharing not permitted, or machine still unlocking:
3452 return setError(VBOX_E_INVALID_OBJECT_STATE,
3453 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3454 mUserData->s.strName.c_str());
3455 }
3456 else
3457 {
3458 // machine is not locked: then write-lock the machine (create the session machine)
3459
3460 // must not be busy
3461 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3462
3463 // get the caller's session PID
3464 RTPROCESS pid = NIL_RTPROCESS;
3465 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3466 pSessionControl->GetPID((ULONG*)&pid);
3467 Assert(pid != NIL_RTPROCESS);
3468
3469 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3470
3471 if (fLaunchingVMProcess)
3472 {
3473 // this machine is awaiting for a spawning session to be opened:
3474 // then the calling process must be the one that got started by
3475 // LaunchVMProcess()
3476
3477 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3478 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3479
3480 if (mData->mSession.mPID != pid)
3481 return setError(E_ACCESSDENIED,
3482 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3483 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3484 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3485 }
3486
3487 // create the mutable SessionMachine from the current machine
3488 ComObjPtr<SessionMachine> sessionMachine;
3489 sessionMachine.createObject();
3490 rc = sessionMachine->init(this);
3491 AssertComRC(rc);
3492
3493 /* NOTE: doing return from this function after this point but
3494 * before the end is forbidden since it may call SessionMachine::uninit()
3495 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3496 * lock while still holding the Machine lock in alock so that a deadlock
3497 * is possible due to the wrong lock order. */
3498
3499 if (SUCCEEDED(rc))
3500 {
3501 /*
3502 * Set the session state to Spawning to protect against subsequent
3503 * attempts to open a session and to unregister the machine after
3504 * we release the lock.
3505 */
3506 SessionState_T origState = mData->mSession.mState;
3507 mData->mSession.mState = SessionState_Spawning;
3508
3509 /*
3510 * Release the lock before calling the client process -- it will call
3511 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3512 * because the state is Spawning, so that LaunchVMProcess() and
3513 * LockMachine() calls will fail. This method, called before we
3514 * acquire the lock again, will fail because of the wrong PID.
3515 *
3516 * Note that mData->mSession.mRemoteControls accessed outside
3517 * the lock may not be modified when state is Spawning, so it's safe.
3518 */
3519 alock.release();
3520
3521 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3522 rc = pSessionControl->AssignMachine(sessionMachine, lockType);
3523 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3524
3525 /* The failure may occur w/o any error info (from RPC), so provide one */
3526 if (FAILED(rc))
3527 setError(VBOX_E_VM_ERROR,
3528 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3529
3530 if ( SUCCEEDED(rc)
3531 && fLaunchingVMProcess
3532 )
3533 {
3534 /* complete the remote session initialization */
3535
3536 /* get the console from the direct session */
3537 ComPtr<IConsole> console;
3538 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3539 ComAssertComRC(rc);
3540
3541 if (SUCCEEDED(rc) && !console)
3542 {
3543 ComAssert(!!console);
3544 rc = E_FAIL;
3545 }
3546
3547 /* assign machine & console to the remote session */
3548 if (SUCCEEDED(rc))
3549 {
3550 /*
3551 * after LaunchVMProcess(), the first and the only
3552 * entry in remoteControls is that remote session
3553 */
3554 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3555 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3556 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3557
3558 /* The failure may occur w/o any error info (from RPC), so provide one */
3559 if (FAILED(rc))
3560 setError(VBOX_E_VM_ERROR,
3561 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3562 }
3563
3564 if (FAILED(rc))
3565 pSessionControl->Uninitialize();
3566 }
3567
3568 /* acquire the lock again */
3569 alock.acquire();
3570
3571 /* Restore the session state */
3572 mData->mSession.mState = origState;
3573 }
3574
3575 // finalize spawning anyway (this is why we don't return on errors above)
3576 if (fLaunchingVMProcess)
3577 {
3578 /* Note that the progress object is finalized later */
3579 /** @todo Consider checking mData->mSession.mProgress for cancellation
3580 * around here. */
3581
3582 /* We don't reset mSession.mPID here because it is necessary for
3583 * SessionMachine::uninit() to reap the child process later. */
3584
3585 if (FAILED(rc))
3586 {
3587 /* Close the remote session, remove the remote control from the list
3588 * and reset session state to Closed (@note keep the code in sync
3589 * with the relevant part in openSession()). */
3590
3591 Assert(mData->mSession.mRemoteControls.size() == 1);
3592 if (mData->mSession.mRemoteControls.size() == 1)
3593 {
3594 ErrorInfoKeeper eik;
3595 mData->mSession.mRemoteControls.front()->Uninitialize();
3596 }
3597
3598 mData->mSession.mRemoteControls.clear();
3599 mData->mSession.mState = SessionState_Unlocked;
3600 }
3601 }
3602 else
3603 {
3604 /* memorize PID of the directly opened session */
3605 if (SUCCEEDED(rc))
3606 mData->mSession.mPID = pid;
3607 }
3608
3609 if (SUCCEEDED(rc))
3610 {
3611 /* memorize the direct session control and cache IUnknown for it */
3612 mData->mSession.mDirectControl = pSessionControl;
3613 mData->mSession.mState = SessionState_Locked;
3614 /* associate the SessionMachine with this Machine */
3615 mData->mSession.mMachine = sessionMachine;
3616
3617 /* request an IUnknown pointer early from the remote party for later
3618 * identity checks (it will be internally cached within mDirectControl
3619 * at least on XPCOM) */
3620 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3621 NOREF(unk);
3622 }
3623
3624 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3625 * would break the lock order */
3626 alock.release();
3627
3628 /* uninitialize the created session machine on failure */
3629 if (FAILED(rc))
3630 sessionMachine->uninit();
3631
3632 }
3633
3634 if (SUCCEEDED(rc))
3635 {
3636 /*
3637 * tell the client watcher thread to update the set of
3638 * machines that have open sessions
3639 */
3640 mParent->updateClientWatcher();
3641
3642 if (oldState != SessionState_Locked)
3643 /* fire an event */
3644 mParent->onSessionStateChange(getId(), SessionState_Locked);
3645 }
3646
3647 return rc;
3648}
3649
3650/**
3651 * @note Locks objects!
3652 */
3653STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3654 IN_BSTR aFrontend,
3655 IN_BSTR aEnvironment,
3656 IProgress **aProgress)
3657{
3658 CheckComArgStr(aFrontend);
3659 Utf8Str strFrontend(aFrontend);
3660 Utf8Str strEnvironment(aEnvironment);
3661 /* "emergencystop" doesn't need the session, so skip the checks/interface
3662 * retrieval. This code doesn't quite fit in here, but introducing a
3663 * special API method would be even more effort, and would require explicit
3664 * support by every API client. It's better to hide the feature a bit. */
3665 if (strFrontend != "emergencystop")
3666 CheckComArgNotNull(aSession);
3667 CheckComArgOutPointerValid(aProgress);
3668
3669 AutoCaller autoCaller(this);
3670 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3671
3672 HRESULT rc = S_OK;
3673 if (strFrontend.isEmpty())
3674 {
3675 Bstr bstrFrontend;
3676 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3677 if (FAILED(rc))
3678 return rc;
3679 strFrontend = bstrFrontend;
3680 if (strFrontend.isEmpty())
3681 {
3682 ComPtr<ISystemProperties> systemProperties;
3683 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3684 if (FAILED(rc))
3685 return rc;
3686 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3687 if (FAILED(rc))
3688 return rc;
3689 strFrontend = bstrFrontend;
3690 }
3691 /* paranoia - emergencystop is not a valid default */
3692 if (strFrontend == "emergencystop")
3693 strFrontend = Utf8Str::Empty;
3694 }
3695
3696 if (strFrontend != "emergencystop")
3697 {
3698 /* check the session state */
3699 SessionState_T state;
3700 rc = aSession->COMGETTER(State)(&state);
3701 if (FAILED(rc))
3702 return rc;
3703
3704 if (state != SessionState_Unlocked)
3705 return setError(VBOX_E_INVALID_OBJECT_STATE,
3706 tr("The given session is busy"));
3707
3708 /* get the IInternalSessionControl interface */
3709 ComPtr<IInternalSessionControl> control(aSession);
3710 ComAssertMsgRet(!control.isNull(),
3711 ("No IInternalSessionControl interface"),
3712 E_INVALIDARG);
3713
3714 /* get the teleporter enable state for the progress object init. */
3715 BOOL fTeleporterEnabled;
3716 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3717 if (FAILED(rc))
3718 return rc;
3719
3720 /* create a progress object */
3721 ComObjPtr<ProgressProxy> progress;
3722 progress.createObject();
3723 rc = progress->init(mParent,
3724 static_cast<IMachine*>(this),
3725 Bstr(tr("Starting VM")).raw(),
3726 TRUE /* aCancelable */,
3727 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3728 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3729 2 /* uFirstOperationWeight */,
3730 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3731
3732 if (SUCCEEDED(rc))
3733 {
3734 rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
3735 if (SUCCEEDED(rc))
3736 {
3737 progress.queryInterfaceTo(aProgress);
3738
3739 /* signal the client watcher thread */
3740 mParent->updateClientWatcher();
3741
3742 /* fire an event */
3743 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3744 }
3745 }
3746 }
3747 else
3748 {
3749 /* no progress object - either instant success or failure */
3750 *aProgress = NULL;
3751
3752 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3753
3754 if (mData->mSession.mState != SessionState_Locked)
3755 return setError(VBOX_E_INVALID_OBJECT_STATE,
3756 tr("The machine '%s' is not locked by a session"),
3757 mUserData->s.strName.c_str());
3758
3759 /* must have a VM process associated - do not kill normal API clients
3760 * with an open session */
3761 if (!Global::IsOnline(mData->mMachineState))
3762 return setError(VBOX_E_INVALID_OBJECT_STATE,
3763 tr("The machine '%s' does not have a VM process"),
3764 mUserData->s.strName.c_str());
3765
3766 /* forcibly terminate the VM process */
3767 if (mData->mSession.mPID != NIL_RTPROCESS)
3768 RTProcTerminate(mData->mSession.mPID);
3769
3770 /* signal the client watcher thread, as most likely the client has
3771 * been terminated */
3772 mParent->updateClientWatcher();
3773 }
3774
3775 return rc;
3776}
3777
3778STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3779{
3780 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3781 return setError(E_INVALIDARG,
3782 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3783 aPosition, SchemaDefs::MaxBootPosition);
3784
3785 if (aDevice == DeviceType_USB)
3786 return setError(E_NOTIMPL,
3787 tr("Booting from USB device is currently not supported"));
3788
3789 AutoCaller autoCaller(this);
3790 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3791
3792 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3793
3794 HRESULT rc = checkStateDependency(MutableStateDep);
3795 if (FAILED(rc)) return rc;
3796
3797 setModified(IsModified_MachineData);
3798 mHWData.backup();
3799 mHWData->mBootOrder[aPosition - 1] = aDevice;
3800
3801 return S_OK;
3802}
3803
3804STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3805{
3806 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3807 return setError(E_INVALIDARG,
3808 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3809 aPosition, SchemaDefs::MaxBootPosition);
3810
3811 AutoCaller autoCaller(this);
3812 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3813
3814 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3815
3816 *aDevice = mHWData->mBootOrder[aPosition - 1];
3817
3818 return S_OK;
3819}
3820
3821STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
3822 LONG aControllerPort,
3823 LONG aDevice,
3824 DeviceType_T aType,
3825 IMedium *aMedium)
3826{
3827 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3828 aControllerName, aControllerPort, aDevice, aType, aMedium));
3829
3830 CheckComArgStrNotEmptyOrNull(aControllerName);
3831
3832 AutoCaller autoCaller(this);
3833 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3834
3835 // request the host lock first, since might be calling Host methods for getting host drives;
3836 // next, protect the media tree all the while we're in here, as well as our member variables
3837 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
3838 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3839
3840 HRESULT rc = checkStateDependency(MutableStateDep);
3841 if (FAILED(rc)) return rc;
3842
3843 /// @todo NEWMEDIA implicit machine registration
3844 if (!mData->mRegistered)
3845 return setError(VBOX_E_INVALID_OBJECT_STATE,
3846 tr("Cannot attach storage devices to an unregistered machine"));
3847
3848 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3849
3850 /* Check for an existing controller. */
3851 ComObjPtr<StorageController> ctl;
3852 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
3853 if (FAILED(rc)) return rc;
3854
3855 StorageControllerType_T ctrlType;
3856 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3857 if (FAILED(rc))
3858 return setError(E_FAIL,
3859 tr("Could not get type of controller '%ls'"),
3860 aControllerName);
3861
3862 bool fSilent = false;
3863 Utf8Str strReconfig;
3864
3865 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3866 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3867 if (FAILED(rc))
3868 return rc;
3869 if ( mData->mMachineState == MachineState_Paused
3870 && strReconfig == "1")
3871 fSilent = true;
3872
3873 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3874 bool fHotplug = false;
3875 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3876 fHotplug = true;
3877
3878 if (fHotplug && !isControllerHotplugCapable(ctrlType))
3879 return setError(VBOX_E_INVALID_VM_STATE,
3880 tr("Controller '%ls' does not support hotplugging"),
3881 aControllerName);
3882
3883 // check that the port and device are not out of range
3884 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
3885 if (FAILED(rc)) return rc;
3886
3887 /* check if the device slot is already busy */
3888 MediumAttachment *pAttachTemp;
3889 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
3890 aControllerName,
3891 aControllerPort,
3892 aDevice)))
3893 {
3894 Medium *pMedium = pAttachTemp->getMedium();
3895 if (pMedium)
3896 {
3897 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3898 return setError(VBOX_E_OBJECT_IN_USE,
3899 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3900 pMedium->getLocationFull().c_str(),
3901 aControllerPort,
3902 aDevice,
3903 aControllerName);
3904 }
3905 else
3906 return setError(VBOX_E_OBJECT_IN_USE,
3907 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3908 aControllerPort, aDevice, aControllerName);
3909 }
3910
3911 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
3912 if (aMedium && medium.isNull())
3913 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3914
3915 AutoCaller mediumCaller(medium);
3916 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3917
3918 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3919
3920 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
3921 && !medium.isNull()
3922 )
3923 return setError(VBOX_E_OBJECT_IN_USE,
3924 tr("Medium '%s' is already attached to this virtual machine"),
3925 medium->getLocationFull().c_str());
3926
3927 if (!medium.isNull())
3928 {
3929 MediumType_T mtype = medium->getType();
3930 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3931 // For DVDs it's not written to the config file, so needs no global config
3932 // version bump. For floppies it's a new attribute "type", which is ignored
3933 // by older VirtualBox version, so needs no global config version bump either.
3934 // For hard disks this type is not accepted.
3935 if (mtype == MediumType_MultiAttach)
3936 {
3937 // This type is new with VirtualBox 4.0 and therefore requires settings
3938 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3939 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3940 // two reasons: The medium type is a property of the media registry tree, which
3941 // can reside in the global config file (for pre-4.0 media); we would therefore
3942 // possibly need to bump the global config version. We don't want to do that though
3943 // because that might make downgrading to pre-4.0 impossible.
3944 // As a result, we can only use these two new types if the medium is NOT in the
3945 // global registry:
3946 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
3947 if ( medium->isInRegistry(uuidGlobalRegistry)
3948 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3949 )
3950 return setError(VBOX_E_INVALID_OBJECT_STATE,
3951 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3952 "to machines that were created with VirtualBox 4.0 or later"),
3953 medium->getLocationFull().c_str());
3954 }
3955 }
3956
3957 bool fIndirect = false;
3958 if (!medium.isNull())
3959 fIndirect = medium->isReadOnly();
3960 bool associate = true;
3961
3962 do
3963 {
3964 if ( aType == DeviceType_HardDisk
3965 && mMediaData.isBackedUp())
3966 {
3967 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3968
3969 /* check if the medium was attached to the VM before we started
3970 * changing attachments in which case the attachment just needs to
3971 * be restored */
3972 if ((pAttachTemp = findAttachment(oldAtts, medium)))
3973 {
3974 AssertReturn(!fIndirect, E_FAIL);
3975
3976 /* see if it's the same bus/channel/device */
3977 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
3978 {
3979 /* the simplest case: restore the whole attachment
3980 * and return, nothing else to do */
3981 mMediaData->mAttachments.push_back(pAttachTemp);
3982 return S_OK;
3983 }
3984
3985 /* bus/channel/device differ; we need a new attachment object,
3986 * but don't try to associate it again */
3987 associate = false;
3988 break;
3989 }
3990 }
3991
3992 /* go further only if the attachment is to be indirect */
3993 if (!fIndirect)
3994 break;
3995
3996 /* perform the so called smart attachment logic for indirect
3997 * attachments. Note that smart attachment is only applicable to base
3998 * hard disks. */
3999
4000 if (medium->getParent().isNull())
4001 {
4002 /* first, investigate the backup copy of the current hard disk
4003 * attachments to make it possible to re-attach existing diffs to
4004 * another device slot w/o losing their contents */
4005 if (mMediaData.isBackedUp())
4006 {
4007 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4008
4009 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4010 uint32_t foundLevel = 0;
4011
4012 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
4013 it != oldAtts.end();
4014 ++it)
4015 {
4016 uint32_t level = 0;
4017 MediumAttachment *pAttach = *it;
4018 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4019 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4020 if (pMedium.isNull())
4021 continue;
4022
4023 if (pMedium->getBase(&level) == medium)
4024 {
4025 /* skip the hard disk if its currently attached (we
4026 * cannot attach the same hard disk twice) */
4027 if (findAttachment(mMediaData->mAttachments,
4028 pMedium))
4029 continue;
4030
4031 /* matched device, channel and bus (i.e. attached to the
4032 * same place) will win and immediately stop the search;
4033 * otherwise the attachment that has the youngest
4034 * descendant of medium will be used
4035 */
4036 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
4037 {
4038 /* the simplest case: restore the whole attachment
4039 * and return, nothing else to do */
4040 mMediaData->mAttachments.push_back(*it);
4041 return S_OK;
4042 }
4043 else if ( foundIt == oldAtts.end()
4044 || level > foundLevel /* prefer younger */
4045 )
4046 {
4047 foundIt = it;
4048 foundLevel = level;
4049 }
4050 }
4051 }
4052
4053 if (foundIt != oldAtts.end())
4054 {
4055 /* use the previously attached hard disk */
4056 medium = (*foundIt)->getMedium();
4057 mediumCaller.attach(medium);
4058 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4059 mediumLock.attach(medium);
4060 /* not implicit, doesn't require association with this VM */
4061 fIndirect = false;
4062 associate = false;
4063 /* go right to the MediumAttachment creation */
4064 break;
4065 }
4066 }
4067
4068 /* must give up the medium lock and medium tree lock as below we
4069 * go over snapshots, which needs a lock with higher lock order. */
4070 mediumLock.release();
4071 treeLock.release();
4072
4073 /* then, search through snapshots for the best diff in the given
4074 * hard disk's chain to base the new diff on */
4075
4076 ComObjPtr<Medium> base;
4077 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4078 while (snap)
4079 {
4080 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4081
4082 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
4083
4084 MediumAttachment *pAttachFound = NULL;
4085 uint32_t foundLevel = 0;
4086
4087 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
4088 it != snapAtts.end();
4089 ++it)
4090 {
4091 MediumAttachment *pAttach = *it;
4092 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4093 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4094 if (pMedium.isNull())
4095 continue;
4096
4097 uint32_t level = 0;
4098 if (pMedium->getBase(&level) == medium)
4099 {
4100 /* matched device, channel and bus (i.e. attached to the
4101 * same place) will win and immediately stop the search;
4102 * otherwise the attachment that has the youngest
4103 * descendant of medium will be used
4104 */
4105 if ( pAttach->getDevice() == aDevice
4106 && pAttach->getPort() == aControllerPort
4107 && pAttach->getControllerName() == aControllerName
4108 )
4109 {
4110 pAttachFound = pAttach;
4111 break;
4112 }
4113 else if ( !pAttachFound
4114 || level > foundLevel /* prefer younger */
4115 )
4116 {
4117 pAttachFound = pAttach;
4118 foundLevel = level;
4119 }
4120 }
4121 }
4122
4123 if (pAttachFound)
4124 {
4125 base = pAttachFound->getMedium();
4126 break;
4127 }
4128
4129 snap = snap->getParent();
4130 }
4131
4132 /* re-lock medium tree and the medium, as we need it below */
4133 treeLock.acquire();
4134 mediumLock.acquire();
4135
4136 /* found a suitable diff, use it as a base */
4137 if (!base.isNull())
4138 {
4139 medium = base;
4140 mediumCaller.attach(medium);
4141 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4142 mediumLock.attach(medium);
4143 }
4144 }
4145
4146 Utf8Str strFullSnapshotFolder;
4147 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4148
4149 ComObjPtr<Medium> diff;
4150 diff.createObject();
4151 // store this diff in the same registry as the parent
4152 Guid uuidRegistryParent;
4153 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4154 {
4155 // parent image has no registry: this can happen if we're attaching a new immutable
4156 // image that has not yet been attached (medium then points to the base and we're
4157 // creating the diff image for the immutable, and the parent is not yet registered);
4158 // put the parent in the machine registry then
4159 mediumLock.release();
4160 treeLock.release();
4161 alock.release();
4162 addMediumToRegistry(medium);
4163 alock.acquire();
4164 treeLock.acquire();
4165 mediumLock.acquire();
4166 medium->getFirstRegistryMachineId(uuidRegistryParent);
4167 }
4168 rc = diff->init(mParent,
4169 medium->getPreferredDiffFormat(),
4170 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4171 uuidRegistryParent);
4172 if (FAILED(rc)) return rc;
4173
4174 /* Apply the normal locking logic to the entire chain. */
4175 MediumLockList *pMediumLockList(new MediumLockList());
4176 mediumLock.release();
4177 treeLock.release();
4178 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4179 true /* fMediumLockWrite */,
4180 medium,
4181 *pMediumLockList);
4182 treeLock.acquire();
4183 mediumLock.acquire();
4184 if (SUCCEEDED(rc))
4185 {
4186 mediumLock.release();
4187 treeLock.release();
4188 rc = pMediumLockList->Lock();
4189 treeLock.acquire();
4190 mediumLock.acquire();
4191 if (FAILED(rc))
4192 setError(rc,
4193 tr("Could not lock medium when creating diff '%s'"),
4194 diff->getLocationFull().c_str());
4195 else
4196 {
4197 /* will release the lock before the potentially lengthy
4198 * operation, so protect with the special state */
4199 MachineState_T oldState = mData->mMachineState;
4200 setMachineState(MachineState_SettingUp);
4201
4202 mediumLock.release();
4203 treeLock.release();
4204 alock.release();
4205
4206 rc = medium->createDiffStorage(diff,
4207 MediumVariant_Standard,
4208 pMediumLockList,
4209 NULL /* aProgress */,
4210 true /* aWait */);
4211
4212 alock.acquire();
4213 treeLock.acquire();
4214 mediumLock.acquire();
4215
4216 setMachineState(oldState);
4217 }
4218 }
4219
4220 /* Unlock the media and free the associated memory. */
4221 delete pMediumLockList;
4222
4223 if (FAILED(rc)) return rc;
4224
4225 /* use the created diff for the actual attachment */
4226 medium = diff;
4227 mediumCaller.attach(medium);
4228 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4229 mediumLock.attach(medium);
4230 }
4231 while (0);
4232
4233 ComObjPtr<MediumAttachment> attachment;
4234 attachment.createObject();
4235 rc = attachment->init(this,
4236 medium,
4237 aControllerName,
4238 aControllerPort,
4239 aDevice,
4240 aType,
4241 fIndirect,
4242 false /* fPassthrough */,
4243 false /* fTempEject */,
4244 false /* fNonRotational */,
4245 false /* fDiscard */,
4246 Utf8Str::Empty);
4247 if (FAILED(rc)) return rc;
4248
4249 if (associate && !medium.isNull())
4250 {
4251 // as the last step, associate the medium to the VM
4252 rc = medium->addBackReference(mData->mUuid);
4253 // here we can fail because of Deleting, or being in process of creating a Diff
4254 if (FAILED(rc)) return rc;
4255
4256 mediumLock.release();
4257 treeLock.release();
4258 alock.release();
4259 addMediumToRegistry(medium);
4260 alock.acquire();
4261 treeLock.acquire();
4262 mediumLock.acquire();
4263 }
4264
4265 /* success: finally remember the attachment */
4266 setModified(IsModified_Storage);
4267 mMediaData.backup();
4268 mMediaData->mAttachments.push_back(attachment);
4269
4270 mediumLock.release();
4271 treeLock.release();
4272 alock.release();
4273
4274 if (fHotplug || fSilent)
4275 {
4276 MediumLockList *pMediumLockList(new MediumLockList());
4277
4278 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4279 true /* fMediumLockWrite */,
4280 NULL,
4281 *pMediumLockList);
4282 alock.acquire();
4283 if (FAILED(rc))
4284 delete pMediumLockList;
4285 else
4286 {
4287 mData->mSession.mLockedMedia.Unlock();
4288 alock.release();
4289 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4290 mData->mSession.mLockedMedia.Lock();
4291 alock.acquire();
4292 }
4293 alock.release();
4294
4295 if (SUCCEEDED(rc))
4296 {
4297 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4298 /* Remove lock list in case of error. */
4299 if (FAILED(rc))
4300 {
4301 mData->mSession.mLockedMedia.Unlock();
4302 mData->mSession.mLockedMedia.Remove(attachment);
4303 mData->mSession.mLockedMedia.Lock();
4304 }
4305 }
4306 }
4307
4308 mParent->saveModifiedRegistries();
4309
4310 return rc;
4311}
4312
4313STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4314 LONG aDevice)
4315{
4316 CheckComArgStrNotEmptyOrNull(aControllerName);
4317
4318 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4319 aControllerName, aControllerPort, aDevice));
4320
4321 AutoCaller autoCaller(this);
4322 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4323
4324 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4325
4326 HRESULT rc = checkStateDependency(MutableStateDep);
4327 if (FAILED(rc)) return rc;
4328
4329 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4330
4331 /* Check for an existing controller. */
4332 ComObjPtr<StorageController> ctl;
4333 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4334 if (FAILED(rc)) return rc;
4335
4336 StorageControllerType_T ctrlType;
4337 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4338 if (FAILED(rc))
4339 return setError(E_FAIL,
4340 tr("Could not get type of controller '%ls'"),
4341 aControllerName);
4342
4343 bool fSilent = false;
4344 Utf8Str strReconfig;
4345
4346 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4347 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4348 if (FAILED(rc))
4349 return rc;
4350 if ( mData->mMachineState == MachineState_Paused
4351 && strReconfig == "1")
4352 fSilent = true;
4353
4354 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4355 bool fHotplug = false;
4356 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4357 fHotplug = true;
4358
4359 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4360 return setError(VBOX_E_INVALID_VM_STATE,
4361 tr("Controller '%ls' does not support hotplugging"),
4362 aControllerName);
4363
4364 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4365 aControllerName,
4366 aControllerPort,
4367 aDevice);
4368 if (!pAttach)
4369 return setError(VBOX_E_OBJECT_NOT_FOUND,
4370 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4371 aDevice, aControllerPort, aControllerName);
4372
4373 /*
4374 * The VM has to detach the device before we delete any implicit diffs.
4375 * If this fails we can roll back without loosing data.
4376 */
4377 if (fHotplug || fSilent)
4378 {
4379 alock.release();
4380 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4381 alock.acquire();
4382 }
4383 if (FAILED(rc)) return rc;
4384
4385 /* If we are here everything went well and we can delete the implicit now. */
4386 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4387
4388 alock.release();
4389
4390 mParent->saveModifiedRegistries();
4391
4392 return rc;
4393}
4394
4395STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4396 LONG aDevice, BOOL aPassthrough)
4397{
4398 CheckComArgStrNotEmptyOrNull(aControllerName);
4399
4400 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4401 aControllerName, aControllerPort, aDevice, aPassthrough));
4402
4403 AutoCaller autoCaller(this);
4404 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4405
4406 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4407
4408 HRESULT rc = checkStateDependency(MutableStateDep);
4409 if (FAILED(rc)) return rc;
4410
4411 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4412
4413 if (Global::IsOnlineOrTransient(mData->mMachineState))
4414 return setError(VBOX_E_INVALID_VM_STATE,
4415 tr("Invalid machine state: %s"),
4416 Global::stringifyMachineState(mData->mMachineState));
4417
4418 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4419 aControllerName,
4420 aControllerPort,
4421 aDevice);
4422 if (!pAttach)
4423 return setError(VBOX_E_OBJECT_NOT_FOUND,
4424 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4425 aDevice, aControllerPort, aControllerName);
4426
4427
4428 setModified(IsModified_Storage);
4429 mMediaData.backup();
4430
4431 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4432
4433 if (pAttach->getType() != DeviceType_DVD)
4434 return setError(E_INVALIDARG,
4435 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4436 aDevice, aControllerPort, aControllerName);
4437 pAttach->updatePassthrough(!!aPassthrough);
4438
4439 return S_OK;
4440}
4441
4442STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4443 LONG aDevice, BOOL aTemporaryEject)
4444{
4445 CheckComArgStrNotEmptyOrNull(aControllerName);
4446
4447 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4448 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4449
4450 AutoCaller autoCaller(this);
4451 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4452
4453 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4454
4455 HRESULT rc = checkStateDependency(MutableStateDep);
4456 if (FAILED(rc)) return rc;
4457
4458 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4459 aControllerName,
4460 aControllerPort,
4461 aDevice);
4462 if (!pAttach)
4463 return setError(VBOX_E_OBJECT_NOT_FOUND,
4464 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4465 aDevice, aControllerPort, aControllerName);
4466
4467
4468 setModified(IsModified_Storage);
4469 mMediaData.backup();
4470
4471 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4472
4473 if (pAttach->getType() != DeviceType_DVD)
4474 return setError(E_INVALIDARG,
4475 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4476 aDevice, aControllerPort, aControllerName);
4477 pAttach->updateTempEject(!!aTemporaryEject);
4478
4479 return S_OK;
4480}
4481
4482STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4483 LONG aDevice, BOOL aNonRotational)
4484{
4485 CheckComArgStrNotEmptyOrNull(aControllerName);
4486
4487 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4488 aControllerName, aControllerPort, aDevice, aNonRotational));
4489
4490 AutoCaller autoCaller(this);
4491 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4492
4493 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4494
4495 HRESULT rc = checkStateDependency(MutableStateDep);
4496 if (FAILED(rc)) return rc;
4497
4498 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4499
4500 if (Global::IsOnlineOrTransient(mData->mMachineState))
4501 return setError(VBOX_E_INVALID_VM_STATE,
4502 tr("Invalid machine state: %s"),
4503 Global::stringifyMachineState(mData->mMachineState));
4504
4505 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4506 aControllerName,
4507 aControllerPort,
4508 aDevice);
4509 if (!pAttach)
4510 return setError(VBOX_E_OBJECT_NOT_FOUND,
4511 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4512 aDevice, aControllerPort, aControllerName);
4513
4514
4515 setModified(IsModified_Storage);
4516 mMediaData.backup();
4517
4518 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4519
4520 if (pAttach->getType() != DeviceType_HardDisk)
4521 return setError(E_INVALIDARG,
4522 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"),
4523 aDevice, aControllerPort, aControllerName);
4524 pAttach->updateNonRotational(!!aNonRotational);
4525
4526 return S_OK;
4527}
4528
4529STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4530 LONG aDevice, BOOL aDiscard)
4531{
4532 CheckComArgStrNotEmptyOrNull(aControllerName);
4533
4534 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4535 aControllerName, aControllerPort, aDevice, aDiscard));
4536
4537 AutoCaller autoCaller(this);
4538 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4539
4540 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4541
4542 HRESULT rc = checkStateDependency(MutableStateDep);
4543 if (FAILED(rc)) return rc;
4544
4545 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4546
4547 if (Global::IsOnlineOrTransient(mData->mMachineState))
4548 return setError(VBOX_E_INVALID_VM_STATE,
4549 tr("Invalid machine state: %s"),
4550 Global::stringifyMachineState(mData->mMachineState));
4551
4552 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4553 aControllerName,
4554 aControllerPort,
4555 aDevice);
4556 if (!pAttach)
4557 return setError(VBOX_E_OBJECT_NOT_FOUND,
4558 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4559 aDevice, aControllerPort, aControllerName);
4560
4561
4562 setModified(IsModified_Storage);
4563 mMediaData.backup();
4564
4565 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4566
4567 if (pAttach->getType() != DeviceType_HardDisk)
4568 return setError(E_INVALIDARG,
4569 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"),
4570 aDevice, aControllerPort, aControllerName);
4571 pAttach->updateDiscard(!!aDiscard);
4572
4573 return S_OK;
4574}
4575
4576STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4577 LONG aDevice)
4578{
4579 int rc = S_OK;
4580 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4581 aControllerName, aControllerPort, aDevice));
4582
4583 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4584
4585 return rc;
4586}
4587
4588STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4589 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4590{
4591 CheckComArgStrNotEmptyOrNull(aControllerName);
4592
4593 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4594 aControllerName, aControllerPort, aDevice));
4595
4596 AutoCaller autoCaller(this);
4597 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4598
4599 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4600
4601 HRESULT rc = checkStateDependency(MutableStateDep);
4602 if (FAILED(rc)) return rc;
4603
4604 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4605
4606 if (Global::IsOnlineOrTransient(mData->mMachineState))
4607 return setError(VBOX_E_INVALID_VM_STATE,
4608 tr("Invalid machine state: %s"),
4609 Global::stringifyMachineState(mData->mMachineState));
4610
4611 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4612 aControllerName,
4613 aControllerPort,
4614 aDevice);
4615 if (!pAttach)
4616 return setError(VBOX_E_OBJECT_NOT_FOUND,
4617 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4618 aDevice, aControllerPort, aControllerName);
4619
4620
4621 setModified(IsModified_Storage);
4622 mMediaData.backup();
4623
4624 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4625 if (aBandwidthGroup && group.isNull())
4626 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4627
4628 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4629
4630 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4631 if (strBandwidthGroupOld.isNotEmpty())
4632 {
4633 /* Get the bandwidth group object and release it - this must not fail. */
4634 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4635 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4636 Assert(SUCCEEDED(rc));
4637
4638 pBandwidthGroupOld->release();
4639 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4640 }
4641
4642 if (!group.isNull())
4643 {
4644 group->reference();
4645 pAttach->updateBandwidthGroup(group->getName());
4646 }
4647
4648 return S_OK;
4649}
4650
4651STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4652 LONG aControllerPort,
4653 LONG aDevice,
4654 DeviceType_T aType)
4655{
4656 HRESULT rc = S_OK;
4657
4658 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4659 aControllerName, aControllerPort, aDevice, aType));
4660
4661 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4662
4663 return rc;
4664}
4665
4666
4667
4668STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4669 LONG aControllerPort,
4670 LONG aDevice,
4671 BOOL aForce)
4672{
4673 int rc = S_OK;
4674 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4675 aControllerName, aControllerPort, aForce));
4676
4677 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4678
4679 return rc;
4680}
4681
4682STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4683 LONG aControllerPort,
4684 LONG aDevice,
4685 IMedium *aMedium,
4686 BOOL aForce)
4687{
4688 int rc = S_OK;
4689 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4690 aControllerName, aControllerPort, aDevice, aForce));
4691
4692 CheckComArgStrNotEmptyOrNull(aControllerName);
4693
4694 AutoCaller autoCaller(this);
4695 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4696
4697 // request the host lock first, since might be calling Host methods for getting host drives;
4698 // next, protect the media tree all the while we're in here, as well as our member variables
4699 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4700 this->lockHandle(),
4701 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4702
4703 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4704 aControllerName,
4705 aControllerPort,
4706 aDevice);
4707 if (pAttach.isNull())
4708 return setError(VBOX_E_OBJECT_NOT_FOUND,
4709 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4710 aDevice, aControllerPort, aControllerName);
4711
4712 /* Remember previously mounted medium. The medium before taking the
4713 * backup is not necessarily the same thing. */
4714 ComObjPtr<Medium> oldmedium;
4715 oldmedium = pAttach->getMedium();
4716
4717 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4718 if (aMedium && pMedium.isNull())
4719 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4720
4721 AutoCaller mediumCaller(pMedium);
4722 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4723
4724 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4725 if (pMedium)
4726 {
4727 DeviceType_T mediumType = pAttach->getType();
4728 switch (mediumType)
4729 {
4730 case DeviceType_DVD:
4731 case DeviceType_Floppy:
4732 break;
4733
4734 default:
4735 return setError(VBOX_E_INVALID_OBJECT_STATE,
4736 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
4737 aControllerPort,
4738 aDevice,
4739 aControllerName);
4740 }
4741 }
4742
4743 setModified(IsModified_Storage);
4744 mMediaData.backup();
4745
4746 {
4747 // The backup operation makes the pAttach reference point to the
4748 // old settings. Re-get the correct reference.
4749 pAttach = findAttachment(mMediaData->mAttachments,
4750 aControllerName,
4751 aControllerPort,
4752 aDevice);
4753 if (!oldmedium.isNull())
4754 oldmedium->removeBackReference(mData->mUuid);
4755 if (!pMedium.isNull())
4756 {
4757 pMedium->addBackReference(mData->mUuid);
4758
4759 mediumLock.release();
4760 multiLock.release();
4761 addMediumToRegistry(pMedium);
4762 multiLock.acquire();
4763 mediumLock.acquire();
4764 }
4765
4766 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4767 pAttach->updateMedium(pMedium);
4768 }
4769
4770 setModified(IsModified_Storage);
4771
4772 mediumLock.release();
4773 multiLock.release();
4774 rc = onMediumChange(pAttach, aForce);
4775 multiLock.acquire();
4776 mediumLock.acquire();
4777
4778 /* On error roll back this change only. */
4779 if (FAILED(rc))
4780 {
4781 if (!pMedium.isNull())
4782 pMedium->removeBackReference(mData->mUuid);
4783 pAttach = findAttachment(mMediaData->mAttachments,
4784 aControllerName,
4785 aControllerPort,
4786 aDevice);
4787 /* If the attachment is gone in the meantime, bail out. */
4788 if (pAttach.isNull())
4789 return rc;
4790 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4791 if (!oldmedium.isNull())
4792 oldmedium->addBackReference(mData->mUuid);
4793 pAttach->updateMedium(oldmedium);
4794 }
4795
4796 mediumLock.release();
4797 multiLock.release();
4798
4799 mParent->saveModifiedRegistries();
4800
4801 return rc;
4802}
4803
4804STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
4805 LONG aControllerPort,
4806 LONG aDevice,
4807 IMedium **aMedium)
4808{
4809 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4810 aControllerName, aControllerPort, aDevice));
4811
4812 CheckComArgStrNotEmptyOrNull(aControllerName);
4813 CheckComArgOutPointerValid(aMedium);
4814
4815 AutoCaller autoCaller(this);
4816 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4817
4818 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4819
4820 *aMedium = NULL;
4821
4822 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4823 aControllerName,
4824 aControllerPort,
4825 aDevice);
4826 if (pAttach.isNull())
4827 return setError(VBOX_E_OBJECT_NOT_FOUND,
4828 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4829 aDevice, aControllerPort, aControllerName);
4830
4831 pAttach->getMedium().queryInterfaceTo(aMedium);
4832
4833 return S_OK;
4834}
4835
4836STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
4837{
4838 CheckComArgOutPointerValid(port);
4839 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
4840
4841 AutoCaller autoCaller(this);
4842 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4843
4844 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4845
4846 mSerialPorts[slot].queryInterfaceTo(port);
4847
4848 return S_OK;
4849}
4850
4851STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
4852{
4853 CheckComArgOutPointerValid(port);
4854 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
4855
4856 AutoCaller autoCaller(this);
4857 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4858
4859 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4860
4861 mParallelPorts[slot].queryInterfaceTo(port);
4862
4863 return S_OK;
4864}
4865
4866STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
4867{
4868 CheckComArgOutPointerValid(adapter);
4869 /* Do not assert if slot is out of range, just return the advertised
4870 status. testdriver/vbox.py triggers this in logVmInfo. */
4871 if (slot >= mNetworkAdapters.size())
4872 return setError(E_INVALIDARG,
4873 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4874 slot, mNetworkAdapters.size());
4875
4876 AutoCaller autoCaller(this);
4877 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4878
4879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4880
4881 mNetworkAdapters[slot].queryInterfaceTo(adapter);
4882
4883 return S_OK;
4884}
4885
4886STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
4887{
4888 CheckComArgOutSafeArrayPointerValid(aKeys);
4889
4890 AutoCaller autoCaller(this);
4891 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4892
4893 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4894
4895 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4896 int i = 0;
4897 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4898 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4899 ++it, ++i)
4900 {
4901 const Utf8Str &strKey = it->first;
4902 strKey.cloneTo(&saKeys[i]);
4903 }
4904 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
4905
4906 return S_OK;
4907 }
4908
4909 /**
4910 * @note Locks this object for reading.
4911 */
4912STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
4913 BSTR *aValue)
4914{
4915 CheckComArgStrNotEmptyOrNull(aKey);
4916 CheckComArgOutPointerValid(aValue);
4917
4918 AutoCaller autoCaller(this);
4919 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4920
4921 /* start with nothing found */
4922 Bstr bstrResult("");
4923
4924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4925
4926 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
4927 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4928 // found:
4929 bstrResult = it->second; // source is a Utf8Str
4930
4931 /* return the result to caller (may be empty) */
4932 bstrResult.cloneTo(aValue);
4933
4934 return S_OK;
4935}
4936
4937 /**
4938 * @note Locks mParent for writing + this object for writing.
4939 */
4940STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
4941{
4942 CheckComArgStrNotEmptyOrNull(aKey);
4943
4944 AutoCaller autoCaller(this);
4945 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4946
4947 Utf8Str strKey(aKey);
4948 Utf8Str strValue(aValue);
4949 Utf8Str strOldValue; // empty
4950
4951 // locking note: we only hold the read lock briefly to look up the old value,
4952 // then release it and call the onExtraCanChange callbacks. There is a small
4953 // chance of a race insofar as the callback might be called twice if two callers
4954 // change the same key at the same time, but that's a much better solution
4955 // than the deadlock we had here before. The actual changing of the extradata
4956 // is then performed under the write lock and race-free.
4957
4958 // look up the old value first; if nothing has changed then we need not do anything
4959 {
4960 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4961 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
4962 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4963 strOldValue = it->second;
4964 }
4965
4966 bool fChanged;
4967 if ((fChanged = (strOldValue != strValue)))
4968 {
4969 // ask for permission from all listeners outside the locks;
4970 // onExtraDataCanChange() only briefly requests the VirtualBox
4971 // lock to copy the list of callbacks to invoke
4972 Bstr error;
4973 Bstr bstrValue(aValue);
4974
4975 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
4976 {
4977 const char *sep = error.isEmpty() ? "" : ": ";
4978 CBSTR err = error.raw();
4979 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4980 sep, err));
4981 return setError(E_ACCESSDENIED,
4982 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
4983 aKey,
4984 bstrValue.raw(),
4985 sep,
4986 err);
4987 }
4988
4989 // data is changing and change not vetoed: then write it out under the lock
4990 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4991
4992 if (isSnapshotMachine())
4993 {
4994 HRESULT rc = checkStateDependency(MutableStateDep);
4995 if (FAILED(rc)) return rc;
4996 }
4997
4998 if (strValue.isEmpty())
4999 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
5000 else
5001 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
5002 // creates a new key if needed
5003
5004 bool fNeedsGlobalSaveSettings = false;
5005 saveSettings(&fNeedsGlobalSaveSettings);
5006
5007 if (fNeedsGlobalSaveSettings)
5008 {
5009 // save the global settings; for that we should hold only the VirtualBox lock
5010 alock.release();
5011 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5012 mParent->saveSettings();
5013 }
5014 }
5015
5016 // fire notification outside the lock
5017 if (fChanged)
5018 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
5019
5020 return S_OK;
5021}
5022
5023STDMETHODIMP Machine::SaveSettings()
5024{
5025 AutoCaller autoCaller(this);
5026 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5027
5028 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5029
5030 /* when there was auto-conversion, we want to save the file even if
5031 * the VM is saved */
5032 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
5033 if (FAILED(rc)) return rc;
5034
5035 /* the settings file path may never be null */
5036 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5037
5038 /* save all VM data excluding snapshots */
5039 bool fNeedsGlobalSaveSettings = false;
5040 rc = saveSettings(&fNeedsGlobalSaveSettings);
5041 mlock.release();
5042
5043 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5044 {
5045 // save the global settings; for that we should hold only the VirtualBox lock
5046 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5047 rc = mParent->saveSettings();
5048 }
5049
5050 return rc;
5051}
5052
5053STDMETHODIMP Machine::DiscardSettings()
5054{
5055 AutoCaller autoCaller(this);
5056 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5057
5058 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5059
5060 HRESULT rc = checkStateDependency(MutableStateDep);
5061 if (FAILED(rc)) return rc;
5062
5063 /*
5064 * during this rollback, the session will be notified if data has
5065 * been actually changed
5066 */
5067 rollback(true /* aNotify */);
5068
5069 return S_OK;
5070}
5071
5072/** @note Locks objects! */
5073STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
5074 ComSafeArrayOut(IMedium*, aMedia))
5075{
5076 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5077 AutoLimitedCaller autoCaller(this);
5078 AssertComRCReturnRC(autoCaller.rc());
5079
5080 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5081
5082 Guid id(getId());
5083
5084 if (mData->mSession.mState != SessionState_Unlocked)
5085 return setError(VBOX_E_INVALID_OBJECT_STATE,
5086 tr("Cannot unregister the machine '%s' while it is locked"),
5087 mUserData->s.strName.c_str());
5088
5089 // wait for state dependents to drop to zero
5090 ensureNoStateDependencies();
5091
5092 if (!mData->mAccessible)
5093 {
5094 // inaccessible maschines can only be unregistered; uninitialize ourselves
5095 // here because currently there may be no unregistered that are inaccessible
5096 // (this state combination is not supported). Note releasing the caller and
5097 // leaving the lock before calling uninit()
5098 alock.release();
5099 autoCaller.release();
5100
5101 uninit();
5102
5103 mParent->unregisterMachine(this, id);
5104 // calls VirtualBox::saveSettings()
5105
5106 return S_OK;
5107 }
5108
5109 HRESULT rc = S_OK;
5110
5111 // discard saved state
5112 if (mData->mMachineState == MachineState_Saved)
5113 {
5114 // add the saved state file to the list of files the caller should delete
5115 Assert(!mSSData->strStateFilePath.isEmpty());
5116 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5117
5118 mSSData->strStateFilePath.setNull();
5119
5120 // unconditionally set the machine state to powered off, we now
5121 // know no session has locked the machine
5122 mData->mMachineState = MachineState_PoweredOff;
5123 }
5124
5125 size_t cSnapshots = 0;
5126 if (mData->mFirstSnapshot)
5127 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5128 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
5129 // fail now before we start detaching media
5130 return setError(VBOX_E_INVALID_OBJECT_STATE,
5131 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5132 mUserData->s.strName.c_str(), cSnapshots);
5133
5134 // This list collects the medium objects from all medium attachments
5135 // which we will detach from the machine and its snapshots, in a specific
5136 // order which allows for closing all media without getting "media in use"
5137 // errors, simply by going through the list from the front to the back:
5138 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5139 // and must be closed before the parent media from the snapshots, or closing the parents
5140 // will fail because they still have children);
5141 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5142 // the root ("first") snapshot of the machine.
5143 MediaList llMedia;
5144
5145 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5146 && mMediaData->mAttachments.size()
5147 )
5148 {
5149 // we have media attachments: detach them all and add the Medium objects to our list
5150 if (cleanupMode != CleanupMode_UnregisterOnly)
5151 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5152 else
5153 return setError(VBOX_E_INVALID_OBJECT_STATE,
5154 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5155 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5156 }
5157
5158 if (cSnapshots)
5159 {
5160 // autoCleanup must be true here, or we would have failed above
5161
5162 // add the media from the medium attachments of the snapshots to llMedia
5163 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5164 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5165 // into the children first
5166
5167 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5168 MachineState_T oldState = mData->mMachineState;
5169 mData->mMachineState = MachineState_DeletingSnapshot;
5170
5171 // make a copy of the first snapshot so the refcount does not drop to 0
5172 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5173 // because of the AutoCaller voodoo)
5174 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5175
5176 // GO!
5177 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5178
5179 mData->mMachineState = oldState;
5180 }
5181
5182 if (FAILED(rc))
5183 {
5184 rollbackMedia();
5185 return rc;
5186 }
5187
5188 // commit all the media changes made above
5189 commitMedia();
5190
5191 mData->mRegistered = false;
5192
5193 // machine lock no longer needed
5194 alock.release();
5195
5196 // return media to caller
5197 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5198 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5199
5200 mParent->unregisterMachine(this, id);
5201 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5202
5203 return S_OK;
5204}
5205
5206struct Machine::DeleteTask
5207{
5208 ComObjPtr<Machine> pMachine;
5209 RTCList<ComPtr<IMedium> > llMediums;
5210 StringsList llFilesToDelete;
5211 ComObjPtr<Progress> pProgress;
5212};
5213
5214STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5215{
5216 LogFlowFuncEnter();
5217
5218 AutoCaller autoCaller(this);
5219 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5220
5221 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5222
5223 HRESULT rc = checkStateDependency(MutableStateDep);
5224 if (FAILED(rc)) return rc;
5225
5226 if (mData->mRegistered)
5227 return setError(VBOX_E_INVALID_VM_STATE,
5228 tr("Cannot delete settings of a registered machine"));
5229
5230 DeleteTask *pTask = new DeleteTask;
5231 pTask->pMachine = this;
5232 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5233
5234 // collect files to delete
5235 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5236
5237 for (size_t i = 0; i < sfaMedia.size(); ++i)
5238 {
5239 IMedium *pIMedium(sfaMedia[i]);
5240 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5241 if (pMedium.isNull())
5242 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5243 SafeArray<BSTR> ids;
5244 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5245 if (FAILED(rc)) return rc;
5246 /* At this point the medium should not have any back references
5247 * anymore. If it has it is attached to another VM and *must* not
5248 * deleted. */
5249 if (ids.size() < 1)
5250 pTask->llMediums.append(pMedium);
5251 }
5252 if (mData->pMachineConfigFile->fileExists())
5253 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5254
5255 pTask->pProgress.createObject();
5256 pTask->pProgress->init(getVirtualBox(),
5257 static_cast<IMachine*>(this) /* aInitiator */,
5258 Bstr(tr("Deleting files")).raw(),
5259 true /* fCancellable */,
5260 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5261 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5262
5263 int vrc = RTThreadCreate(NULL,
5264 Machine::deleteThread,
5265 (void*)pTask,
5266 0,
5267 RTTHREADTYPE_MAIN_WORKER,
5268 0,
5269 "MachineDelete");
5270
5271 pTask->pProgress.queryInterfaceTo(aProgress);
5272
5273 if (RT_FAILURE(vrc))
5274 {
5275 delete pTask;
5276 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5277 }
5278
5279 LogFlowFuncLeave();
5280
5281 return S_OK;
5282}
5283
5284/**
5285 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5286 * calls Machine::deleteTaskWorker() on the actual machine object.
5287 * @param Thread
5288 * @param pvUser
5289 * @return
5290 */
5291/*static*/
5292DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5293{
5294 LogFlowFuncEnter();
5295
5296 DeleteTask *pTask = (DeleteTask*)pvUser;
5297 Assert(pTask);
5298 Assert(pTask->pMachine);
5299 Assert(pTask->pProgress);
5300
5301 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5302 pTask->pProgress->notifyComplete(rc);
5303
5304 delete pTask;
5305
5306 LogFlowFuncLeave();
5307
5308 NOREF(Thread);
5309
5310 return VINF_SUCCESS;
5311}
5312
5313/**
5314 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5315 * @param task
5316 * @return
5317 */
5318HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5319{
5320 AutoCaller autoCaller(this);
5321 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5322
5323 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5324
5325 HRESULT rc = S_OK;
5326
5327 try
5328 {
5329 ULONG uLogHistoryCount = 3;
5330 ComPtr<ISystemProperties> systemProperties;
5331 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5332 if (FAILED(rc)) throw rc;
5333
5334 if (!systemProperties.isNull())
5335 {
5336 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5337 if (FAILED(rc)) throw rc;
5338 }
5339
5340 MachineState_T oldState = mData->mMachineState;
5341 setMachineState(MachineState_SettingUp);
5342 alock.release();
5343 for (size_t i = 0; i < task.llMediums.size(); ++i)
5344 {
5345 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5346 {
5347 AutoCaller mac(pMedium);
5348 if (FAILED(mac.rc())) throw mac.rc();
5349 Utf8Str strLocation = pMedium->getLocationFull();
5350 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5351 if (FAILED(rc)) throw rc;
5352 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5353 }
5354 ComPtr<IProgress> pProgress2;
5355 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5356 if (FAILED(rc)) throw rc;
5357 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5358 if (FAILED(rc)) throw rc;
5359 /* Check the result of the asynchrony process. */
5360 LONG iRc;
5361 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5362 if (FAILED(rc)) throw rc;
5363 /* If the thread of the progress object has an error, then
5364 * retrieve the error info from there, or it'll be lost. */
5365 if (FAILED(iRc))
5366 throw setError(ProgressErrorInfo(pProgress2));
5367 }
5368 setMachineState(oldState);
5369 alock.acquire();
5370
5371 // delete the files pushed on the task list by Machine::Delete()
5372 // (this includes saved states of the machine and snapshots and
5373 // medium storage files from the IMedium list passed in, and the
5374 // machine XML file)
5375 StringsList::const_iterator it = task.llFilesToDelete.begin();
5376 while (it != task.llFilesToDelete.end())
5377 {
5378 const Utf8Str &strFile = *it;
5379 LogFunc(("Deleting file %s\n", strFile.c_str()));
5380 int vrc = RTFileDelete(strFile.c_str());
5381 if (RT_FAILURE(vrc))
5382 throw setError(VBOX_E_IPRT_ERROR,
5383 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5384
5385 ++it;
5386 if (it == task.llFilesToDelete.end())
5387 {
5388 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5389 if (FAILED(rc)) throw rc;
5390 break;
5391 }
5392
5393 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5394 if (FAILED(rc)) throw rc;
5395 }
5396
5397 /* delete the settings only when the file actually exists */
5398 if (mData->pMachineConfigFile->fileExists())
5399 {
5400 /* Delete any backup or uncommitted XML files. Ignore failures.
5401 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5402 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5403 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5404 RTFileDelete(otherXml.c_str());
5405 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5406 RTFileDelete(otherXml.c_str());
5407
5408 /* delete the Logs folder, nothing important should be left
5409 * there (we don't check for errors because the user might have
5410 * some private files there that we don't want to delete) */
5411 Utf8Str logFolder;
5412 getLogFolder(logFolder);
5413 Assert(logFolder.length());
5414 if (RTDirExists(logFolder.c_str()))
5415 {
5416 /* Delete all VBox.log[.N] files from the Logs folder
5417 * (this must be in sync with the rotation logic in
5418 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5419 * files that may have been created by the GUI. */
5420 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5421 logFolder.c_str(), RTPATH_DELIMITER);
5422 RTFileDelete(log.c_str());
5423 log = Utf8StrFmt("%s%cVBox.png",
5424 logFolder.c_str(), RTPATH_DELIMITER);
5425 RTFileDelete(log.c_str());
5426 for (int i = uLogHistoryCount; i > 0; i--)
5427 {
5428 log = Utf8StrFmt("%s%cVBox.log.%d",
5429 logFolder.c_str(), RTPATH_DELIMITER, i);
5430 RTFileDelete(log.c_str());
5431 log = Utf8StrFmt("%s%cVBox.png.%d",
5432 logFolder.c_str(), RTPATH_DELIMITER, i);
5433 RTFileDelete(log.c_str());
5434 }
5435
5436 RTDirRemove(logFolder.c_str());
5437 }
5438
5439 /* delete the Snapshots folder, nothing important should be left
5440 * there (we don't check for errors because the user might have
5441 * some private files there that we don't want to delete) */
5442 Utf8Str strFullSnapshotFolder;
5443 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5444 Assert(!strFullSnapshotFolder.isEmpty());
5445 if (RTDirExists(strFullSnapshotFolder.c_str()))
5446 RTDirRemove(strFullSnapshotFolder.c_str());
5447
5448 // delete the directory that contains the settings file, but only
5449 // if it matches the VM name
5450 Utf8Str settingsDir;
5451 if (isInOwnDir(&settingsDir))
5452 RTDirRemove(settingsDir.c_str());
5453 }
5454
5455 alock.release();
5456
5457 mParent->saveModifiedRegistries();
5458 }
5459 catch (HRESULT aRC) { rc = aRC; }
5460
5461 return rc;
5462}
5463
5464STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5465{
5466 CheckComArgOutPointerValid(aSnapshot);
5467
5468 AutoCaller autoCaller(this);
5469 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5470
5471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5472
5473 ComObjPtr<Snapshot> pSnapshot;
5474 HRESULT rc;
5475
5476 if (!aNameOrId || !*aNameOrId)
5477 // null case (caller wants root snapshot): findSnapshotById() handles this
5478 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5479 else
5480 {
5481 Guid uuid(aNameOrId);
5482 if (uuid.isValid())
5483 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5484 else
5485 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5486 }
5487 pSnapshot.queryInterfaceTo(aSnapshot);
5488
5489 return rc;
5490}
5491
5492STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5493{
5494 CheckComArgStrNotEmptyOrNull(aName);
5495 CheckComArgStrNotEmptyOrNull(aHostPath);
5496
5497 AutoCaller autoCaller(this);
5498 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5499
5500 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5501
5502 HRESULT rc = checkStateDependency(MutableStateDep);
5503 if (FAILED(rc)) return rc;
5504
5505 Utf8Str strName(aName);
5506
5507 ComObjPtr<SharedFolder> sharedFolder;
5508 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5509 if (SUCCEEDED(rc))
5510 return setError(VBOX_E_OBJECT_IN_USE,
5511 tr("Shared folder named '%s' already exists"),
5512 strName.c_str());
5513
5514 sharedFolder.createObject();
5515 rc = sharedFolder->init(getMachine(),
5516 strName,
5517 aHostPath,
5518 !!aWritable,
5519 !!aAutoMount,
5520 true /* fFailOnError */);
5521 if (FAILED(rc)) return rc;
5522
5523 setModified(IsModified_SharedFolders);
5524 mHWData.backup();
5525 mHWData->mSharedFolders.push_back(sharedFolder);
5526
5527 /* inform the direct session if any */
5528 alock.release();
5529 onSharedFolderChange();
5530
5531 return S_OK;
5532}
5533
5534STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5535{
5536 CheckComArgStrNotEmptyOrNull(aName);
5537
5538 AutoCaller autoCaller(this);
5539 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5540
5541 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5542
5543 HRESULT rc = checkStateDependency(MutableStateDep);
5544 if (FAILED(rc)) return rc;
5545
5546 ComObjPtr<SharedFolder> sharedFolder;
5547 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5548 if (FAILED(rc)) return rc;
5549
5550 setModified(IsModified_SharedFolders);
5551 mHWData.backup();
5552 mHWData->mSharedFolders.remove(sharedFolder);
5553
5554 /* inform the direct session if any */
5555 alock.release();
5556 onSharedFolderChange();
5557
5558 return S_OK;
5559}
5560
5561STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5562{
5563 CheckComArgOutPointerValid(aCanShow);
5564
5565 /* start with No */
5566 *aCanShow = FALSE;
5567
5568 AutoCaller autoCaller(this);
5569 AssertComRCReturnRC(autoCaller.rc());
5570
5571 ComPtr<IInternalSessionControl> directControl;
5572 {
5573 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5574
5575 if (mData->mSession.mState != SessionState_Locked)
5576 return setError(VBOX_E_INVALID_VM_STATE,
5577 tr("Machine is not locked for session (session state: %s)"),
5578 Global::stringifySessionState(mData->mSession.mState));
5579
5580 directControl = mData->mSession.mDirectControl;
5581 }
5582
5583 /* ignore calls made after #OnSessionEnd() is called */
5584 if (!directControl)
5585 return S_OK;
5586
5587 LONG64 dummy;
5588 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5589}
5590
5591STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5592{
5593 CheckComArgOutPointerValid(aWinId);
5594
5595 AutoCaller autoCaller(this);
5596 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5597
5598 ComPtr<IInternalSessionControl> directControl;
5599 {
5600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5601
5602 if (mData->mSession.mState != SessionState_Locked)
5603 return setError(E_FAIL,
5604 tr("Machine is not locked for session (session state: %s)"),
5605 Global::stringifySessionState(mData->mSession.mState));
5606
5607 directControl = mData->mSession.mDirectControl;
5608 }
5609
5610 /* ignore calls made after #OnSessionEnd() is called */
5611 if (!directControl)
5612 return S_OK;
5613
5614 BOOL dummy;
5615 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5616}
5617
5618#ifdef VBOX_WITH_GUEST_PROPS
5619/**
5620 * Look up a guest property in VBoxSVC's internal structures.
5621 */
5622HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5623 BSTR *aValue,
5624 LONG64 *aTimestamp,
5625 BSTR *aFlags) const
5626{
5627 using namespace guestProp;
5628
5629 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5630 Utf8Str strName(aName);
5631 HWData::GuestPropertyMap::const_iterator it =
5632 mHWData->mGuestProperties.find(strName);
5633
5634 if (it != mHWData->mGuestProperties.end())
5635 {
5636 char szFlags[MAX_FLAGS_LEN + 1];
5637 it->second.strValue.cloneTo(aValue);
5638 *aTimestamp = it->second.mTimestamp;
5639 writeFlags(it->second.mFlags, szFlags);
5640 Bstr(szFlags).cloneTo(aFlags);
5641 }
5642
5643 return S_OK;
5644}
5645
5646/**
5647 * Query the VM that a guest property belongs to for the property.
5648 * @returns E_ACCESSDENIED if the VM process is not available or not
5649 * currently handling queries and the lookup should then be done in
5650 * VBoxSVC.
5651 */
5652HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5653 BSTR *aValue,
5654 LONG64 *aTimestamp,
5655 BSTR *aFlags) const
5656{
5657 HRESULT rc;
5658 ComPtr<IInternalSessionControl> directControl;
5659 directControl = mData->mSession.mDirectControl;
5660
5661 /* fail if we were called after #OnSessionEnd() is called. This is a
5662 * silly race condition. */
5663
5664 if (!directControl)
5665 rc = E_ACCESSDENIED;
5666 else
5667 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5668 false /* isSetter */,
5669 aValue, aTimestamp, aFlags);
5670 return rc;
5671}
5672#endif // VBOX_WITH_GUEST_PROPS
5673
5674STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5675 BSTR *aValue,
5676 LONG64 *aTimestamp,
5677 BSTR *aFlags)
5678{
5679#ifndef VBOX_WITH_GUEST_PROPS
5680 ReturnComNotImplemented();
5681#else // VBOX_WITH_GUEST_PROPS
5682 CheckComArgStrNotEmptyOrNull(aName);
5683 CheckComArgOutPointerValid(aValue);
5684 CheckComArgOutPointerValid(aTimestamp);
5685 CheckComArgOutPointerValid(aFlags);
5686
5687 AutoCaller autoCaller(this);
5688 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5689
5690 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5691 if (rc == E_ACCESSDENIED)
5692 /* The VM is not running or the service is not (yet) accessible */
5693 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5694 return rc;
5695#endif // VBOX_WITH_GUEST_PROPS
5696}
5697
5698STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5699{
5700 LONG64 dummyTimestamp;
5701 Bstr dummyFlags;
5702 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5703}
5704
5705STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5706{
5707 Bstr dummyValue;
5708 Bstr dummyFlags;
5709 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5710}
5711
5712#ifdef VBOX_WITH_GUEST_PROPS
5713/**
5714 * Set a guest property in VBoxSVC's internal structures.
5715 */
5716HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5717 IN_BSTR aFlags)
5718{
5719 using namespace guestProp;
5720
5721 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5722 HRESULT rc = S_OK;
5723 HWData::GuestProperty property;
5724 property.mFlags = NILFLAG;
5725
5726 rc = checkStateDependency(MutableStateDep);
5727 if (FAILED(rc))
5728 {
5729 LogRel(("Machine::setGuestPropertyToService: %ls -> %Rhrc\n", aName, rc)); /* !REMOVE ME! Debugging testboxes! */
5730 return rc;
5731 }
5732
5733 try
5734 {
5735 Utf8Str utf8Name(aName);
5736 Utf8Str utf8Flags(aFlags);
5737 uint32_t fFlags = NILFLAG;
5738 if ( (aFlags != NULL)
5739 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags))
5740 )
5741 return setError(E_INVALIDARG,
5742 tr("Invalid guest property flag values: '%ls'"),
5743 aFlags);
5744
5745 HWData::GuestPropertyMap::iterator it =
5746 mHWData->mGuestProperties.find(utf8Name);
5747
5748 if (it == mHWData->mGuestProperties.end())
5749 {
5750 setModified(IsModified_MachineData);
5751 mHWData.backupEx();
5752
5753 RTTIMESPEC time;
5754 HWData::GuestProperty prop;
5755 prop.strValue = aValue;
5756 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5757 prop.mFlags = fFlags;
5758
5759 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
5760 }
5761 else
5762 {
5763 if (it->second.mFlags & (RDONLYHOST))
5764 {
5765 rc = setError(E_ACCESSDENIED,
5766 tr("The property '%ls' cannot be changed by the host"),
5767 aName);
5768 }
5769 else
5770 {
5771 setModified(IsModified_MachineData);
5772 mHWData.backupEx();
5773
5774 /* The backupEx() operation invalidates our iterator,
5775 * so get a new one. */
5776 it = mHWData->mGuestProperties.find(utf8Name);
5777 Assert(it != mHWData->mGuestProperties.end());
5778
5779 if (RT_VALID_PTR(aValue) && *(aValue) != '\0')
5780 {
5781 RTTIMESPEC time;
5782 it->second.strValue = aValue;
5783 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5784 if (aFlags != NULL)
5785 it->second.mFlags = fFlags;
5786 }
5787 else
5788 {
5789 mHWData->mGuestProperties.erase(it);
5790 }
5791 }
5792 }
5793
5794 if ( SUCCEEDED(rc)
5795 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5796 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5797 RTSTR_MAX,
5798 utf8Name.c_str(),
5799 RTSTR_MAX,
5800 NULL)
5801 )
5802 )
5803 {
5804 alock.release();
5805
5806 mParent->onGuestPropertyChange(mData->mUuid, aName,
5807 aValue ? aValue : Bstr("").raw(),
5808 aFlags ? aFlags : Bstr("").raw());
5809 }
5810 }
5811 catch (std::bad_alloc &)
5812 {
5813 rc = E_OUTOFMEMORY;
5814 }
5815
5816 return rc;
5817}
5818
5819/**
5820 * Set a property on the VM that that property belongs to.
5821 * @returns E_ACCESSDENIED if the VM process is not available or not
5822 * currently handling queries and the setting should then be done in
5823 * VBoxSVC.
5824 */
5825HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
5826 IN_BSTR aFlags)
5827{
5828 HRESULT rc;
5829
5830 try
5831 {
5832 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5833
5834 BSTR dummy = NULL; /* will not be changed (setter) */
5835 LONG64 dummy64;
5836 if (!directControl)
5837 rc = E_ACCESSDENIED;
5838 else
5839 {
5840 /** @todo Fix when adding DeleteGuestProperty(),
5841 see defect. */
5842 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
5843 true /* isSetter */,
5844 &dummy, &dummy64, &dummy);
5845 if (FAILED(rc))
5846 {
5847 LogRel(("Machine::setGuestPropertyToVM: %ls -> %Rhrc\n", aName, rc)); /* !REMOVE ME! Debugging testboxes! */
5848 /* testbox hacking: a shot in the dark. */
5849 ErrorInfoKeeper eik;
5850 return rc;
5851 }
5852 }
5853 }
5854 catch (std::bad_alloc &)
5855 {
5856 rc = E_OUTOFMEMORY;
5857 }
5858
5859 return rc;
5860}
5861#endif // VBOX_WITH_GUEST_PROPS
5862
5863STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
5864 IN_BSTR aFlags)
5865{
5866#ifndef VBOX_WITH_GUEST_PROPS
5867 ReturnComNotImplemented();
5868#else // VBOX_WITH_GUEST_PROPS
5869 CheckComArgStrNotEmptyOrNull1(aName, 0x80face08);
5870 CheckComArgMaybeNull1(aFlags, 0x80face03);
5871 CheckComArgMaybeNull1(aValue, 0x80face04);
5872
5873 AutoCaller autoCaller(this);
5874 if (FAILED(autoCaller.rc()))
5875 return autoCaller.rc();
5876
5877 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
5878 if (rc == E_ACCESSDENIED)
5879 /* The VM is not running or the service is not (yet) accessible */
5880 rc = setGuestPropertyToService(aName, aValue, aFlags);
5881 return rc;
5882#endif // VBOX_WITH_GUEST_PROPS
5883}
5884
5885STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
5886{
5887 return SetGuestProperty(aName, aValue, NULL);
5888}
5889
5890STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
5891{
5892 HRESULT hrc = SetGuestProperty(aName, NULL, NULL);
5893 LogRel(("DeleteGuestProperty: %ls -> %Rhrc\n", aName, hrc)); /* !REMOVE ME! Debugging testboxes! */
5894 return hrc;
5895}
5896
5897#ifdef VBOX_WITH_GUEST_PROPS
5898/**
5899 * Enumerate the guest properties in VBoxSVC's internal structures.
5900 */
5901HRESULT Machine::enumerateGuestPropertiesInService
5902 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5903 ComSafeArrayOut(BSTR, aValues),
5904 ComSafeArrayOut(LONG64, aTimestamps),
5905 ComSafeArrayOut(BSTR, aFlags))
5906{
5907 using namespace guestProp;
5908
5909 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5910 Utf8Str strPatterns(aPatterns);
5911
5912 HWData::GuestPropertyMap propMap;
5913
5914 /*
5915 * Look for matching patterns and build up a list.
5916 */
5917 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5918 while (it != mHWData->mGuestProperties.end())
5919 {
5920 if ( strPatterns.isEmpty()
5921 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5922 RTSTR_MAX,
5923 it->first.c_str(),
5924 RTSTR_MAX,
5925 NULL)
5926 )
5927 {
5928 propMap.insert(*it);
5929 }
5930
5931 it++;
5932 }
5933
5934 alock.release();
5935
5936 /*
5937 * And build up the arrays for returning the property information.
5938 */
5939 size_t cEntries = propMap.size();
5940 SafeArray<BSTR> names(cEntries);
5941 SafeArray<BSTR> values(cEntries);
5942 SafeArray<LONG64> timestamps(cEntries);
5943 SafeArray<BSTR> flags(cEntries);
5944 size_t iProp = 0;
5945
5946 it = propMap.begin();
5947 while (it != propMap.end())
5948 {
5949 char szFlags[MAX_FLAGS_LEN + 1];
5950 it->first.cloneTo(&names[iProp]);
5951 it->second.strValue.cloneTo(&values[iProp]);
5952 timestamps[iProp] = it->second.mTimestamp;
5953 writeFlags(it->second.mFlags, szFlags);
5954 Bstr(szFlags).cloneTo(&flags[iProp++]);
5955 it++;
5956 }
5957 names.detachTo(ComSafeArrayOutArg(aNames));
5958 values.detachTo(ComSafeArrayOutArg(aValues));
5959 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
5960 flags.detachTo(ComSafeArrayOutArg(aFlags));
5961 return S_OK;
5962}
5963
5964/**
5965 * Enumerate the properties managed by a VM.
5966 * @returns E_ACCESSDENIED if the VM process is not available or not
5967 * currently handling queries and the setting should then be done in
5968 * VBoxSVC.
5969 */
5970HRESULT Machine::enumerateGuestPropertiesOnVM
5971 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5972 ComSafeArrayOut(BSTR, aValues),
5973 ComSafeArrayOut(LONG64, aTimestamps),
5974 ComSafeArrayOut(BSTR, aFlags))
5975{
5976 HRESULT rc;
5977 ComPtr<IInternalSessionControl> directControl;
5978 directControl = mData->mSession.mDirectControl;
5979
5980 if (!directControl)
5981 rc = E_ACCESSDENIED;
5982 else
5983 rc = directControl->EnumerateGuestProperties
5984 (aPatterns, ComSafeArrayOutArg(aNames),
5985 ComSafeArrayOutArg(aValues),
5986 ComSafeArrayOutArg(aTimestamps),
5987 ComSafeArrayOutArg(aFlags));
5988 return rc;
5989}
5990#endif // VBOX_WITH_GUEST_PROPS
5991
5992STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
5993 ComSafeArrayOut(BSTR, aNames),
5994 ComSafeArrayOut(BSTR, aValues),
5995 ComSafeArrayOut(LONG64, aTimestamps),
5996 ComSafeArrayOut(BSTR, aFlags))
5997{
5998#ifndef VBOX_WITH_GUEST_PROPS
5999 ReturnComNotImplemented();
6000#else // VBOX_WITH_GUEST_PROPS
6001 CheckComArgMaybeNull(aPatterns);
6002 CheckComArgOutSafeArrayPointerValid(aNames);
6003 CheckComArgOutSafeArrayPointerValid(aValues);
6004 CheckComArgOutSafeArrayPointerValid(aTimestamps);
6005 CheckComArgOutSafeArrayPointerValid(aFlags);
6006
6007 AutoCaller autoCaller(this);
6008 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6009
6010 HRESULT rc = enumerateGuestPropertiesOnVM
6011 (aPatterns, ComSafeArrayOutArg(aNames),
6012 ComSafeArrayOutArg(aValues),
6013 ComSafeArrayOutArg(aTimestamps),
6014 ComSafeArrayOutArg(aFlags));
6015 if (rc == E_ACCESSDENIED)
6016 /* The VM is not running or the service is not (yet) accessible */
6017 rc = enumerateGuestPropertiesInService
6018 (aPatterns, ComSafeArrayOutArg(aNames),
6019 ComSafeArrayOutArg(aValues),
6020 ComSafeArrayOutArg(aTimestamps),
6021 ComSafeArrayOutArg(aFlags));
6022 return rc;
6023#endif // VBOX_WITH_GUEST_PROPS
6024}
6025
6026STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
6027 ComSafeArrayOut(IMediumAttachment*, aAttachments))
6028{
6029 MediaData::AttachmentList atts;
6030
6031 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
6032 if (FAILED(rc)) return rc;
6033
6034 SafeIfaceArray<IMediumAttachment> attachments(atts);
6035 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
6036
6037 return S_OK;
6038}
6039
6040STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
6041 LONG aControllerPort,
6042 LONG aDevice,
6043 IMediumAttachment **aAttachment)
6044{
6045 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
6046 aControllerName, aControllerPort, aDevice));
6047
6048 CheckComArgStrNotEmptyOrNull(aControllerName);
6049 CheckComArgOutPointerValid(aAttachment);
6050
6051 AutoCaller autoCaller(this);
6052 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6053
6054 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6055
6056 *aAttachment = NULL;
6057
6058 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
6059 aControllerName,
6060 aControllerPort,
6061 aDevice);
6062 if (pAttach.isNull())
6063 return setError(VBOX_E_OBJECT_NOT_FOUND,
6064 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
6065 aDevice, aControllerPort, aControllerName);
6066
6067 pAttach.queryInterfaceTo(aAttachment);
6068
6069 return S_OK;
6070}
6071
6072STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
6073 StorageBus_T aConnectionType,
6074 IStorageController **controller)
6075{
6076 CheckComArgStrNotEmptyOrNull(aName);
6077
6078 if ( (aConnectionType <= StorageBus_Null)
6079 || (aConnectionType > StorageBus_SAS))
6080 return setError(E_INVALIDARG,
6081 tr("Invalid connection type: %d"),
6082 aConnectionType);
6083
6084 AutoCaller autoCaller(this);
6085 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6086
6087 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6088
6089 HRESULT rc = checkStateDependency(MutableStateDep);
6090 if (FAILED(rc)) return rc;
6091
6092 /* try to find one with the name first. */
6093 ComObjPtr<StorageController> ctrl;
6094
6095 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
6096 if (SUCCEEDED(rc))
6097 return setError(VBOX_E_OBJECT_IN_USE,
6098 tr("Storage controller named '%ls' already exists"),
6099 aName);
6100
6101 ctrl.createObject();
6102
6103 /* get a new instance number for the storage controller */
6104 ULONG ulInstance = 0;
6105 bool fBootable = true;
6106 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6107 it != mStorageControllers->end();
6108 ++it)
6109 {
6110 if ((*it)->getStorageBus() == aConnectionType)
6111 {
6112 ULONG ulCurInst = (*it)->getInstance();
6113
6114 if (ulCurInst >= ulInstance)
6115 ulInstance = ulCurInst + 1;
6116
6117 /* Only one controller of each type can be marked as bootable. */
6118 if ((*it)->getBootable())
6119 fBootable = false;
6120 }
6121 }
6122
6123 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6124 if (FAILED(rc)) return rc;
6125
6126 setModified(IsModified_Storage);
6127 mStorageControllers.backup();
6128 mStorageControllers->push_back(ctrl);
6129
6130 ctrl.queryInterfaceTo(controller);
6131
6132 /* inform the direct session if any */
6133 alock.release();
6134 onStorageControllerChange();
6135
6136 return S_OK;
6137}
6138
6139STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6140 IStorageController **aStorageController)
6141{
6142 CheckComArgStrNotEmptyOrNull(aName);
6143
6144 AutoCaller autoCaller(this);
6145 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6146
6147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6148
6149 ComObjPtr<StorageController> ctrl;
6150
6151 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6152 if (SUCCEEDED(rc))
6153 ctrl.queryInterfaceTo(aStorageController);
6154
6155 return rc;
6156}
6157
6158STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6159 IStorageController **aStorageController)
6160{
6161 AutoCaller autoCaller(this);
6162 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6163
6164 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6165
6166 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6167 it != mStorageControllers->end();
6168 ++it)
6169 {
6170 if ((*it)->getInstance() == aInstance)
6171 {
6172 (*it).queryInterfaceTo(aStorageController);
6173 return S_OK;
6174 }
6175 }
6176
6177 return setError(VBOX_E_OBJECT_NOT_FOUND,
6178 tr("Could not find a storage controller with instance number '%lu'"),
6179 aInstance);
6180}
6181
6182STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6183{
6184 AutoCaller autoCaller(this);
6185 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6186
6187 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6188
6189 HRESULT rc = checkStateDependency(MutableStateDep);
6190 if (FAILED(rc)) return rc;
6191
6192 ComObjPtr<StorageController> ctrl;
6193
6194 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6195 if (SUCCEEDED(rc))
6196 {
6197 /* Ensure that only one controller of each type is marked as bootable. */
6198 if (fBootable == TRUE)
6199 {
6200 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6201 it != mStorageControllers->end();
6202 ++it)
6203 {
6204 ComObjPtr<StorageController> aCtrl = (*it);
6205
6206 if ( (aCtrl->getName() != Utf8Str(aName))
6207 && aCtrl->getBootable() == TRUE
6208 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6209 && aCtrl->getControllerType() == ctrl->getControllerType())
6210 {
6211 aCtrl->setBootable(FALSE);
6212 break;
6213 }
6214 }
6215 }
6216
6217 if (SUCCEEDED(rc))
6218 {
6219 ctrl->setBootable(fBootable);
6220 setModified(IsModified_Storage);
6221 }
6222 }
6223
6224 if (SUCCEEDED(rc))
6225 {
6226 /* inform the direct session if any */
6227 alock.release();
6228 onStorageControllerChange();
6229 }
6230
6231 return rc;
6232}
6233
6234STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6235{
6236 CheckComArgStrNotEmptyOrNull(aName);
6237
6238 AutoCaller autoCaller(this);
6239 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6240
6241 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6242
6243 HRESULT rc = checkStateDependency(MutableStateDep);
6244 if (FAILED(rc)) return rc;
6245
6246 ComObjPtr<StorageController> ctrl;
6247 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6248 if (FAILED(rc)) return rc;
6249
6250 {
6251 /* find all attached devices to the appropriate storage controller and detach them all */
6252 // make a temporary list because detachDevice invalidates iterators into
6253 // mMediaData->mAttachments
6254 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6255
6256 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6257 it != llAttachments2.end();
6258 ++it)
6259 {
6260 MediumAttachment *pAttachTemp = *it;
6261
6262 AutoCaller localAutoCaller(pAttachTemp);
6263 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6264
6265 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6266
6267 if (pAttachTemp->getControllerName() == aName)
6268 {
6269 rc = detachDevice(pAttachTemp, alock, NULL);
6270 if (FAILED(rc)) return rc;
6271 }
6272 }
6273 }
6274
6275 /* We can remove it now. */
6276 setModified(IsModified_Storage);
6277 mStorageControllers.backup();
6278
6279 ctrl->unshare();
6280
6281 mStorageControllers->remove(ctrl);
6282
6283 /* inform the direct session if any */
6284 alock.release();
6285 onStorageControllerChange();
6286
6287 return S_OK;
6288}
6289
6290STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6291 ULONG *puOriginX,
6292 ULONG *puOriginY,
6293 ULONG *puWidth,
6294 ULONG *puHeight,
6295 BOOL *pfEnabled)
6296{
6297 LogFlowThisFunc(("\n"));
6298
6299 CheckComArgNotNull(puOriginX);
6300 CheckComArgNotNull(puOriginY);
6301 CheckComArgNotNull(puWidth);
6302 CheckComArgNotNull(puHeight);
6303 CheckComArgNotNull(pfEnabled);
6304
6305 uint32_t u32OriginX= 0;
6306 uint32_t u32OriginY= 0;
6307 uint32_t u32Width = 0;
6308 uint32_t u32Height = 0;
6309 uint16_t u16Flags = 0;
6310
6311 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6312 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6313 if (RT_FAILURE(vrc))
6314 {
6315#ifdef RT_OS_WINDOWS
6316 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6317 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6318 * So just assign fEnable to TRUE again.
6319 * The right fix would be to change GUI API wrappers to make sure that parameters
6320 * are changed only if API succeeds.
6321 */
6322 *pfEnabled = TRUE;
6323#endif
6324 return setError(VBOX_E_IPRT_ERROR,
6325 tr("Saved guest size is not available (%Rrc)"),
6326 vrc);
6327 }
6328
6329 *puOriginX = u32OriginX;
6330 *puOriginY = u32OriginY;
6331 *puWidth = u32Width;
6332 *puHeight = u32Height;
6333 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6334
6335 return S_OK;
6336}
6337
6338STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6339{
6340 LogFlowThisFunc(("\n"));
6341
6342 CheckComArgNotNull(aSize);
6343 CheckComArgNotNull(aWidth);
6344 CheckComArgNotNull(aHeight);
6345
6346 if (aScreenId != 0)
6347 return E_NOTIMPL;
6348
6349 AutoCaller autoCaller(this);
6350 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6351
6352 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6353
6354 uint8_t *pu8Data = NULL;
6355 uint32_t cbData = 0;
6356 uint32_t u32Width = 0;
6357 uint32_t u32Height = 0;
6358
6359 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6360
6361 if (RT_FAILURE(vrc))
6362 return setError(VBOX_E_IPRT_ERROR,
6363 tr("Saved screenshot data is not available (%Rrc)"),
6364 vrc);
6365
6366 *aSize = cbData;
6367 *aWidth = u32Width;
6368 *aHeight = u32Height;
6369
6370 freeSavedDisplayScreenshot(pu8Data);
6371
6372 return S_OK;
6373}
6374
6375STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6376{
6377 LogFlowThisFunc(("\n"));
6378
6379 CheckComArgNotNull(aWidth);
6380 CheckComArgNotNull(aHeight);
6381 CheckComArgOutSafeArrayPointerValid(aData);
6382
6383 if (aScreenId != 0)
6384 return E_NOTIMPL;
6385
6386 AutoCaller autoCaller(this);
6387 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6388
6389 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6390
6391 uint8_t *pu8Data = NULL;
6392 uint32_t cbData = 0;
6393 uint32_t u32Width = 0;
6394 uint32_t u32Height = 0;
6395
6396 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6397
6398 if (RT_FAILURE(vrc))
6399 return setError(VBOX_E_IPRT_ERROR,
6400 tr("Saved screenshot data is not available (%Rrc)"),
6401 vrc);
6402
6403 *aWidth = u32Width;
6404 *aHeight = u32Height;
6405
6406 com::SafeArray<BYTE> bitmap(cbData);
6407 /* Convert pixels to format expected by the API caller. */
6408 if (aBGR)
6409 {
6410 /* [0] B, [1] G, [2] R, [3] A. */
6411 for (unsigned i = 0; i < cbData; i += 4)
6412 {
6413 bitmap[i] = pu8Data[i];
6414 bitmap[i + 1] = pu8Data[i + 1];
6415 bitmap[i + 2] = pu8Data[i + 2];
6416 bitmap[i + 3] = 0xff;
6417 }
6418 }
6419 else
6420 {
6421 /* [0] R, [1] G, [2] B, [3] A. */
6422 for (unsigned i = 0; i < cbData; i += 4)
6423 {
6424 bitmap[i] = pu8Data[i + 2];
6425 bitmap[i + 1] = pu8Data[i + 1];
6426 bitmap[i + 2] = pu8Data[i];
6427 bitmap[i + 3] = 0xff;
6428 }
6429 }
6430 bitmap.detachTo(ComSafeArrayOutArg(aData));
6431
6432 freeSavedDisplayScreenshot(pu8Data);
6433
6434 return S_OK;
6435}
6436
6437
6438STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6439{
6440 LogFlowThisFunc(("\n"));
6441
6442 CheckComArgNotNull(aWidth);
6443 CheckComArgNotNull(aHeight);
6444 CheckComArgOutSafeArrayPointerValid(aData);
6445
6446 if (aScreenId != 0)
6447 return E_NOTIMPL;
6448
6449 AutoCaller autoCaller(this);
6450 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6451
6452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6453
6454 uint8_t *pu8Data = NULL;
6455 uint32_t cbData = 0;
6456 uint32_t u32Width = 0;
6457 uint32_t u32Height = 0;
6458
6459 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6460
6461 if (RT_FAILURE(vrc))
6462 return setError(VBOX_E_IPRT_ERROR,
6463 tr("Saved screenshot data is not available (%Rrc)"),
6464 vrc);
6465
6466 *aWidth = u32Width;
6467 *aHeight = u32Height;
6468
6469 HRESULT rc = S_OK;
6470 uint8_t *pu8PNG = NULL;
6471 uint32_t cbPNG = 0;
6472 uint32_t cxPNG = 0;
6473 uint32_t cyPNG = 0;
6474
6475 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6476
6477 if (RT_SUCCESS(vrc))
6478 {
6479 com::SafeArray<BYTE> screenData(cbPNG);
6480 screenData.initFrom(pu8PNG, cbPNG);
6481 if (pu8PNG)
6482 RTMemFree(pu8PNG);
6483 screenData.detachTo(ComSafeArrayOutArg(aData));
6484 }
6485 else
6486 {
6487 if (pu8PNG)
6488 RTMemFree(pu8PNG);
6489 return setError(VBOX_E_IPRT_ERROR,
6490 tr("Could not convert screenshot to PNG (%Rrc)"),
6491 vrc);
6492 }
6493
6494 freeSavedDisplayScreenshot(pu8Data);
6495
6496 return rc;
6497}
6498
6499STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6500{
6501 LogFlowThisFunc(("\n"));
6502
6503 CheckComArgNotNull(aSize);
6504 CheckComArgNotNull(aWidth);
6505 CheckComArgNotNull(aHeight);
6506
6507 if (aScreenId != 0)
6508 return E_NOTIMPL;
6509
6510 AutoCaller autoCaller(this);
6511 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6512
6513 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6514
6515 uint8_t *pu8Data = NULL;
6516 uint32_t cbData = 0;
6517 uint32_t u32Width = 0;
6518 uint32_t u32Height = 0;
6519
6520 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6521
6522 if (RT_FAILURE(vrc))
6523 return setError(VBOX_E_IPRT_ERROR,
6524 tr("Saved screenshot data is not available (%Rrc)"),
6525 vrc);
6526
6527 *aSize = cbData;
6528 *aWidth = u32Width;
6529 *aHeight = u32Height;
6530
6531 freeSavedDisplayScreenshot(pu8Data);
6532
6533 return S_OK;
6534}
6535
6536STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6537{
6538 LogFlowThisFunc(("\n"));
6539
6540 CheckComArgNotNull(aWidth);
6541 CheckComArgNotNull(aHeight);
6542 CheckComArgOutSafeArrayPointerValid(aData);
6543
6544 if (aScreenId != 0)
6545 return E_NOTIMPL;
6546
6547 AutoCaller autoCaller(this);
6548 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6549
6550 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6551
6552 uint8_t *pu8Data = NULL;
6553 uint32_t cbData = 0;
6554 uint32_t u32Width = 0;
6555 uint32_t u32Height = 0;
6556
6557 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6558
6559 if (RT_FAILURE(vrc))
6560 return setError(VBOX_E_IPRT_ERROR,
6561 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6562 vrc);
6563
6564 *aWidth = u32Width;
6565 *aHeight = u32Height;
6566
6567 com::SafeArray<BYTE> png(cbData);
6568 png.initFrom(pu8Data, cbData);
6569 png.detachTo(ComSafeArrayOutArg(aData));
6570
6571 freeSavedDisplayScreenshot(pu8Data);
6572
6573 return S_OK;
6574}
6575
6576STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6577{
6578 HRESULT rc = S_OK;
6579 LogFlowThisFunc(("\n"));
6580
6581 AutoCaller autoCaller(this);
6582 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6583
6584 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6585
6586 if (!mHWData->mCPUHotPlugEnabled)
6587 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6588
6589 if (aCpu >= mHWData->mCPUCount)
6590 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6591
6592 if (mHWData->mCPUAttached[aCpu])
6593 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6594
6595 alock.release();
6596 rc = onCPUChange(aCpu, false);
6597 alock.acquire();
6598 if (FAILED(rc)) return rc;
6599
6600 setModified(IsModified_MachineData);
6601 mHWData.backup();
6602 mHWData->mCPUAttached[aCpu] = true;
6603
6604 /* Save settings if online */
6605 if (Global::IsOnline(mData->mMachineState))
6606 saveSettings(NULL);
6607
6608 return S_OK;
6609}
6610
6611STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6612{
6613 HRESULT rc = S_OK;
6614 LogFlowThisFunc(("\n"));
6615
6616 AutoCaller autoCaller(this);
6617 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6618
6619 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6620
6621 if (!mHWData->mCPUHotPlugEnabled)
6622 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6623
6624 if (aCpu >= SchemaDefs::MaxCPUCount)
6625 return setError(E_INVALIDARG,
6626 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6627 SchemaDefs::MaxCPUCount);
6628
6629 if (!mHWData->mCPUAttached[aCpu])
6630 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6631
6632 /* CPU 0 can't be detached */
6633 if (aCpu == 0)
6634 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6635
6636 alock.release();
6637 rc = onCPUChange(aCpu, true);
6638 alock.acquire();
6639 if (FAILED(rc)) return rc;
6640
6641 setModified(IsModified_MachineData);
6642 mHWData.backup();
6643 mHWData->mCPUAttached[aCpu] = false;
6644
6645 /* Save settings if online */
6646 if (Global::IsOnline(mData->mMachineState))
6647 saveSettings(NULL);
6648
6649 return S_OK;
6650}
6651
6652STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6653{
6654 LogFlowThisFunc(("\n"));
6655
6656 CheckComArgNotNull(aCpuAttached);
6657
6658 *aCpuAttached = false;
6659
6660 AutoCaller autoCaller(this);
6661 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6662
6663 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6664
6665 /* If hotplug is enabled the CPU is always enabled. */
6666 if (!mHWData->mCPUHotPlugEnabled)
6667 {
6668 if (aCpu < mHWData->mCPUCount)
6669 *aCpuAttached = true;
6670 }
6671 else
6672 {
6673 if (aCpu < SchemaDefs::MaxCPUCount)
6674 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6675 }
6676
6677 return S_OK;
6678}
6679
6680STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6681{
6682 CheckComArgOutPointerValid(aName);
6683
6684 AutoCaller autoCaller(this);
6685 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6686
6687 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6688
6689 Utf8Str log = queryLogFilename(aIdx);
6690 if (!RTFileExists(log.c_str()))
6691 log.setNull();
6692 log.cloneTo(aName);
6693
6694 return S_OK;
6695}
6696
6697STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6698{
6699 LogFlowThisFunc(("\n"));
6700 CheckComArgOutSafeArrayPointerValid(aData);
6701 if (aSize < 0)
6702 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6703
6704 AutoCaller autoCaller(this);
6705 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6706
6707 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6708
6709 HRESULT rc = S_OK;
6710 Utf8Str log = queryLogFilename(aIdx);
6711
6712 /* do not unnecessarily hold the lock while doing something which does
6713 * not need the lock and potentially takes a long time. */
6714 alock.release();
6715
6716 /* Limit the chunk size to 32K for now, as that gives better performance
6717 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6718 * One byte expands to approx. 25 bytes of breathtaking XML. */
6719 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6720 com::SafeArray<BYTE> logData(cbData);
6721
6722 RTFILE LogFile;
6723 int vrc = RTFileOpen(&LogFile, log.c_str(),
6724 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6725 if (RT_SUCCESS(vrc))
6726 {
6727 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6728 if (RT_SUCCESS(vrc))
6729 logData.resize(cbData);
6730 else
6731 rc = setError(VBOX_E_IPRT_ERROR,
6732 tr("Could not read log file '%s' (%Rrc)"),
6733 log.c_str(), vrc);
6734 RTFileClose(LogFile);
6735 }
6736 else
6737 rc = setError(VBOX_E_IPRT_ERROR,
6738 tr("Could not open log file '%s' (%Rrc)"),
6739 log.c_str(), vrc);
6740
6741 if (FAILED(rc))
6742 logData.resize(0);
6743 logData.detachTo(ComSafeArrayOutArg(aData));
6744
6745 return rc;
6746}
6747
6748
6749/**
6750 * Currently this method doesn't attach device to the running VM,
6751 * just makes sure it's plugged on next VM start.
6752 */
6753STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6754{
6755 AutoCaller autoCaller(this);
6756 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6757
6758 // lock scope
6759 {
6760 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6761
6762 HRESULT rc = checkStateDependency(MutableStateDep);
6763 if (FAILED(rc)) return rc;
6764
6765 ChipsetType_T aChipset = ChipsetType_PIIX3;
6766 COMGETTER(ChipsetType)(&aChipset);
6767
6768 if (aChipset != ChipsetType_ICH9)
6769 {
6770 return setError(E_INVALIDARG,
6771 tr("Host PCI attachment only supported with ICH9 chipset"));
6772 }
6773
6774 // check if device with this host PCI address already attached
6775 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6776 it != mHWData->mPCIDeviceAssignments.end();
6777 ++it)
6778 {
6779 LONG iHostAddress = -1;
6780 ComPtr<PCIDeviceAttachment> pAttach;
6781 pAttach = *it;
6782 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6783 if (iHostAddress == hostAddress)
6784 return setError(E_INVALIDARG,
6785 tr("Device with host PCI address already attached to this VM"));
6786 }
6787
6788 ComObjPtr<PCIDeviceAttachment> pda;
6789 char name[32];
6790
6791 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6792 Bstr bname(name);
6793 pda.createObject();
6794 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
6795 setModified(IsModified_MachineData);
6796 mHWData.backup();
6797 mHWData->mPCIDeviceAssignments.push_back(pda);
6798 }
6799
6800 return S_OK;
6801}
6802
6803/**
6804 * Currently this method doesn't detach device from the running VM,
6805 * just makes sure it's not plugged on next VM start.
6806 */
6807STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
6808{
6809 AutoCaller autoCaller(this);
6810 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6811
6812 ComObjPtr<PCIDeviceAttachment> pAttach;
6813 bool fRemoved = false;
6814 HRESULT rc;
6815
6816 // lock scope
6817 {
6818 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6819
6820 rc = checkStateDependency(MutableStateDep);
6821 if (FAILED(rc)) return rc;
6822
6823 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6824 it != mHWData->mPCIDeviceAssignments.end();
6825 ++it)
6826 {
6827 LONG iHostAddress = -1;
6828 pAttach = *it;
6829 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6830 if (iHostAddress != -1 && iHostAddress == hostAddress)
6831 {
6832 setModified(IsModified_MachineData);
6833 mHWData.backup();
6834 mHWData->mPCIDeviceAssignments.remove(pAttach);
6835 fRemoved = true;
6836 break;
6837 }
6838 }
6839 }
6840
6841
6842 /* Fire event outside of the lock */
6843 if (fRemoved)
6844 {
6845 Assert(!pAttach.isNull());
6846 ComPtr<IEventSource> es;
6847 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6848 Assert(SUCCEEDED(rc));
6849 Bstr mid;
6850 rc = this->COMGETTER(Id)(mid.asOutParam());
6851 Assert(SUCCEEDED(rc));
6852 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6853 }
6854
6855 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6856 tr("No host PCI device %08x attached"),
6857 hostAddress
6858 );
6859}
6860
6861STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
6862{
6863 CheckComArgOutSafeArrayPointerValid(aAssignments);
6864
6865 AutoCaller autoCaller(this);
6866 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6867
6868 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6869
6870 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
6871 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
6872
6873 return S_OK;
6874}
6875
6876STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
6877{
6878 CheckComArgOutPointerValid(aBandwidthControl);
6879
6880 AutoCaller autoCaller(this);
6881 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6882
6883 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
6884
6885 return S_OK;
6886}
6887
6888STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
6889{
6890 CheckComArgOutPointerValid(pfEnabled);
6891 AutoCaller autoCaller(this);
6892 HRESULT hrc = autoCaller.rc();
6893 if (SUCCEEDED(hrc))
6894 {
6895 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6896 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
6897 }
6898 return hrc;
6899}
6900
6901STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
6902{
6903 AutoCaller autoCaller(this);
6904 HRESULT hrc = autoCaller.rc();
6905 if (SUCCEEDED(hrc))
6906 {
6907 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6908 hrc = checkStateDependency(MutableStateDep);
6909 if (SUCCEEDED(hrc))
6910 {
6911 hrc = mHWData.backupEx();
6912 if (SUCCEEDED(hrc))
6913 {
6914 setModified(IsModified_MachineData);
6915 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
6916 }
6917 }
6918 }
6919 return hrc;
6920}
6921
6922STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
6923{
6924 CheckComArgOutPointerValid(pbstrConfig);
6925 AutoCaller autoCaller(this);
6926 HRESULT hrc = autoCaller.rc();
6927 if (SUCCEEDED(hrc))
6928 {
6929 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6930 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
6931 }
6932 return hrc;
6933}
6934
6935STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
6936{
6937 CheckComArgStr(bstrConfig);
6938 AutoCaller autoCaller(this);
6939 HRESULT hrc = autoCaller.rc();
6940 if (SUCCEEDED(hrc))
6941 {
6942 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6943 hrc = checkStateDependency(MutableStateDep);
6944 if (SUCCEEDED(hrc))
6945 {
6946 hrc = mHWData.backupEx();
6947 if (SUCCEEDED(hrc))
6948 {
6949 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
6950 if (SUCCEEDED(hrc))
6951 setModified(IsModified_MachineData);
6952 }
6953 }
6954 }
6955 return hrc;
6956
6957}
6958
6959STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
6960{
6961 CheckComArgOutPointerValid(pfAllow);
6962 AutoCaller autoCaller(this);
6963 HRESULT hrc = autoCaller.rc();
6964 if (SUCCEEDED(hrc))
6965 {
6966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6967 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
6968 }
6969 return hrc;
6970}
6971
6972STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
6973{
6974 AutoCaller autoCaller(this);
6975 HRESULT hrc = autoCaller.rc();
6976 if (SUCCEEDED(hrc))
6977 {
6978 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6979 hrc = checkStateDependency(MutableStateDep);
6980 if (SUCCEEDED(hrc))
6981 {
6982 hrc = mHWData.backupEx();
6983 if (SUCCEEDED(hrc))
6984 {
6985 setModified(IsModified_MachineData);
6986 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
6987 }
6988 }
6989 }
6990 return hrc;
6991}
6992
6993STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
6994{
6995 CheckComArgOutPointerValid(pfEnabled);
6996 AutoCaller autoCaller(this);
6997 HRESULT hrc = autoCaller.rc();
6998 if (SUCCEEDED(hrc))
6999 {
7000 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7001 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
7002 }
7003 return hrc;
7004}
7005
7006STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
7007{
7008 AutoCaller autoCaller(this);
7009 HRESULT hrc = autoCaller.rc();
7010 if (SUCCEEDED(hrc))
7011 {
7012 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7013 hrc = checkStateDependency(MutableStateDep);
7014 if ( SUCCEEDED(hrc)
7015 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
7016 {
7017 AutostartDb *autostartDb = mParent->getAutostartDb();
7018 int vrc;
7019
7020 if (fEnabled)
7021 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7022 else
7023 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7024
7025 if (RT_SUCCESS(vrc))
7026 {
7027 hrc = mHWData.backupEx();
7028 if (SUCCEEDED(hrc))
7029 {
7030 setModified(IsModified_MachineData);
7031 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
7032 }
7033 }
7034 else if (vrc == VERR_NOT_SUPPORTED)
7035 hrc = setError(VBOX_E_NOT_SUPPORTED,
7036 tr("The VM autostart feature is not supported on this platform"));
7037 else if (vrc == VERR_PATH_NOT_FOUND)
7038 hrc = setError(E_FAIL,
7039 tr("The path to the autostart database is not set"));
7040 else
7041 hrc = setError(E_UNEXPECTED,
7042 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7043 fEnabled ? "Adding" : "Removing",
7044 mUserData->s.strName.c_str(), vrc);
7045 }
7046 }
7047 return hrc;
7048}
7049
7050STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
7051{
7052 CheckComArgOutPointerValid(puDelay);
7053 AutoCaller autoCaller(this);
7054 HRESULT hrc = autoCaller.rc();
7055 if (SUCCEEDED(hrc))
7056 {
7057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7058 *puDelay = mHWData->mAutostart.uAutostartDelay;
7059 }
7060 return hrc;
7061}
7062
7063STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
7064{
7065 AutoCaller autoCaller(this);
7066 HRESULT hrc = autoCaller.rc();
7067 if (SUCCEEDED(hrc))
7068 {
7069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7070 hrc = checkStateDependency(MutableStateDep);
7071 if (SUCCEEDED(hrc))
7072 {
7073 hrc = mHWData.backupEx();
7074 if (SUCCEEDED(hrc))
7075 {
7076 setModified(IsModified_MachineData);
7077 mHWData->mAutostart.uAutostartDelay = uDelay;
7078 }
7079 }
7080 }
7081 return hrc;
7082}
7083
7084STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
7085{
7086 CheckComArgOutPointerValid(penmAutostopType);
7087 AutoCaller autoCaller(this);
7088 HRESULT hrc = autoCaller.rc();
7089 if (SUCCEEDED(hrc))
7090 {
7091 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7092 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
7093 }
7094 return hrc;
7095}
7096
7097STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
7098{
7099 AutoCaller autoCaller(this);
7100 HRESULT hrc = autoCaller.rc();
7101 if (SUCCEEDED(hrc))
7102 {
7103 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7104 hrc = checkStateDependency(MutableStateDep);
7105 if ( SUCCEEDED(hrc)
7106 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
7107 {
7108 AutostartDb *autostartDb = mParent->getAutostartDb();
7109 int vrc;
7110
7111 if (enmAutostopType != AutostopType_Disabled)
7112 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7113 else
7114 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7115
7116 if (RT_SUCCESS(vrc))
7117 {
7118 hrc = mHWData.backupEx();
7119 if (SUCCEEDED(hrc))
7120 {
7121 setModified(IsModified_MachineData);
7122 mHWData->mAutostart.enmAutostopType = enmAutostopType;
7123 }
7124 }
7125 else if (vrc == VERR_NOT_SUPPORTED)
7126 hrc = setError(VBOX_E_NOT_SUPPORTED,
7127 tr("The VM autostop feature is not supported on this platform"));
7128 else if (vrc == VERR_PATH_NOT_FOUND)
7129 hrc = setError(E_FAIL,
7130 tr("The path to the autostart database is not set"));
7131 else
7132 hrc = setError(E_UNEXPECTED,
7133 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7134 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7135 mUserData->s.strName.c_str(), vrc);
7136 }
7137 }
7138 return hrc;
7139}
7140
7141STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7142{
7143 CheckComArgOutPointerValid(aDefaultFrontend);
7144 AutoCaller autoCaller(this);
7145 HRESULT hrc = autoCaller.rc();
7146 if (SUCCEEDED(hrc))
7147 {
7148 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7149 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7150 }
7151 return hrc;
7152}
7153
7154STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7155{
7156 CheckComArgStr(aDefaultFrontend);
7157 AutoCaller autoCaller(this);
7158 HRESULT hrc = autoCaller.rc();
7159 if (SUCCEEDED(hrc))
7160 {
7161 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7162 hrc = checkStateDependency(MutableOrSavedStateDep);
7163 if (SUCCEEDED(hrc))
7164 {
7165 hrc = mHWData.backupEx();
7166 if (SUCCEEDED(hrc))
7167 {
7168 setModified(IsModified_MachineData);
7169 mHWData->mDefaultFrontend = aDefaultFrontend;
7170 }
7171 }
7172 }
7173 return hrc;
7174}
7175
7176
7177STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7178{
7179 LogFlowFuncEnter();
7180
7181 CheckComArgNotNull(pTarget);
7182 CheckComArgOutPointerValid(pProgress);
7183
7184 /* Convert the options. */
7185 RTCList<CloneOptions_T> optList;
7186 if (options != NULL)
7187 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7188
7189 if (optList.contains(CloneOptions_Link))
7190 {
7191 if (!isSnapshotMachine())
7192 return setError(E_INVALIDARG,
7193 tr("Linked clone can only be created from a snapshot"));
7194 if (mode != CloneMode_MachineState)
7195 return setError(E_INVALIDARG,
7196 tr("Linked clone can only be created for a single machine state"));
7197 }
7198 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7199
7200 AutoCaller autoCaller(this);
7201 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7202
7203
7204 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7205
7206 HRESULT rc = pWorker->start(pProgress);
7207
7208 LogFlowFuncLeave();
7209
7210 return rc;
7211}
7212
7213// public methods for internal purposes
7214/////////////////////////////////////////////////////////////////////////////
7215
7216/**
7217 * Adds the given IsModified_* flag to the dirty flags of the machine.
7218 * This must be called either during loadSettings or under the machine write lock.
7219 * @param fl
7220 */
7221void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7222{
7223 mData->flModifications |= fl;
7224 if (fAllowStateModification && isStateModificationAllowed())
7225 mData->mCurrentStateModified = true;
7226}
7227
7228/**
7229 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7230 * care of the write locking.
7231 *
7232 * @param fModifications The flag to add.
7233 */
7234void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7235{
7236 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7237 setModified(fModification, fAllowStateModification);
7238}
7239
7240/**
7241 * Saves the registry entry of this machine to the given configuration node.
7242 *
7243 * @param aEntryNode Node to save the registry entry to.
7244 *
7245 * @note locks this object for reading.
7246 */
7247HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7248{
7249 AutoLimitedCaller autoCaller(this);
7250 AssertComRCReturnRC(autoCaller.rc());
7251
7252 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7253
7254 data.uuid = mData->mUuid;
7255 data.strSettingsFile = mData->m_strConfigFile;
7256
7257 return S_OK;
7258}
7259
7260/**
7261 * Calculates the absolute path of the given path taking the directory of the
7262 * machine settings file as the current directory.
7263 *
7264 * @param aPath Path to calculate the absolute path for.
7265 * @param aResult Where to put the result (used only on success, can be the
7266 * same Utf8Str instance as passed in @a aPath).
7267 * @return IPRT result.
7268 *
7269 * @note Locks this object for reading.
7270 */
7271int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7272{
7273 AutoCaller autoCaller(this);
7274 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7275
7276 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7277
7278 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7279
7280 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7281
7282 strSettingsDir.stripFilename();
7283 char folder[RTPATH_MAX];
7284 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7285 if (RT_SUCCESS(vrc))
7286 aResult = folder;
7287
7288 return vrc;
7289}
7290
7291/**
7292 * Copies strSource to strTarget, making it relative to the machine folder
7293 * if it is a subdirectory thereof, or simply copying it otherwise.
7294 *
7295 * @param strSource Path to evaluate and copy.
7296 * @param strTarget Buffer to receive target path.
7297 *
7298 * @note Locks this object for reading.
7299 */
7300void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7301 Utf8Str &strTarget)
7302{
7303 AutoCaller autoCaller(this);
7304 AssertComRCReturn(autoCaller.rc(), (void)0);
7305
7306 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7307
7308 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7309 // use strTarget as a temporary buffer to hold the machine settings dir
7310 strTarget = mData->m_strConfigFileFull;
7311 strTarget.stripFilename();
7312 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7313 {
7314 // is relative: then append what's left
7315 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7316 // for empty paths (only possible for subdirs) use "." to avoid
7317 // triggering default settings for not present config attributes.
7318 if (strTarget.isEmpty())
7319 strTarget = ".";
7320 }
7321 else
7322 // is not relative: then overwrite
7323 strTarget = strSource;
7324}
7325
7326/**
7327 * Returns the full path to the machine's log folder in the
7328 * \a aLogFolder argument.
7329 */
7330void Machine::getLogFolder(Utf8Str &aLogFolder)
7331{
7332 AutoCaller autoCaller(this);
7333 AssertComRCReturnVoid(autoCaller.rc());
7334
7335 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7336
7337 char szTmp[RTPATH_MAX];
7338 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7339 if (RT_SUCCESS(vrc))
7340 {
7341 if (szTmp[0] && !mUserData.isNull())
7342 {
7343 char szTmp2[RTPATH_MAX];
7344 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7345 if (RT_SUCCESS(vrc))
7346 aLogFolder = BstrFmt("%s%c%s",
7347 szTmp2,
7348 RTPATH_DELIMITER,
7349 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7350 }
7351 else
7352 vrc = VERR_PATH_IS_RELATIVE;
7353 }
7354
7355 if (RT_FAILURE(vrc))
7356 {
7357 // fallback if VBOX_USER_LOGHOME is not set or invalid
7358 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7359 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7360 aLogFolder.append(RTPATH_DELIMITER);
7361 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7362 }
7363}
7364
7365/**
7366 * Returns the full path to the machine's log file for an given index.
7367 */
7368Utf8Str Machine::queryLogFilename(ULONG idx)
7369{
7370 Utf8Str logFolder;
7371 getLogFolder(logFolder);
7372 Assert(logFolder.length());
7373 Utf8Str log;
7374 if (idx == 0)
7375 log = Utf8StrFmt("%s%cVBox.log",
7376 logFolder.c_str(), RTPATH_DELIMITER);
7377 else
7378 log = Utf8StrFmt("%s%cVBox.log.%d",
7379 logFolder.c_str(), RTPATH_DELIMITER, idx);
7380 return log;
7381}
7382
7383/**
7384 * Composes a unique saved state filename based on the current system time. The filename is
7385 * granular to the second so this will work so long as no more than one snapshot is taken on
7386 * a machine per second.
7387 *
7388 * Before version 4.1, we used this formula for saved state files:
7389 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7390 * which no longer works because saved state files can now be shared between the saved state of the
7391 * "saved" machine and an online snapshot, and the following would cause problems:
7392 * 1) save machine
7393 * 2) create online snapshot from that machine state --> reusing saved state file
7394 * 3) save machine again --> filename would be reused, breaking the online snapshot
7395 *
7396 * So instead we now use a timestamp.
7397 *
7398 * @param str
7399 */
7400void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7401{
7402 AutoCaller autoCaller(this);
7403 AssertComRCReturnVoid(autoCaller.rc());
7404
7405 {
7406 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7407 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7408 }
7409
7410 RTTIMESPEC ts;
7411 RTTimeNow(&ts);
7412 RTTIME time;
7413 RTTimeExplode(&time, &ts);
7414
7415 strStateFilePath += RTPATH_DELIMITER;
7416 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7417 time.i32Year, time.u8Month, time.u8MonthDay,
7418 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7419}
7420
7421/**
7422 * @note Locks this object for writing, calls the client process
7423 * (inside the lock).
7424 */
7425HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7426 const Utf8Str &strFrontend,
7427 const Utf8Str &strEnvironment,
7428 ProgressProxy *aProgress)
7429{
7430 LogFlowThisFuncEnter();
7431
7432 AssertReturn(aControl, E_FAIL);
7433 AssertReturn(aProgress, E_FAIL);
7434
7435 AutoCaller autoCaller(this);
7436 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7437
7438 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7439
7440 if (!mData->mRegistered)
7441 return setError(E_UNEXPECTED,
7442 tr("The machine '%s' is not registered"),
7443 mUserData->s.strName.c_str());
7444
7445 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7446
7447 if ( mData->mSession.mState == SessionState_Locked
7448 || mData->mSession.mState == SessionState_Spawning
7449 || mData->mSession.mState == SessionState_Unlocking)
7450 return setError(VBOX_E_INVALID_OBJECT_STATE,
7451 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7452 mUserData->s.strName.c_str());
7453
7454 /* may not be busy */
7455 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7456
7457 /* get the path to the executable */
7458 char szPath[RTPATH_MAX];
7459 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7460 size_t sz = strlen(szPath);
7461 szPath[sz++] = RTPATH_DELIMITER;
7462 szPath[sz] = 0;
7463 char *cmd = szPath + sz;
7464 sz = RTPATH_MAX - sz;
7465
7466 int vrc = VINF_SUCCESS;
7467 RTPROCESS pid = NIL_RTPROCESS;
7468
7469 RTENV env = RTENV_DEFAULT;
7470
7471 if (!strEnvironment.isEmpty())
7472 {
7473 char *newEnvStr = NULL;
7474
7475 do
7476 {
7477 /* clone the current environment */
7478 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7479 AssertRCBreakStmt(vrc2, vrc = vrc2);
7480
7481 newEnvStr = RTStrDup(strEnvironment.c_str());
7482 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7483
7484 /* put new variables to the environment
7485 * (ignore empty variable names here since RTEnv API
7486 * intentionally doesn't do that) */
7487 char *var = newEnvStr;
7488 for (char *p = newEnvStr; *p; ++p)
7489 {
7490 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7491 {
7492 *p = '\0';
7493 if (*var)
7494 {
7495 char *val = strchr(var, '=');
7496 if (val)
7497 {
7498 *val++ = '\0';
7499 vrc2 = RTEnvSetEx(env, var, val);
7500 }
7501 else
7502 vrc2 = RTEnvUnsetEx(env, var);
7503 if (RT_FAILURE(vrc2))
7504 break;
7505 }
7506 var = p + 1;
7507 }
7508 }
7509 if (RT_SUCCESS(vrc2) && *var)
7510 vrc2 = RTEnvPutEx(env, var);
7511
7512 AssertRCBreakStmt(vrc2, vrc = vrc2);
7513 }
7514 while (0);
7515
7516 if (newEnvStr != NULL)
7517 RTStrFree(newEnvStr);
7518 }
7519
7520 /* Qt is default */
7521#ifdef VBOX_WITH_QTGUI
7522 if (strFrontend == "gui" || strFrontend == "GUI/Qt" || strFrontend == "")
7523 {
7524# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7525 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7526# else
7527 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7528# endif
7529 Assert(sz >= sizeof(VirtualBox_exe));
7530 strcpy(cmd, VirtualBox_exe);
7531
7532 Utf8Str idStr = mData->mUuid.toString();
7533 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7534 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7535 }
7536#else /* !VBOX_WITH_QTGUI */
7537 if (0)
7538 ;
7539#endif /* VBOX_WITH_QTGUI */
7540
7541 else
7542
7543#ifdef VBOX_WITH_VBOXSDL
7544 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7545 {
7546 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7547 Assert(sz >= sizeof(VBoxSDL_exe));
7548 strcpy(cmd, VBoxSDL_exe);
7549
7550 Utf8Str idStr = mData->mUuid.toString();
7551 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7552 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7553 }
7554#else /* !VBOX_WITH_VBOXSDL */
7555 if (0)
7556 ;
7557#endif /* !VBOX_WITH_VBOXSDL */
7558
7559 else
7560
7561#ifdef VBOX_WITH_HEADLESS
7562 if ( strFrontend == "headless"
7563 || strFrontend == "capture"
7564 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7565 )
7566 {
7567 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7568 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7569 * and a VM works even if the server has not been installed.
7570 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7571 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7572 * differently in 4.0 and 3.x.
7573 */
7574 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7575 Assert(sz >= sizeof(VBoxHeadless_exe));
7576 strcpy(cmd, VBoxHeadless_exe);
7577
7578 Utf8Str idStr = mData->mUuid.toString();
7579 /* Leave space for "--capture" arg. */
7580 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7581 "--startvm", idStr.c_str(),
7582 "--vrde", "config",
7583 0, /* For "--capture". */
7584 0 };
7585 if (strFrontend == "capture")
7586 {
7587 unsigned pos = RT_ELEMENTS(args) - 2;
7588 args[pos] = "--capture";
7589 }
7590 vrc = RTProcCreate(szPath, args, env,
7591#ifdef RT_OS_WINDOWS
7592 RTPROC_FLAGS_NO_WINDOW
7593#else
7594 0
7595#endif
7596 , &pid);
7597 }
7598#else /* !VBOX_WITH_HEADLESS */
7599 if (0)
7600 ;
7601#endif /* !VBOX_WITH_HEADLESS */
7602 else
7603 {
7604 RTEnvDestroy(env);
7605 return setError(E_INVALIDARG,
7606 tr("Invalid frontend name: '%s'"),
7607 strFrontend.c_str());
7608 }
7609
7610 RTEnvDestroy(env);
7611
7612 if (RT_FAILURE(vrc))
7613 return setError(VBOX_E_IPRT_ERROR,
7614 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7615 mUserData->s.strName.c_str(), vrc);
7616
7617 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7618
7619 /*
7620 * Note that we don't release the lock here before calling the client,
7621 * because it doesn't need to call us back if called with a NULL argument.
7622 * Releasing the lock here is dangerous because we didn't prepare the
7623 * launch data yet, but the client we've just started may happen to be
7624 * too fast and call openSession() that will fail (because of PID, etc.),
7625 * so that the Machine will never get out of the Spawning session state.
7626 */
7627
7628 /* inform the session that it will be a remote one */
7629 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7630 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
7631 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7632
7633 if (FAILED(rc))
7634 {
7635 /* restore the session state */
7636 mData->mSession.mState = SessionState_Unlocked;
7637 /* The failure may occur w/o any error info (from RPC), so provide one */
7638 return setError(VBOX_E_VM_ERROR,
7639 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7640 }
7641
7642 /* attach launch data to the machine */
7643 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7644 mData->mSession.mRemoteControls.push_back(aControl);
7645 mData->mSession.mProgress = aProgress;
7646 mData->mSession.mPID = pid;
7647 mData->mSession.mState = SessionState_Spawning;
7648 mData->mSession.mType = strFrontend;
7649
7650 LogFlowThisFuncLeave();
7651 return S_OK;
7652}
7653
7654/**
7655 * Returns @c true if the given machine has an open direct session and returns
7656 * the session machine instance and additional session data (on some platforms)
7657 * if so.
7658 *
7659 * Note that when the method returns @c false, the arguments remain unchanged.
7660 *
7661 * @param aMachine Session machine object.
7662 * @param aControl Direct session control object (optional).
7663 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7664 *
7665 * @note locks this object for reading.
7666 */
7667#if defined(RT_OS_WINDOWS)
7668bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7669 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7670 HANDLE *aIPCSem /*= NULL*/,
7671 bool aAllowClosing /*= false*/)
7672#elif defined(RT_OS_OS2)
7673bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7674 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7675 HMTX *aIPCSem /*= NULL*/,
7676 bool aAllowClosing /*= false*/)
7677#else
7678bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7679 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7680 bool aAllowClosing /*= false*/)
7681#endif
7682{
7683 AutoLimitedCaller autoCaller(this);
7684 AssertComRCReturn(autoCaller.rc(), false);
7685
7686 /* just return false for inaccessible machines */
7687 if (autoCaller.state() != Ready)
7688 return false;
7689
7690 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7691
7692 if ( mData->mSession.mState == SessionState_Locked
7693 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7694 )
7695 {
7696 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7697
7698 aMachine = mData->mSession.mMachine;
7699
7700 if (aControl != NULL)
7701 *aControl = mData->mSession.mDirectControl;
7702
7703#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7704 /* Additional session data */
7705 if (aIPCSem != NULL)
7706 *aIPCSem = aMachine->mIPCSem;
7707#endif
7708 return true;
7709 }
7710
7711 return false;
7712}
7713
7714/**
7715 * Returns @c true if the given machine has an spawning direct session and
7716 * returns and additional session data (on some platforms) if so.
7717 *
7718 * Note that when the method returns @c false, the arguments remain unchanged.
7719 *
7720 * @param aPID PID of the spawned direct session process.
7721 *
7722 * @note locks this object for reading.
7723 */
7724#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7725bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7726#else
7727bool Machine::isSessionSpawning()
7728#endif
7729{
7730 AutoLimitedCaller autoCaller(this);
7731 AssertComRCReturn(autoCaller.rc(), false);
7732
7733 /* just return false for inaccessible machines */
7734 if (autoCaller.state() != Ready)
7735 return false;
7736
7737 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7738
7739 if (mData->mSession.mState == SessionState_Spawning)
7740 {
7741#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7742 /* Additional session data */
7743 if (aPID != NULL)
7744 {
7745 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
7746 *aPID = mData->mSession.mPID;
7747 }
7748#endif
7749 return true;
7750 }
7751
7752 return false;
7753}
7754
7755/**
7756 * Called from the client watcher thread to check for unexpected client process
7757 * death during Session_Spawning state (e.g. before it successfully opened a
7758 * direct session).
7759 *
7760 * On Win32 and on OS/2, this method is called only when we've got the
7761 * direct client's process termination notification, so it always returns @c
7762 * true.
7763 *
7764 * On other platforms, this method returns @c true if the client process is
7765 * terminated and @c false if it's still alive.
7766 *
7767 * @note Locks this object for writing.
7768 */
7769bool Machine::checkForSpawnFailure()
7770{
7771 AutoCaller autoCaller(this);
7772 if (!autoCaller.isOk())
7773 {
7774 /* nothing to do */
7775 LogFlowThisFunc(("Already uninitialized!\n"));
7776 return true;
7777 }
7778
7779 /* VirtualBox::addProcessToReap() needs a write lock */
7780 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
7781
7782 if (mData->mSession.mState != SessionState_Spawning)
7783 {
7784 /* nothing to do */
7785 LogFlowThisFunc(("Not spawning any more!\n"));
7786 return true;
7787 }
7788
7789 HRESULT rc = S_OK;
7790
7791#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7792
7793 /* the process was already unexpectedly terminated, we just need to set an
7794 * error and finalize session spawning */
7795 rc = setError(E_FAIL,
7796 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
7797 getName().c_str());
7798#else
7799
7800 /* PID not yet initialized, skip check. */
7801 if (mData->mSession.mPID == NIL_RTPROCESS)
7802 return false;
7803
7804 RTPROCSTATUS status;
7805 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
7806 &status);
7807
7808 if (vrc != VERR_PROCESS_RUNNING)
7809 {
7810 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7811 rc = setError(E_FAIL,
7812 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7813 getName().c_str(), status.iStatus);
7814 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7815 rc = setError(E_FAIL,
7816 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7817 getName().c_str(), status.iStatus);
7818 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7819 rc = setError(E_FAIL,
7820 tr("The virtual machine '%s' has terminated abnormally"),
7821 getName().c_str(), status.iStatus);
7822 else
7823 rc = setError(E_FAIL,
7824 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7825 getName().c_str(), rc);
7826 }
7827
7828#endif
7829
7830 if (FAILED(rc))
7831 {
7832 /* Close the remote session, remove the remote control from the list
7833 * and reset session state to Closed (@note keep the code in sync with
7834 * the relevant part in checkForSpawnFailure()). */
7835
7836 Assert(mData->mSession.mRemoteControls.size() == 1);
7837 if (mData->mSession.mRemoteControls.size() == 1)
7838 {
7839 ErrorInfoKeeper eik;
7840 mData->mSession.mRemoteControls.front()->Uninitialize();
7841 }
7842
7843 mData->mSession.mRemoteControls.clear();
7844 mData->mSession.mState = SessionState_Unlocked;
7845
7846 /* finalize the progress after setting the state */
7847 if (!mData->mSession.mProgress.isNull())
7848 {
7849 mData->mSession.mProgress->notifyComplete(rc);
7850 mData->mSession.mProgress.setNull();
7851 }
7852
7853 mParent->addProcessToReap(mData->mSession.mPID);
7854 mData->mSession.mPID = NIL_RTPROCESS;
7855
7856 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7857 return true;
7858 }
7859
7860 return false;
7861}
7862
7863/**
7864 * Checks whether the machine can be registered. If so, commits and saves
7865 * all settings.
7866 *
7867 * @note Must be called from mParent's write lock. Locks this object and
7868 * children for writing.
7869 */
7870HRESULT Machine::prepareRegister()
7871{
7872 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7873
7874 AutoLimitedCaller autoCaller(this);
7875 AssertComRCReturnRC(autoCaller.rc());
7876
7877 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7878
7879 /* wait for state dependents to drop to zero */
7880 ensureNoStateDependencies();
7881
7882 if (!mData->mAccessible)
7883 return setError(VBOX_E_INVALID_OBJECT_STATE,
7884 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7885 mUserData->s.strName.c_str(),
7886 mData->mUuid.toString().c_str());
7887
7888 AssertReturn(autoCaller.state() == Ready, E_FAIL);
7889
7890 if (mData->mRegistered)
7891 return setError(VBOX_E_INVALID_OBJECT_STATE,
7892 tr("The machine '%s' with UUID {%s} is already registered"),
7893 mUserData->s.strName.c_str(),
7894 mData->mUuid.toString().c_str());
7895
7896 HRESULT rc = S_OK;
7897
7898 // Ensure the settings are saved. If we are going to be registered and
7899 // no config file exists yet, create it by calling saveSettings() too.
7900 if ( (mData->flModifications)
7901 || (!mData->pMachineConfigFile->fileExists())
7902 )
7903 {
7904 rc = saveSettings(NULL);
7905 // no need to check whether VirtualBox.xml needs saving too since
7906 // we can't have a machine XML file rename pending
7907 if (FAILED(rc)) return rc;
7908 }
7909
7910 /* more config checking goes here */
7911
7912 if (SUCCEEDED(rc))
7913 {
7914 /* we may have had implicit modifications we want to fix on success */
7915 commit();
7916
7917 mData->mRegistered = true;
7918 }
7919 else
7920 {
7921 /* we may have had implicit modifications we want to cancel on failure*/
7922 rollback(false /* aNotify */);
7923 }
7924
7925 return rc;
7926}
7927
7928/**
7929 * Increases the number of objects dependent on the machine state or on the
7930 * registered state. Guarantees that these two states will not change at least
7931 * until #releaseStateDependency() is called.
7932 *
7933 * Depending on the @a aDepType value, additional state checks may be made.
7934 * These checks will set extended error info on failure. See
7935 * #checkStateDependency() for more info.
7936 *
7937 * If this method returns a failure, the dependency is not added and the caller
7938 * is not allowed to rely on any particular machine state or registration state
7939 * value and may return the failed result code to the upper level.
7940 *
7941 * @param aDepType Dependency type to add.
7942 * @param aState Current machine state (NULL if not interested).
7943 * @param aRegistered Current registered state (NULL if not interested).
7944 *
7945 * @note Locks this object for writing.
7946 */
7947HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7948 MachineState_T *aState /* = NULL */,
7949 BOOL *aRegistered /* = NULL */)
7950{
7951 AutoCaller autoCaller(this);
7952 AssertComRCReturnRC(autoCaller.rc());
7953
7954 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7955
7956 HRESULT rc = checkStateDependency(aDepType);
7957 if (FAILED(rc)) return rc;
7958
7959 {
7960 if (mData->mMachineStateChangePending != 0)
7961 {
7962 /* ensureNoStateDependencies() is waiting for state dependencies to
7963 * drop to zero so don't add more. It may make sense to wait a bit
7964 * and retry before reporting an error (since the pending state
7965 * transition should be really quick) but let's just assert for
7966 * now to see if it ever happens on practice. */
7967
7968 AssertFailed();
7969
7970 return setError(E_ACCESSDENIED,
7971 tr("Machine state change is in progress. Please retry the operation later."));
7972 }
7973
7974 ++mData->mMachineStateDeps;
7975 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7976 }
7977
7978 if (aState)
7979 *aState = mData->mMachineState;
7980 if (aRegistered)
7981 *aRegistered = mData->mRegistered;
7982
7983 return S_OK;
7984}
7985
7986/**
7987 * Decreases the number of objects dependent on the machine state.
7988 * Must always complete the #addStateDependency() call after the state
7989 * dependency is no more necessary.
7990 */
7991void Machine::releaseStateDependency()
7992{
7993 AutoCaller autoCaller(this);
7994 AssertComRCReturnVoid(autoCaller.rc());
7995
7996 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7997
7998 /* releaseStateDependency() w/o addStateDependency()? */
7999 AssertReturnVoid(mData->mMachineStateDeps != 0);
8000 -- mData->mMachineStateDeps;
8001
8002 if (mData->mMachineStateDeps == 0)
8003 {
8004 /* inform ensureNoStateDependencies() that there are no more deps */
8005 if (mData->mMachineStateChangePending != 0)
8006 {
8007 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8008 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8009 }
8010 }
8011}
8012
8013Utf8Str Machine::getExtraData(const Utf8Str &strKey)
8014{
8015 /* start with nothing found */
8016 Utf8Str strResult("");
8017
8018 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8019
8020 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8021 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8022 // found:
8023 strResult = it->second; // source is a Utf8Str
8024
8025 return strResult;
8026}
8027
8028// protected methods
8029/////////////////////////////////////////////////////////////////////////////
8030
8031/**
8032 * Performs machine state checks based on the @a aDepType value. If a check
8033 * fails, this method will set extended error info, otherwise it will return
8034 * S_OK. It is supposed, that on failure, the caller will immediately return
8035 * the return value of this method to the upper level.
8036 *
8037 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8038 *
8039 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8040 * current state of this machine object allows to change settings of the
8041 * machine (i.e. the machine is not registered, or registered but not running
8042 * and not saved). It is useful to call this method from Machine setters
8043 * before performing any change.
8044 *
8045 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8046 * as for MutableStateDep except that if the machine is saved, S_OK is also
8047 * returned. This is useful in setters which allow changing machine
8048 * properties when it is in the saved state.
8049 *
8050 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
8051 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
8052 * Aborted).
8053 *
8054 * @param aDepType Dependency type to check.
8055 *
8056 * @note Non Machine based classes should use #addStateDependency() and
8057 * #releaseStateDependency() methods or the smart AutoStateDependency
8058 * template.
8059 *
8060 * @note This method must be called from under this object's read or write
8061 * lock.
8062 */
8063HRESULT Machine::checkStateDependency(StateDependency aDepType)
8064{
8065 switch (aDepType)
8066 {
8067 case AnyStateDep:
8068 {
8069 break;
8070 }
8071 case MutableStateDep:
8072 {
8073 if ( mData->mRegistered
8074 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8075 || ( mData->mMachineState != MachineState_Paused
8076 && mData->mMachineState != MachineState_Running
8077 && mData->mMachineState != MachineState_Aborted
8078 && mData->mMachineState != MachineState_Teleported
8079 && mData->mMachineState != MachineState_PoweredOff
8080 )
8081 )
8082 )
8083 return setError(VBOX_E_INVALID_VM_STATE,
8084 tr("The machine is not mutable (state is %s)"),
8085 Global::stringifyMachineState(mData->mMachineState));
8086 break;
8087 }
8088 case MutableOrSavedStateDep:
8089 {
8090 if ( mData->mRegistered
8091 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8092 || ( mData->mMachineState != MachineState_Paused
8093 && mData->mMachineState != MachineState_Running
8094 && mData->mMachineState != MachineState_Aborted
8095 && mData->mMachineState != MachineState_Teleported
8096 && mData->mMachineState != MachineState_Saved
8097 && mData->mMachineState != MachineState_PoweredOff
8098 )
8099 )
8100 )
8101 return setError(VBOX_E_INVALID_VM_STATE,
8102 tr("The machine is not mutable (state is %s)"),
8103 Global::stringifyMachineState(mData->mMachineState));
8104 break;
8105 }
8106 case OfflineStateDep:
8107 {
8108 if ( mData->mRegistered
8109 && ( !isSessionMachine()
8110 || ( mData->mMachineState != MachineState_PoweredOff
8111 && mData->mMachineState != MachineState_Saved
8112 && mData->mMachineState != MachineState_Aborted
8113 && mData->mMachineState != MachineState_Teleported
8114 )
8115 )
8116 )
8117 return setError(VBOX_E_INVALID_VM_STATE,
8118 tr("The machine is not offline (state is %s)"),
8119 Global::stringifyMachineState(mData->mMachineState));
8120 break;
8121 }
8122 }
8123
8124 return S_OK;
8125}
8126
8127/**
8128 * Helper to initialize all associated child objects and allocate data
8129 * structures.
8130 *
8131 * This method must be called as a part of the object's initialization procedure
8132 * (usually done in the #init() method).
8133 *
8134 * @note Must be called only from #init() or from #registeredInit().
8135 */
8136HRESULT Machine::initDataAndChildObjects()
8137{
8138 AutoCaller autoCaller(this);
8139 AssertComRCReturnRC(autoCaller.rc());
8140 AssertComRCReturn(autoCaller.state() == InInit ||
8141 autoCaller.state() == Limited, E_FAIL);
8142
8143 AssertReturn(!mData->mAccessible, E_FAIL);
8144
8145 /* allocate data structures */
8146 mSSData.allocate();
8147 mUserData.allocate();
8148 mHWData.allocate();
8149 mMediaData.allocate();
8150 mStorageControllers.allocate();
8151
8152 /* initialize mOSTypeId */
8153 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
8154
8155 /* create associated BIOS settings object */
8156 unconst(mBIOSSettings).createObject();
8157 mBIOSSettings->init(this);
8158
8159 /* create an associated VRDE object (default is disabled) */
8160 unconst(mVRDEServer).createObject();
8161 mVRDEServer->init(this);
8162
8163 /* create associated serial port objects */
8164 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8165 {
8166 unconst(mSerialPorts[slot]).createObject();
8167 mSerialPorts[slot]->init(this, slot);
8168 }
8169
8170 /* create associated parallel port objects */
8171 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8172 {
8173 unconst(mParallelPorts[slot]).createObject();
8174 mParallelPorts[slot]->init(this, slot);
8175 }
8176
8177 /* create the audio adapter object (always present, default is disabled) */
8178 unconst(mAudioAdapter).createObject();
8179 mAudioAdapter->init(this);
8180
8181 /* create the USB controller object (always present, default is disabled) */
8182 unconst(mUSBController).createObject();
8183 mUSBController->init(this);
8184
8185 /* create associated network adapter objects */
8186 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8187 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8188 {
8189 unconst(mNetworkAdapters[slot]).createObject();
8190 mNetworkAdapters[slot]->init(this, slot);
8191 }
8192
8193 /* create the bandwidth control */
8194 unconst(mBandwidthControl).createObject();
8195 mBandwidthControl->init(this);
8196
8197 return S_OK;
8198}
8199
8200/**
8201 * Helper to uninitialize all associated child objects and to free all data
8202 * structures.
8203 *
8204 * This method must be called as a part of the object's uninitialization
8205 * procedure (usually done in the #uninit() method).
8206 *
8207 * @note Must be called only from #uninit() or from #registeredInit().
8208 */
8209void Machine::uninitDataAndChildObjects()
8210{
8211 AutoCaller autoCaller(this);
8212 AssertComRCReturnVoid(autoCaller.rc());
8213 AssertComRCReturnVoid( autoCaller.state() == InUninit
8214 || autoCaller.state() == Limited);
8215
8216 /* tell all our other child objects we've been uninitialized */
8217 if (mBandwidthControl)
8218 {
8219 mBandwidthControl->uninit();
8220 unconst(mBandwidthControl).setNull();
8221 }
8222
8223 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8224 {
8225 if (mNetworkAdapters[slot])
8226 {
8227 mNetworkAdapters[slot]->uninit();
8228 unconst(mNetworkAdapters[slot]).setNull();
8229 }
8230 }
8231
8232 if (mUSBController)
8233 {
8234 mUSBController->uninit();
8235 unconst(mUSBController).setNull();
8236 }
8237
8238 if (mAudioAdapter)
8239 {
8240 mAudioAdapter->uninit();
8241 unconst(mAudioAdapter).setNull();
8242 }
8243
8244 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8245 {
8246 if (mParallelPorts[slot])
8247 {
8248 mParallelPorts[slot]->uninit();
8249 unconst(mParallelPorts[slot]).setNull();
8250 }
8251 }
8252
8253 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8254 {
8255 if (mSerialPorts[slot])
8256 {
8257 mSerialPorts[slot]->uninit();
8258 unconst(mSerialPorts[slot]).setNull();
8259 }
8260 }
8261
8262 if (mVRDEServer)
8263 {
8264 mVRDEServer->uninit();
8265 unconst(mVRDEServer).setNull();
8266 }
8267
8268 if (mBIOSSettings)
8269 {
8270 mBIOSSettings->uninit();
8271 unconst(mBIOSSettings).setNull();
8272 }
8273
8274 /* Deassociate media (only when a real Machine or a SnapshotMachine
8275 * instance is uninitialized; SessionMachine instances refer to real
8276 * Machine media). This is necessary for a clean re-initialization of
8277 * the VM after successfully re-checking the accessibility state. Note
8278 * that in case of normal Machine or SnapshotMachine uninitialization (as
8279 * a result of unregistering or deleting the snapshot), outdated media
8280 * attachments will already be uninitialized and deleted, so this
8281 * code will not affect them. */
8282 if ( !!mMediaData
8283 && (!isSessionMachine())
8284 )
8285 {
8286 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8287 it != mMediaData->mAttachments.end();
8288 ++it)
8289 {
8290 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8291 if (pMedium.isNull())
8292 continue;
8293 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8294 AssertComRC(rc);
8295 }
8296 }
8297
8298 if (!isSessionMachine() && !isSnapshotMachine())
8299 {
8300 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8301 if (mData->mFirstSnapshot)
8302 {
8303 // snapshots tree is protected by machine write lock; strictly
8304 // this isn't necessary here since we're deleting the entire
8305 // machine, but otherwise we assert in Snapshot::uninit()
8306 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8307 mData->mFirstSnapshot->uninit();
8308 mData->mFirstSnapshot.setNull();
8309 }
8310
8311 mData->mCurrentSnapshot.setNull();
8312 }
8313
8314 /* free data structures (the essential mData structure is not freed here
8315 * since it may be still in use) */
8316 mMediaData.free();
8317 mStorageControllers.free();
8318 mHWData.free();
8319 mUserData.free();
8320 mSSData.free();
8321}
8322
8323/**
8324 * Returns a pointer to the Machine object for this machine that acts like a
8325 * parent for complex machine data objects such as shared folders, etc.
8326 *
8327 * For primary Machine objects and for SnapshotMachine objects, returns this
8328 * object's pointer itself. For SessionMachine objects, returns the peer
8329 * (primary) machine pointer.
8330 */
8331Machine* Machine::getMachine()
8332{
8333 if (isSessionMachine())
8334 return (Machine*)mPeer;
8335 return this;
8336}
8337
8338/**
8339 * Makes sure that there are no machine state dependents. If necessary, waits
8340 * for the number of dependents to drop to zero.
8341 *
8342 * Make sure this method is called from under this object's write lock to
8343 * guarantee that no new dependents may be added when this method returns
8344 * control to the caller.
8345 *
8346 * @note Locks this object for writing. The lock will be released while waiting
8347 * (if necessary).
8348 *
8349 * @warning To be used only in methods that change the machine state!
8350 */
8351void Machine::ensureNoStateDependencies()
8352{
8353 AssertReturnVoid(isWriteLockOnCurrentThread());
8354
8355 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8356
8357 /* Wait for all state dependents if necessary */
8358 if (mData->mMachineStateDeps != 0)
8359 {
8360 /* lazy semaphore creation */
8361 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8362 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8363
8364 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8365 mData->mMachineStateDeps));
8366
8367 ++mData->mMachineStateChangePending;
8368
8369 /* reset the semaphore before waiting, the last dependent will signal
8370 * it */
8371 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8372
8373 alock.release();
8374
8375 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8376
8377 alock.acquire();
8378
8379 -- mData->mMachineStateChangePending;
8380 }
8381}
8382
8383/**
8384 * Changes the machine state and informs callbacks.
8385 *
8386 * This method is not intended to fail so it either returns S_OK or asserts (and
8387 * returns a failure).
8388 *
8389 * @note Locks this object for writing.
8390 */
8391HRESULT Machine::setMachineState(MachineState_T aMachineState)
8392{
8393 LogFlowThisFuncEnter();
8394 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8395
8396 AutoCaller autoCaller(this);
8397 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8398
8399 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8400
8401 /* wait for state dependents to drop to zero */
8402 ensureNoStateDependencies();
8403
8404 if (mData->mMachineState != aMachineState)
8405 {
8406 mData->mMachineState = aMachineState;
8407
8408 RTTimeNow(&mData->mLastStateChange);
8409
8410 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8411 }
8412
8413 LogFlowThisFuncLeave();
8414 return S_OK;
8415}
8416
8417/**
8418 * Searches for a shared folder with the given logical name
8419 * in the collection of shared folders.
8420 *
8421 * @param aName logical name of the shared folder
8422 * @param aSharedFolder where to return the found object
8423 * @param aSetError whether to set the error info if the folder is
8424 * not found
8425 * @return
8426 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8427 *
8428 * @note
8429 * must be called from under the object's lock!
8430 */
8431HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8432 ComObjPtr<SharedFolder> &aSharedFolder,
8433 bool aSetError /* = false */)
8434{
8435 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8436 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8437 it != mHWData->mSharedFolders.end();
8438 ++it)
8439 {
8440 SharedFolder *pSF = *it;
8441 AutoCaller autoCaller(pSF);
8442 if (pSF->getName() == aName)
8443 {
8444 aSharedFolder = pSF;
8445 rc = S_OK;
8446 break;
8447 }
8448 }
8449
8450 if (aSetError && FAILED(rc))
8451 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8452
8453 return rc;
8454}
8455
8456/**
8457 * Initializes all machine instance data from the given settings structures
8458 * from XML. The exception is the machine UUID which needs special handling
8459 * depending on the caller's use case, so the caller needs to set that herself.
8460 *
8461 * This gets called in several contexts during machine initialization:
8462 *
8463 * -- When machine XML exists on disk already and needs to be loaded into memory,
8464 * for example, from registeredInit() to load all registered machines on
8465 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8466 * attached to the machine should be part of some media registry already.
8467 *
8468 * -- During OVF import, when a machine config has been constructed from an
8469 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8470 * ensure that the media listed as attachments in the config (which have
8471 * been imported from the OVF) receive the correct registry ID.
8472 *
8473 * -- During VM cloning.
8474 *
8475 * @param config Machine settings from XML.
8476 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8477 * @return
8478 */
8479HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8480 const Guid *puuidRegistry)
8481{
8482 // copy name, description, OS type, teleporter, UTC etc.
8483 mUserData->s = config.machineUserData;
8484
8485 // look up the object by Id to check it is valid
8486 ComPtr<IGuestOSType> guestOSType;
8487 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8488 guestOSType.asOutParam());
8489 if (FAILED(rc)) return rc;
8490
8491 // stateFile (optional)
8492 if (config.strStateFile.isEmpty())
8493 mSSData->strStateFilePath.setNull();
8494 else
8495 {
8496 Utf8Str stateFilePathFull(config.strStateFile);
8497 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8498 if (RT_FAILURE(vrc))
8499 return setError(E_FAIL,
8500 tr("Invalid saved state file path '%s' (%Rrc)"),
8501 config.strStateFile.c_str(),
8502 vrc);
8503 mSSData->strStateFilePath = stateFilePathFull;
8504 }
8505
8506 // snapshot folder needs special processing so set it again
8507 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8508 if (FAILED(rc)) return rc;
8509
8510 /* Copy the extra data items (Not in any case config is already the same as
8511 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8512 * make sure the extra data map is copied). */
8513 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8514
8515 /* currentStateModified (optional, default is true) */
8516 mData->mCurrentStateModified = config.fCurrentStateModified;
8517
8518 mData->mLastStateChange = config.timeLastStateChange;
8519
8520 /*
8521 * note: all mUserData members must be assigned prior this point because
8522 * we need to commit changes in order to let mUserData be shared by all
8523 * snapshot machine instances.
8524 */
8525 mUserData.commitCopy();
8526
8527 // machine registry, if present (must be loaded before snapshots)
8528 if (config.canHaveOwnMediaRegistry())
8529 {
8530 // determine machine folder
8531 Utf8Str strMachineFolder = getSettingsFileFull();
8532 strMachineFolder.stripFilename();
8533 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8534 config.mediaRegistry,
8535 strMachineFolder);
8536 if (FAILED(rc)) return rc;
8537 }
8538
8539 /* Snapshot node (optional) */
8540 size_t cRootSnapshots;
8541 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8542 {
8543 // there must be only one root snapshot
8544 Assert(cRootSnapshots == 1);
8545
8546 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8547
8548 rc = loadSnapshot(snap,
8549 config.uuidCurrentSnapshot,
8550 NULL); // no parent == first snapshot
8551 if (FAILED(rc)) return rc;
8552 }
8553
8554 // hardware data
8555 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8556 if (FAILED(rc)) return rc;
8557
8558 // load storage controllers
8559 rc = loadStorageControllers(config.storageMachine,
8560 puuidRegistry,
8561 NULL /* puuidSnapshot */);
8562 if (FAILED(rc)) return rc;
8563
8564 /*
8565 * NOTE: the assignment below must be the last thing to do,
8566 * otherwise it will be not possible to change the settings
8567 * somewhere in the code above because all setters will be
8568 * blocked by checkStateDependency(MutableStateDep).
8569 */
8570
8571 /* set the machine state to Aborted or Saved when appropriate */
8572 if (config.fAborted)
8573 {
8574 mSSData->strStateFilePath.setNull();
8575
8576 /* no need to use setMachineState() during init() */
8577 mData->mMachineState = MachineState_Aborted;
8578 }
8579 else if (!mSSData->strStateFilePath.isEmpty())
8580 {
8581 /* no need to use setMachineState() during init() */
8582 mData->mMachineState = MachineState_Saved;
8583 }
8584
8585 // after loading settings, we are no longer different from the XML on disk
8586 mData->flModifications = 0;
8587
8588 return S_OK;
8589}
8590
8591/**
8592 * Recursively loads all snapshots starting from the given.
8593 *
8594 * @param aNode <Snapshot> node.
8595 * @param aCurSnapshotId Current snapshot ID from the settings file.
8596 * @param aParentSnapshot Parent snapshot.
8597 */
8598HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8599 const Guid &aCurSnapshotId,
8600 Snapshot *aParentSnapshot)
8601{
8602 AssertReturn(!isSnapshotMachine(), E_FAIL);
8603 AssertReturn(!isSessionMachine(), E_FAIL);
8604
8605 HRESULT rc = S_OK;
8606
8607 Utf8Str strStateFile;
8608 if (!data.strStateFile.isEmpty())
8609 {
8610 /* optional */
8611 strStateFile = data.strStateFile;
8612 int vrc = calculateFullPath(strStateFile, strStateFile);
8613 if (RT_FAILURE(vrc))
8614 return setError(E_FAIL,
8615 tr("Invalid saved state file path '%s' (%Rrc)"),
8616 strStateFile.c_str(),
8617 vrc);
8618 }
8619
8620 /* create a snapshot machine object */
8621 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8622 pSnapshotMachine.createObject();
8623 rc = pSnapshotMachine->initFromSettings(this,
8624 data.hardware,
8625 &data.debugging,
8626 &data.autostart,
8627 data.storage,
8628 data.uuid.ref(),
8629 strStateFile);
8630 if (FAILED(rc)) return rc;
8631
8632 /* create a snapshot object */
8633 ComObjPtr<Snapshot> pSnapshot;
8634 pSnapshot.createObject();
8635 /* initialize the snapshot */
8636 rc = pSnapshot->init(mParent, // VirtualBox object
8637 data.uuid,
8638 data.strName,
8639 data.strDescription,
8640 data.timestamp,
8641 pSnapshotMachine,
8642 aParentSnapshot);
8643 if (FAILED(rc)) return rc;
8644
8645 /* memorize the first snapshot if necessary */
8646 if (!mData->mFirstSnapshot)
8647 mData->mFirstSnapshot = pSnapshot;
8648
8649 /* memorize the current snapshot when appropriate */
8650 if ( !mData->mCurrentSnapshot
8651 && pSnapshot->getId() == aCurSnapshotId
8652 )
8653 mData->mCurrentSnapshot = pSnapshot;
8654
8655 // now create the children
8656 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8657 it != data.llChildSnapshots.end();
8658 ++it)
8659 {
8660 const settings::Snapshot &childData = *it;
8661 // recurse
8662 rc = loadSnapshot(childData,
8663 aCurSnapshotId,
8664 pSnapshot); // parent = the one we created above
8665 if (FAILED(rc)) return rc;
8666 }
8667
8668 return rc;
8669}
8670
8671/**
8672 * Loads settings into mHWData.
8673 *
8674 * @param data Reference to the hardware settings.
8675 * @param pDbg Pointer to the debugging settings.
8676 * @param pAutostart Pointer to the autostart settings.
8677 */
8678HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8679 const settings::Autostart *pAutostart)
8680{
8681 AssertReturn(!isSessionMachine(), E_FAIL);
8682
8683 HRESULT rc = S_OK;
8684
8685 try
8686 {
8687 /* The hardware version attribute (optional). */
8688 mHWData->mHWVersion = data.strVersion;
8689 mHWData->mHardwareUUID = data.uuid;
8690
8691 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8692 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8693 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8694 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8695 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8696 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8697 mHWData->mPAEEnabled = data.fPAE;
8698 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8699 mHWData->mLongMode = data.enmLongMode;
8700 mHWData->mCPUCount = data.cCPUs;
8701 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8702 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8703
8704 // cpu
8705 if (mHWData->mCPUHotPlugEnabled)
8706 {
8707 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8708 it != data.llCpus.end();
8709 ++it)
8710 {
8711 const settings::Cpu &cpu = *it;
8712
8713 mHWData->mCPUAttached[cpu.ulId] = true;
8714 }
8715 }
8716
8717 // cpuid leafs
8718 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8719 it != data.llCpuIdLeafs.end();
8720 ++it)
8721 {
8722 const settings::CpuIdLeaf &leaf = *it;
8723
8724 switch (leaf.ulId)
8725 {
8726 case 0x0:
8727 case 0x1:
8728 case 0x2:
8729 case 0x3:
8730 case 0x4:
8731 case 0x5:
8732 case 0x6:
8733 case 0x7:
8734 case 0x8:
8735 case 0x9:
8736 case 0xA:
8737 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8738 break;
8739
8740 case 0x80000000:
8741 case 0x80000001:
8742 case 0x80000002:
8743 case 0x80000003:
8744 case 0x80000004:
8745 case 0x80000005:
8746 case 0x80000006:
8747 case 0x80000007:
8748 case 0x80000008:
8749 case 0x80000009:
8750 case 0x8000000A:
8751 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8752 break;
8753
8754 default:
8755 /* just ignore */
8756 break;
8757 }
8758 }
8759
8760 mHWData->mMemorySize = data.ulMemorySizeMB;
8761 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8762
8763 // boot order
8764 for (size_t i = 0;
8765 i < RT_ELEMENTS(mHWData->mBootOrder);
8766 i++)
8767 {
8768 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8769 if (it == data.mapBootOrder.end())
8770 mHWData->mBootOrder[i] = DeviceType_Null;
8771 else
8772 mHWData->mBootOrder[i] = it->second;
8773 }
8774
8775 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8776 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8777 mHWData->mMonitorCount = data.cMonitors;
8778 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8779 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8780 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8781 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8782 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8783 mHWData->mVideoCaptureFile = data.strVideoCaptureFile;
8784 mHWData->mFirmwareType = data.firmwareType;
8785 mHWData->mPointingHIDType = data.pointingHIDType;
8786 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8787 mHWData->mChipsetType = data.chipsetType;
8788 mHWData->mEmulatedUSBWebcamEnabled = data.fEmulatedUSBWebcam;
8789 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8790 mHWData->mHPETEnabled = data.fHPETEnabled;
8791
8792 /* VRDEServer */
8793 rc = mVRDEServer->loadSettings(data.vrdeSettings);
8794 if (FAILED(rc)) return rc;
8795
8796 /* BIOS */
8797 rc = mBIOSSettings->loadSettings(data.biosSettings);
8798 if (FAILED(rc)) return rc;
8799
8800 // Bandwidth control (must come before network adapters)
8801 rc = mBandwidthControl->loadSettings(data.ioSettings);
8802 if (FAILED(rc)) return rc;
8803
8804 /* USB Controller */
8805 rc = mUSBController->loadSettings(data.usbController);
8806 if (FAILED(rc)) return rc;
8807
8808 // network adapters
8809 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8810 uint32_t oldCount = mNetworkAdapters.size();
8811 if (newCount > oldCount)
8812 {
8813 mNetworkAdapters.resize(newCount);
8814 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
8815 {
8816 unconst(mNetworkAdapters[slot]).createObject();
8817 mNetworkAdapters[slot]->init(this, slot);
8818 }
8819 }
8820 else if (newCount < oldCount)
8821 mNetworkAdapters.resize(newCount);
8822 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8823 it != data.llNetworkAdapters.end();
8824 ++it)
8825 {
8826 const settings::NetworkAdapter &nic = *it;
8827
8828 /* slot unicity is guaranteed by XML Schema */
8829 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8830 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
8831 if (FAILED(rc)) return rc;
8832 }
8833
8834 // serial ports
8835 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8836 it != data.llSerialPorts.end();
8837 ++it)
8838 {
8839 const settings::SerialPort &s = *it;
8840
8841 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8842 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
8843 if (FAILED(rc)) return rc;
8844 }
8845
8846 // parallel ports (optional)
8847 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8848 it != data.llParallelPorts.end();
8849 ++it)
8850 {
8851 const settings::ParallelPort &p = *it;
8852
8853 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8854 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
8855 if (FAILED(rc)) return rc;
8856 }
8857
8858 /* AudioAdapter */
8859 rc = mAudioAdapter->loadSettings(data.audioAdapter);
8860 if (FAILED(rc)) return rc;
8861
8862 /* Shared folders */
8863 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8864 it != data.llSharedFolders.end();
8865 ++it)
8866 {
8867 const settings::SharedFolder &sf = *it;
8868
8869 ComObjPtr<SharedFolder> sharedFolder;
8870 /* Check for double entries. Not allowed! */
8871 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8872 if (SUCCEEDED(rc))
8873 return setError(VBOX_E_OBJECT_IN_USE,
8874 tr("Shared folder named '%s' already exists"),
8875 sf.strName.c_str());
8876
8877 /* Create the new shared folder. Don't break on error. This will be
8878 * reported when the machine starts. */
8879 sharedFolder.createObject();
8880 rc = sharedFolder->init(getMachine(),
8881 sf.strName,
8882 sf.strHostPath,
8883 RT_BOOL(sf.fWritable),
8884 RT_BOOL(sf.fAutoMount),
8885 false /* fFailOnError */);
8886 if (FAILED(rc)) return rc;
8887 mHWData->mSharedFolders.push_back(sharedFolder);
8888 }
8889
8890 // Clipboard
8891 mHWData->mClipboardMode = data.clipboardMode;
8892
8893 // drag'n'drop
8894 mHWData->mDragAndDropMode = data.dragAndDropMode;
8895
8896 // guest settings
8897 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8898
8899 // IO settings
8900 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8901 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8902
8903 // Host PCI devices
8904 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8905 it != data.pciAttachments.end();
8906 ++it)
8907 {
8908 const settings::HostPCIDeviceAttachment &hpda = *it;
8909 ComObjPtr<PCIDeviceAttachment> pda;
8910
8911 pda.createObject();
8912 pda->loadSettings(this, hpda);
8913 mHWData->mPCIDeviceAssignments.push_back(pda);
8914 }
8915
8916 /*
8917 * (The following isn't really real hardware, but it lives in HWData
8918 * for reasons of convenience.)
8919 */
8920
8921#ifdef VBOX_WITH_GUEST_PROPS
8922 /* Guest properties (optional) */
8923 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8924 it != data.llGuestProperties.end();
8925 ++it)
8926 {
8927 const settings::GuestProperty &prop = *it;
8928 uint32_t fFlags = guestProp::NILFLAG;
8929 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8930 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8931 mHWData->mGuestProperties[prop.strName] = property;
8932 }
8933
8934 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8935#endif /* VBOX_WITH_GUEST_PROPS defined */
8936
8937 rc = loadDebugging(pDbg);
8938 if (FAILED(rc))
8939 return rc;
8940
8941 mHWData->mAutostart = *pAutostart;
8942
8943 /* default frontend */
8944 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8945 }
8946 catch(std::bad_alloc &)
8947 {
8948 return E_OUTOFMEMORY;
8949 }
8950
8951 AssertComRC(rc);
8952 return rc;
8953}
8954
8955/**
8956 * Called from Machine::loadHardware() to load the debugging settings of the
8957 * machine.
8958 *
8959 * @param pDbg Pointer to the settings.
8960 */
8961HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
8962{
8963 mHWData->mDebugging = *pDbg;
8964 /* no more processing currently required, this will probably change. */
8965 return S_OK;
8966}
8967
8968/**
8969 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
8970 *
8971 * @param data
8972 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8973 * @param puuidSnapshot
8974 * @return
8975 */
8976HRESULT Machine::loadStorageControllers(const settings::Storage &data,
8977 const Guid *puuidRegistry,
8978 const Guid *puuidSnapshot)
8979{
8980 AssertReturn(!isSessionMachine(), E_FAIL);
8981
8982 HRESULT rc = S_OK;
8983
8984 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8985 it != data.llStorageControllers.end();
8986 ++it)
8987 {
8988 const settings::StorageController &ctlData = *it;
8989
8990 ComObjPtr<StorageController> pCtl;
8991 /* Try to find one with the name first. */
8992 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8993 if (SUCCEEDED(rc))
8994 return setError(VBOX_E_OBJECT_IN_USE,
8995 tr("Storage controller named '%s' already exists"),
8996 ctlData.strName.c_str());
8997
8998 pCtl.createObject();
8999 rc = pCtl->init(this,
9000 ctlData.strName,
9001 ctlData.storageBus,
9002 ctlData.ulInstance,
9003 ctlData.fBootable);
9004 if (FAILED(rc)) return rc;
9005
9006 mStorageControllers->push_back(pCtl);
9007
9008 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9009 if (FAILED(rc)) return rc;
9010
9011 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9012 if (FAILED(rc)) return rc;
9013
9014 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9015 if (FAILED(rc)) return rc;
9016
9017 /* Set IDE emulation settings (only for AHCI controller). */
9018 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9019 {
9020 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9021 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9022 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9023 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9024 )
9025 return rc;
9026 }
9027
9028 /* Load the attached devices now. */
9029 rc = loadStorageDevices(pCtl,
9030 ctlData,
9031 puuidRegistry,
9032 puuidSnapshot);
9033 if (FAILED(rc)) return rc;
9034 }
9035
9036 return S_OK;
9037}
9038
9039/**
9040 * Called from loadStorageControllers for a controller's devices.
9041 *
9042 * @param aStorageController
9043 * @param data
9044 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9045 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9046 * @return
9047 */
9048HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
9049 const settings::StorageController &data,
9050 const Guid *puuidRegistry,
9051 const Guid *puuidSnapshot)
9052{
9053 HRESULT rc = S_OK;
9054
9055 /* paranoia: detect duplicate attachments */
9056 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9057 it != data.llAttachedDevices.end();
9058 ++it)
9059 {
9060 const settings::AttachedDevice &ad = *it;
9061
9062 for (settings::AttachedDevicesList::const_iterator it2 = it;
9063 it2 != data.llAttachedDevices.end();
9064 ++it2)
9065 {
9066 if (it == it2)
9067 continue;
9068
9069 const settings::AttachedDevice &ad2 = *it2;
9070
9071 if ( ad.lPort == ad2.lPort
9072 && ad.lDevice == ad2.lDevice)
9073 {
9074 return setError(E_FAIL,
9075 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9076 aStorageController->getName().c_str(),
9077 ad.lPort,
9078 ad.lDevice,
9079 mUserData->s.strName.c_str());
9080 }
9081 }
9082 }
9083
9084 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9085 it != data.llAttachedDevices.end();
9086 ++it)
9087 {
9088 const settings::AttachedDevice &dev = *it;
9089 ComObjPtr<Medium> medium;
9090
9091 switch (dev.deviceType)
9092 {
9093 case DeviceType_Floppy:
9094 case DeviceType_DVD:
9095 if (dev.strHostDriveSrc.isNotEmpty())
9096 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9097 else
9098 rc = mParent->findRemoveableMedium(dev.deviceType,
9099 dev.uuid,
9100 false /* fRefresh */,
9101 false /* aSetError */,
9102 medium);
9103 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9104 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9105 rc = S_OK;
9106 break;
9107
9108 case DeviceType_HardDisk:
9109 {
9110 /* find a hard disk by UUID */
9111 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9112 if (FAILED(rc))
9113 {
9114 if (isSnapshotMachine())
9115 {
9116 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9117 // so the user knows that the bad disk is in a snapshot somewhere
9118 com::ErrorInfo info;
9119 return setError(E_FAIL,
9120 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9121 puuidSnapshot->raw(),
9122 info.getText().raw());
9123 }
9124 else
9125 return rc;
9126 }
9127
9128 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9129
9130 if (medium->getType() == MediumType_Immutable)
9131 {
9132 if (isSnapshotMachine())
9133 return setError(E_FAIL,
9134 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9135 "of the virtual machine '%s' ('%s')"),
9136 medium->getLocationFull().c_str(),
9137 dev.uuid.raw(),
9138 puuidSnapshot->raw(),
9139 mUserData->s.strName.c_str(),
9140 mData->m_strConfigFileFull.c_str());
9141
9142 return setError(E_FAIL,
9143 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9144 medium->getLocationFull().c_str(),
9145 dev.uuid.raw(),
9146 mUserData->s.strName.c_str(),
9147 mData->m_strConfigFileFull.c_str());
9148 }
9149
9150 if (medium->getType() == MediumType_MultiAttach)
9151 {
9152 if (isSnapshotMachine())
9153 return setError(E_FAIL,
9154 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9155 "of the virtual machine '%s' ('%s')"),
9156 medium->getLocationFull().c_str(),
9157 dev.uuid.raw(),
9158 puuidSnapshot->raw(),
9159 mUserData->s.strName.c_str(),
9160 mData->m_strConfigFileFull.c_str());
9161
9162 return setError(E_FAIL,
9163 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9164 medium->getLocationFull().c_str(),
9165 dev.uuid.raw(),
9166 mUserData->s.strName.c_str(),
9167 mData->m_strConfigFileFull.c_str());
9168 }
9169
9170 if ( !isSnapshotMachine()
9171 && medium->getChildren().size() != 0
9172 )
9173 return setError(E_FAIL,
9174 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9175 "because it has %d differencing child hard disks"),
9176 medium->getLocationFull().c_str(),
9177 dev.uuid.raw(),
9178 mUserData->s.strName.c_str(),
9179 mData->m_strConfigFileFull.c_str(),
9180 medium->getChildren().size());
9181
9182 if (findAttachment(mMediaData->mAttachments,
9183 medium))
9184 return setError(E_FAIL,
9185 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9186 medium->getLocationFull().c_str(),
9187 dev.uuid.raw(),
9188 mUserData->s.strName.c_str(),
9189 mData->m_strConfigFileFull.c_str());
9190
9191 break;
9192 }
9193
9194 default:
9195 return setError(E_FAIL,
9196 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9197 medium->getLocationFull().c_str(),
9198 mUserData->s.strName.c_str(),
9199 mData->m_strConfigFileFull.c_str());
9200 }
9201
9202 if (FAILED(rc))
9203 break;
9204
9205 /* Bandwidth groups are loaded at this point. */
9206 ComObjPtr<BandwidthGroup> pBwGroup;
9207
9208 if (!dev.strBwGroup.isEmpty())
9209 {
9210 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9211 if (FAILED(rc))
9212 return setError(E_FAIL,
9213 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9214 medium->getLocationFull().c_str(),
9215 dev.strBwGroup.c_str(),
9216 mUserData->s.strName.c_str(),
9217 mData->m_strConfigFileFull.c_str());
9218 pBwGroup->reference();
9219 }
9220
9221 const Bstr controllerName = aStorageController->getName();
9222 ComObjPtr<MediumAttachment> pAttachment;
9223 pAttachment.createObject();
9224 rc = pAttachment->init(this,
9225 medium,
9226 controllerName,
9227 dev.lPort,
9228 dev.lDevice,
9229 dev.deviceType,
9230 false,
9231 dev.fPassThrough,
9232 dev.fTempEject,
9233 dev.fNonRotational,
9234 dev.fDiscard,
9235 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
9236 if (FAILED(rc)) break;
9237
9238 /* associate the medium with this machine and snapshot */
9239 if (!medium.isNull())
9240 {
9241 AutoCaller medCaller(medium);
9242 if (FAILED(medCaller.rc())) return medCaller.rc();
9243 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9244
9245 if (isSnapshotMachine())
9246 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
9247 else
9248 rc = medium->addBackReference(mData->mUuid);
9249 /* If the medium->addBackReference fails it sets an appropriate
9250 * error message, so no need to do any guesswork here. */
9251
9252 if (puuidRegistry)
9253 // caller wants registry ID to be set on all attached media (OVF import case)
9254 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9255 }
9256
9257 if (FAILED(rc))
9258 break;
9259
9260 /* back up mMediaData to let registeredInit() properly rollback on failure
9261 * (= limited accessibility) */
9262 setModified(IsModified_Storage);
9263 mMediaData.backup();
9264 mMediaData->mAttachments.push_back(pAttachment);
9265 }
9266
9267 return rc;
9268}
9269
9270/**
9271 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9272 *
9273 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9274 * @param aSnapshot where to return the found snapshot
9275 * @param aSetError true to set extended error info on failure
9276 */
9277HRESULT Machine::findSnapshotById(const Guid &aId,
9278 ComObjPtr<Snapshot> &aSnapshot,
9279 bool aSetError /* = false */)
9280{
9281 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9282
9283 if (!mData->mFirstSnapshot)
9284 {
9285 if (aSetError)
9286 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9287 return E_FAIL;
9288 }
9289
9290 if (aId.isZero())
9291 aSnapshot = mData->mFirstSnapshot;
9292 else
9293 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9294
9295 if (!aSnapshot)
9296 {
9297 if (aSetError)
9298 return setError(E_FAIL,
9299 tr("Could not find a snapshot with UUID {%s}"),
9300 aId.toString().c_str());
9301 return E_FAIL;
9302 }
9303
9304 return S_OK;
9305}
9306
9307/**
9308 * Returns the snapshot with the given name or fails of no such snapshot.
9309 *
9310 * @param aName snapshot name to find
9311 * @param aSnapshot where to return the found snapshot
9312 * @param aSetError true to set extended error info on failure
9313 */
9314HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9315 ComObjPtr<Snapshot> &aSnapshot,
9316 bool aSetError /* = false */)
9317{
9318 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9319
9320 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9321
9322 if (!mData->mFirstSnapshot)
9323 {
9324 if (aSetError)
9325 return setError(VBOX_E_OBJECT_NOT_FOUND,
9326 tr("This machine does not have any snapshots"));
9327 return VBOX_E_OBJECT_NOT_FOUND;
9328 }
9329
9330 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9331
9332 if (!aSnapshot)
9333 {
9334 if (aSetError)
9335 return setError(VBOX_E_OBJECT_NOT_FOUND,
9336 tr("Could not find a snapshot named '%s'"), strName.c_str());
9337 return VBOX_E_OBJECT_NOT_FOUND;
9338 }
9339
9340 return S_OK;
9341}
9342
9343/**
9344 * Returns a storage controller object with the given name.
9345 *
9346 * @param aName storage controller name to find
9347 * @param aStorageController where to return the found storage controller
9348 * @param aSetError true to set extended error info on failure
9349 */
9350HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9351 ComObjPtr<StorageController> &aStorageController,
9352 bool aSetError /* = false */)
9353{
9354 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9355
9356 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9357 it != mStorageControllers->end();
9358 ++it)
9359 {
9360 if ((*it)->getName() == aName)
9361 {
9362 aStorageController = (*it);
9363 return S_OK;
9364 }
9365 }
9366
9367 if (aSetError)
9368 return setError(VBOX_E_OBJECT_NOT_FOUND,
9369 tr("Could not find a storage controller named '%s'"),
9370 aName.c_str());
9371 return VBOX_E_OBJECT_NOT_FOUND;
9372}
9373
9374HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9375 MediaData::AttachmentList &atts)
9376{
9377 AutoCaller autoCaller(this);
9378 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9379
9380 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9381
9382 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9383 it != mMediaData->mAttachments.end();
9384 ++it)
9385 {
9386 const ComObjPtr<MediumAttachment> &pAtt = *it;
9387
9388 // should never happen, but deal with NULL pointers in the list.
9389 AssertStmt(!pAtt.isNull(), continue);
9390
9391 // getControllerName() needs caller+read lock
9392 AutoCaller autoAttCaller(pAtt);
9393 if (FAILED(autoAttCaller.rc()))
9394 {
9395 atts.clear();
9396 return autoAttCaller.rc();
9397 }
9398 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9399
9400 if (pAtt->getControllerName() == aName)
9401 atts.push_back(pAtt);
9402 }
9403
9404 return S_OK;
9405}
9406
9407/**
9408 * Helper for #saveSettings. Cares about renaming the settings directory and
9409 * file if the machine name was changed and about creating a new settings file
9410 * if this is a new machine.
9411 *
9412 * @note Must be never called directly but only from #saveSettings().
9413 */
9414HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9415{
9416 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9417
9418 HRESULT rc = S_OK;
9419
9420 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9421
9422 /// @todo need to handle primary group change, too
9423
9424 /* attempt to rename the settings file if machine name is changed */
9425 if ( mUserData->s.fNameSync
9426 && mUserData.isBackedUp()
9427 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9428 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9429 )
9430 {
9431 bool dirRenamed = false;
9432 bool fileRenamed = false;
9433
9434 Utf8Str configFile, newConfigFile;
9435 Utf8Str configFilePrev, newConfigFilePrev;
9436 Utf8Str configDir, newConfigDir;
9437
9438 do
9439 {
9440 int vrc = VINF_SUCCESS;
9441
9442 Utf8Str name = mUserData.backedUpData()->s.strName;
9443 Utf8Str newName = mUserData->s.strName;
9444 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9445 if (group == "/")
9446 group.setNull();
9447 Utf8Str newGroup = mUserData->s.llGroups.front();
9448 if (newGroup == "/")
9449 newGroup.setNull();
9450
9451 configFile = mData->m_strConfigFileFull;
9452
9453 /* first, rename the directory if it matches the group and machine name */
9454 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9455 group.c_str(), RTPATH_DELIMITER, name.c_str());
9456 /** @todo hack, make somehow use of ComposeMachineFilename */
9457 if (mUserData->s.fDirectoryIncludesUUID)
9458 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9459 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9460 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9461 /** @todo hack, make somehow use of ComposeMachineFilename */
9462 if (mUserData->s.fDirectoryIncludesUUID)
9463 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9464 configDir = configFile;
9465 configDir.stripFilename();
9466 newConfigDir = configDir;
9467 if ( configDir.length() >= groupPlusName.length()
9468 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9469 {
9470 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9471 Utf8Str newConfigBaseDir(newConfigDir);
9472 newConfigDir.append(newGroupPlusName);
9473 /* consistency: use \ if appropriate on the platform */
9474 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9475 /* new dir and old dir cannot be equal here because of 'if'
9476 * above and because name != newName */
9477 Assert(configDir != newConfigDir);
9478 if (!fSettingsFileIsNew)
9479 {
9480 /* perform real rename only if the machine is not new */
9481 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9482 if ( vrc == VERR_FILE_NOT_FOUND
9483 || vrc == VERR_PATH_NOT_FOUND)
9484 {
9485 /* create the parent directory, then retry renaming */
9486 Utf8Str parent(newConfigDir);
9487 parent.stripFilename();
9488 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9489 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9490 }
9491 if (RT_FAILURE(vrc))
9492 {
9493 rc = setError(E_FAIL,
9494 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9495 configDir.c_str(),
9496 newConfigDir.c_str(),
9497 vrc);
9498 break;
9499 }
9500 /* delete subdirectories which are no longer needed */
9501 Utf8Str dir(configDir);
9502 dir.stripFilename();
9503 while (dir != newConfigBaseDir && dir != ".")
9504 {
9505 vrc = RTDirRemove(dir.c_str());
9506 if (RT_FAILURE(vrc))
9507 break;
9508 dir.stripFilename();
9509 }
9510 dirRenamed = true;
9511 }
9512 }
9513
9514 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9515 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9516
9517 /* then try to rename the settings file itself */
9518 if (newConfigFile != configFile)
9519 {
9520 /* get the path to old settings file in renamed directory */
9521 configFile = Utf8StrFmt("%s%c%s",
9522 newConfigDir.c_str(),
9523 RTPATH_DELIMITER,
9524 RTPathFilename(configFile.c_str()));
9525 if (!fSettingsFileIsNew)
9526 {
9527 /* perform real rename only if the machine is not new */
9528 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9529 if (RT_FAILURE(vrc))
9530 {
9531 rc = setError(E_FAIL,
9532 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9533 configFile.c_str(),
9534 newConfigFile.c_str(),
9535 vrc);
9536 break;
9537 }
9538 fileRenamed = true;
9539 configFilePrev = configFile;
9540 configFilePrev += "-prev";
9541 newConfigFilePrev = newConfigFile;
9542 newConfigFilePrev += "-prev";
9543 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9544 }
9545 }
9546
9547 // update m_strConfigFileFull amd mConfigFile
9548 mData->m_strConfigFileFull = newConfigFile;
9549 // compute the relative path too
9550 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9551
9552 // store the old and new so that VirtualBox::saveSettings() can update
9553 // the media registry
9554 if ( mData->mRegistered
9555 && configDir != newConfigDir)
9556 {
9557 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9558
9559 if (pfNeedsGlobalSaveSettings)
9560 *pfNeedsGlobalSaveSettings = true;
9561 }
9562
9563 // in the saved state file path, replace the old directory with the new directory
9564 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9565 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9566
9567 // and do the same thing for the saved state file paths of all the online snapshots
9568 if (mData->mFirstSnapshot)
9569 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9570 newConfigDir.c_str());
9571 }
9572 while (0);
9573
9574 if (FAILED(rc))
9575 {
9576 /* silently try to rename everything back */
9577 if (fileRenamed)
9578 {
9579 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9580 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9581 }
9582 if (dirRenamed)
9583 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9584 }
9585
9586 if (FAILED(rc)) return rc;
9587 }
9588
9589 if (fSettingsFileIsNew)
9590 {
9591 /* create a virgin config file */
9592 int vrc = VINF_SUCCESS;
9593
9594 /* ensure the settings directory exists */
9595 Utf8Str path(mData->m_strConfigFileFull);
9596 path.stripFilename();
9597 if (!RTDirExists(path.c_str()))
9598 {
9599 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9600 if (RT_FAILURE(vrc))
9601 {
9602 return setError(E_FAIL,
9603 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9604 path.c_str(),
9605 vrc);
9606 }
9607 }
9608
9609 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9610 path = Utf8Str(mData->m_strConfigFileFull);
9611 RTFILE f = NIL_RTFILE;
9612 vrc = RTFileOpen(&f, path.c_str(),
9613 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9614 if (RT_FAILURE(vrc))
9615 return setError(E_FAIL,
9616 tr("Could not create the settings file '%s' (%Rrc)"),
9617 path.c_str(),
9618 vrc);
9619 RTFileClose(f);
9620 }
9621
9622 return rc;
9623}
9624
9625/**
9626 * Saves and commits machine data, user data and hardware data.
9627 *
9628 * Note that on failure, the data remains uncommitted.
9629 *
9630 * @a aFlags may combine the following flags:
9631 *
9632 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9633 * Used when saving settings after an operation that makes them 100%
9634 * correspond to the settings from the current snapshot.
9635 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9636 * #isReallyModified() returns false. This is necessary for cases when we
9637 * change machine data directly, not through the backup()/commit() mechanism.
9638 * - SaveS_Force: settings will be saved without doing a deep compare of the
9639 * settings structures. This is used when this is called because snapshots
9640 * have changed to avoid the overhead of the deep compare.
9641 *
9642 * @note Must be called from under this object's write lock. Locks children for
9643 * writing.
9644 *
9645 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9646 * initialized to false and that will be set to true by this function if
9647 * the caller must invoke VirtualBox::saveSettings() because the global
9648 * settings have changed. This will happen if a machine rename has been
9649 * saved and the global machine and media registries will therefore need
9650 * updating.
9651 */
9652HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9653 int aFlags /*= 0*/)
9654{
9655 LogFlowThisFuncEnter();
9656
9657 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9658
9659 /* make sure child objects are unable to modify the settings while we are
9660 * saving them */
9661 ensureNoStateDependencies();
9662
9663 AssertReturn(!isSnapshotMachine(),
9664 E_FAIL);
9665
9666 HRESULT rc = S_OK;
9667 bool fNeedsWrite = false;
9668
9669 /* First, prepare to save settings. It will care about renaming the
9670 * settings directory and file if the machine name was changed and about
9671 * creating a new settings file if this is a new machine. */
9672 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9673 if (FAILED(rc)) return rc;
9674
9675 // keep a pointer to the current settings structures
9676 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9677 settings::MachineConfigFile *pNewConfig = NULL;
9678
9679 try
9680 {
9681 // make a fresh one to have everyone write stuff into
9682 pNewConfig = new settings::MachineConfigFile(NULL);
9683 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9684
9685 // now go and copy all the settings data from COM to the settings structures
9686 // (this calles saveSettings() on all the COM objects in the machine)
9687 copyMachineDataToSettings(*pNewConfig);
9688
9689 if (aFlags & SaveS_ResetCurStateModified)
9690 {
9691 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9692 mData->mCurrentStateModified = FALSE;
9693 fNeedsWrite = true; // always, no need to compare
9694 }
9695 else if (aFlags & SaveS_Force)
9696 {
9697 fNeedsWrite = true; // always, no need to compare
9698 }
9699 else
9700 {
9701 if (!mData->mCurrentStateModified)
9702 {
9703 // do a deep compare of the settings that we just saved with the settings
9704 // previously stored in the config file; this invokes MachineConfigFile::operator==
9705 // which does a deep compare of all the settings, which is expensive but less expensive
9706 // than writing out XML in vain
9707 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9708
9709 // could still be modified if any settings changed
9710 mData->mCurrentStateModified = fAnySettingsChanged;
9711
9712 fNeedsWrite = fAnySettingsChanged;
9713 }
9714 else
9715 fNeedsWrite = true;
9716 }
9717
9718 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9719
9720 if (fNeedsWrite)
9721 // now spit it all out!
9722 pNewConfig->write(mData->m_strConfigFileFull);
9723
9724 mData->pMachineConfigFile = pNewConfig;
9725 delete pOldConfig;
9726 commit();
9727
9728 // after saving settings, we are no longer different from the XML on disk
9729 mData->flModifications = 0;
9730 }
9731 catch (HRESULT err)
9732 {
9733 // we assume that error info is set by the thrower
9734 rc = err;
9735
9736 // restore old config
9737 delete pNewConfig;
9738 mData->pMachineConfigFile = pOldConfig;
9739 }
9740 catch (...)
9741 {
9742 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9743 }
9744
9745 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9746 {
9747 /* Fire the data change event, even on failure (since we've already
9748 * committed all data). This is done only for SessionMachines because
9749 * mutable Machine instances are always not registered (i.e. private
9750 * to the client process that creates them) and thus don't need to
9751 * inform callbacks. */
9752 if (isSessionMachine())
9753 mParent->onMachineDataChange(mData->mUuid);
9754 }
9755
9756 LogFlowThisFunc(("rc=%08X\n", rc));
9757 LogFlowThisFuncLeave();
9758 return rc;
9759}
9760
9761/**
9762 * Implementation for saving the machine settings into the given
9763 * settings::MachineConfigFile instance. This copies machine extradata
9764 * from the previous machine config file in the instance data, if any.
9765 *
9766 * This gets called from two locations:
9767 *
9768 * -- Machine::saveSettings(), during the regular XML writing;
9769 *
9770 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9771 * exported to OVF and we write the VirtualBox proprietary XML
9772 * into a <vbox:Machine> tag.
9773 *
9774 * This routine fills all the fields in there, including snapshots, *except*
9775 * for the following:
9776 *
9777 * -- fCurrentStateModified. There is some special logic associated with that.
9778 *
9779 * The caller can then call MachineConfigFile::write() or do something else
9780 * with it.
9781 *
9782 * Caller must hold the machine lock!
9783 *
9784 * This throws XML errors and HRESULT, so the caller must have a catch block!
9785 */
9786void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
9787{
9788 // deep copy extradata
9789 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9790
9791 config.uuid = mData->mUuid;
9792
9793 // copy name, description, OS type, teleport, UTC etc.
9794 config.machineUserData = mUserData->s;
9795
9796 if ( mData->mMachineState == MachineState_Saved
9797 || mData->mMachineState == MachineState_Restoring
9798 // when deleting a snapshot we may or may not have a saved state in the current state,
9799 // so let's not assert here please
9800 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9801 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9802 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9803 && (!mSSData->strStateFilePath.isEmpty())
9804 )
9805 )
9806 {
9807 Assert(!mSSData->strStateFilePath.isEmpty());
9808 /* try to make the file name relative to the settings file dir */
9809 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9810 }
9811 else
9812 {
9813 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9814 config.strStateFile.setNull();
9815 }
9816
9817 if (mData->mCurrentSnapshot)
9818 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
9819 else
9820 config.uuidCurrentSnapshot.clear();
9821
9822 config.timeLastStateChange = mData->mLastStateChange;
9823 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9824 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9825
9826 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9827 if (FAILED(rc)) throw rc;
9828
9829 rc = saveStorageControllers(config.storageMachine);
9830 if (FAILED(rc)) throw rc;
9831
9832 // save machine's media registry if this is VirtualBox 4.0 or later
9833 if (config.canHaveOwnMediaRegistry())
9834 {
9835 // determine machine folder
9836 Utf8Str strMachineFolder = getSettingsFileFull();
9837 strMachineFolder.stripFilename();
9838 mParent->saveMediaRegistry(config.mediaRegistry,
9839 getId(), // only media with registry ID == machine UUID
9840 strMachineFolder);
9841 // this throws HRESULT
9842 }
9843
9844 // save snapshots
9845 rc = saveAllSnapshots(config);
9846 if (FAILED(rc)) throw rc;
9847}
9848
9849/**
9850 * Saves all snapshots of the machine into the given machine config file. Called
9851 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9852 * @param config
9853 * @return
9854 */
9855HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
9856{
9857 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9858
9859 HRESULT rc = S_OK;
9860
9861 try
9862 {
9863 config.llFirstSnapshot.clear();
9864
9865 if (mData->mFirstSnapshot)
9866 {
9867 settings::Snapshot snapNew;
9868 config.llFirstSnapshot.push_back(snapNew);
9869
9870 // get reference to the fresh copy of the snapshot on the list and
9871 // work on that copy directly to avoid excessive copying later
9872 settings::Snapshot &snap = config.llFirstSnapshot.front();
9873
9874 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
9875 if (FAILED(rc)) throw rc;
9876 }
9877
9878// if (mType == IsSessionMachine)
9879// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9880
9881 }
9882 catch (HRESULT err)
9883 {
9884 /* we assume that error info is set by the thrower */
9885 rc = err;
9886 }
9887 catch (...)
9888 {
9889 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9890 }
9891
9892 return rc;
9893}
9894
9895/**
9896 * Saves the VM hardware configuration. It is assumed that the
9897 * given node is empty.
9898 *
9899 * @param data Reference to the settings object for the hardware config.
9900 * @param pDbg Pointer to the settings object for the debugging config
9901 * which happens to live in mHWData.
9902 * @param pAutostart Pointer to the settings object for the autostart config
9903 * which happens to live in mHWData.
9904 */
9905HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9906 settings::Autostart *pAutostart)
9907{
9908 HRESULT rc = S_OK;
9909
9910 try
9911 {
9912 /* The hardware version attribute (optional).
9913 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9914 if ( mHWData->mHWVersion == "1"
9915 && mSSData->strStateFilePath.isEmpty()
9916 )
9917 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. */
9918
9919 data.strVersion = mHWData->mHWVersion;
9920 data.uuid = mHWData->mHardwareUUID;
9921
9922 // CPU
9923 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9924 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
9925 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9926 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9927 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9928 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9929 data.fPAE = !!mHWData->mPAEEnabled;
9930 data.enmLongMode = mHWData->mLongMode;
9931 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9932
9933 /* Standard and Extended CPUID leafs. */
9934 data.llCpuIdLeafs.clear();
9935 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
9936 {
9937 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9938 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9939 }
9940 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
9941 {
9942 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9943 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9944 }
9945
9946 data.cCPUs = mHWData->mCPUCount;
9947 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9948 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9949
9950 data.llCpus.clear();
9951 if (data.fCpuHotPlug)
9952 {
9953 for (unsigned idx = 0; idx < data.cCPUs; idx++)
9954 {
9955 if (mHWData->mCPUAttached[idx])
9956 {
9957 settings::Cpu cpu;
9958 cpu.ulId = idx;
9959 data.llCpus.push_back(cpu);
9960 }
9961 }
9962 }
9963
9964 // memory
9965 data.ulMemorySizeMB = mHWData->mMemorySize;
9966 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9967
9968 // firmware
9969 data.firmwareType = mHWData->mFirmwareType;
9970
9971 // HID
9972 data.pointingHIDType = mHWData->mPointingHIDType;
9973 data.keyboardHIDType = mHWData->mKeyboardHIDType;
9974
9975 // chipset
9976 data.chipsetType = mHWData->mChipsetType;
9977
9978 data.fEmulatedUSBWebcam = !!mHWData->mEmulatedUSBWebcamEnabled;
9979 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9980
9981 // HPET
9982 data.fHPETEnabled = !!mHWData->mHPETEnabled;
9983
9984 // boot order
9985 data.mapBootOrder.clear();
9986 for (size_t i = 0;
9987 i < RT_ELEMENTS(mHWData->mBootOrder);
9988 ++i)
9989 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9990
9991 // display
9992 data.graphicsControllerType = mHWData->mGraphicsControllerType;
9993 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9994 data.cMonitors = mHWData->mMonitorCount;
9995 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9996 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9997 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
9998 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
9999 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10000 data.strVideoCaptureFile = mHWData->mVideoCaptureFile;
10001
10002 /* VRDEServer settings (optional) */
10003 rc = mVRDEServer->saveSettings(data.vrdeSettings);
10004 if (FAILED(rc)) throw rc;
10005
10006 /* BIOS (required) */
10007 rc = mBIOSSettings->saveSettings(data.biosSettings);
10008 if (FAILED(rc)) throw rc;
10009
10010 /* USB Controller (required) */
10011 rc = mUSBController->saveSettings(data.usbController);
10012 if (FAILED(rc)) throw rc;
10013
10014 /* Network adapters (required) */
10015 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10016 data.llNetworkAdapters.clear();
10017 /* Write out only the nominal number of network adapters for this
10018 * chipset type. Since Machine::commit() hasn't been called there
10019 * may be extra NIC settings in the vector. */
10020 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10021 {
10022 settings::NetworkAdapter nic;
10023 nic.ulSlot = slot;
10024 /* paranoia check... must not be NULL, but must not crash either. */
10025 if (mNetworkAdapters[slot])
10026 {
10027 rc = mNetworkAdapters[slot]->saveSettings(nic);
10028 if (FAILED(rc)) throw rc;
10029
10030 data.llNetworkAdapters.push_back(nic);
10031 }
10032 }
10033
10034 /* Serial ports */
10035 data.llSerialPorts.clear();
10036 for (ULONG slot = 0;
10037 slot < RT_ELEMENTS(mSerialPorts);
10038 ++slot)
10039 {
10040 settings::SerialPort s;
10041 s.ulSlot = slot;
10042 rc = mSerialPorts[slot]->saveSettings(s);
10043 if (FAILED(rc)) return rc;
10044
10045 data.llSerialPorts.push_back(s);
10046 }
10047
10048 /* Parallel ports */
10049 data.llParallelPorts.clear();
10050 for (ULONG slot = 0;
10051 slot < RT_ELEMENTS(mParallelPorts);
10052 ++slot)
10053 {
10054 settings::ParallelPort p;
10055 p.ulSlot = slot;
10056 rc = mParallelPorts[slot]->saveSettings(p);
10057 if (FAILED(rc)) return rc;
10058
10059 data.llParallelPorts.push_back(p);
10060 }
10061
10062 /* Audio adapter */
10063 rc = mAudioAdapter->saveSettings(data.audioAdapter);
10064 if (FAILED(rc)) return rc;
10065
10066 /* Shared folders */
10067 data.llSharedFolders.clear();
10068 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10069 it != mHWData->mSharedFolders.end();
10070 ++it)
10071 {
10072 SharedFolder *pSF = *it;
10073 AutoCaller sfCaller(pSF);
10074 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10075 settings::SharedFolder sf;
10076 sf.strName = pSF->getName();
10077 sf.strHostPath = pSF->getHostPath();
10078 sf.fWritable = !!pSF->isWritable();
10079 sf.fAutoMount = !!pSF->isAutoMounted();
10080
10081 data.llSharedFolders.push_back(sf);
10082 }
10083
10084 // clipboard
10085 data.clipboardMode = mHWData->mClipboardMode;
10086
10087 // drag'n'drop
10088 data.dragAndDropMode = mHWData->mDragAndDropMode;
10089
10090 /* Guest */
10091 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10092
10093 // IO settings
10094 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10095 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10096
10097 /* BandwidthControl (required) */
10098 rc = mBandwidthControl->saveSettings(data.ioSettings);
10099 if (FAILED(rc)) throw rc;
10100
10101 /* Host PCI devices */
10102 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10103 it != mHWData->mPCIDeviceAssignments.end();
10104 ++it)
10105 {
10106 ComObjPtr<PCIDeviceAttachment> pda = *it;
10107 settings::HostPCIDeviceAttachment hpda;
10108
10109 rc = pda->saveSettings(hpda);
10110 if (FAILED(rc)) throw rc;
10111
10112 data.pciAttachments.push_back(hpda);
10113 }
10114
10115
10116 // guest properties
10117 data.llGuestProperties.clear();
10118#ifdef VBOX_WITH_GUEST_PROPS
10119 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10120 it != mHWData->mGuestProperties.end();
10121 ++it)
10122 {
10123 HWData::GuestProperty property = it->second;
10124
10125 /* Remove transient guest properties at shutdown unless we
10126 * are saving state */
10127 if ( ( mData->mMachineState == MachineState_PoweredOff
10128 || mData->mMachineState == MachineState_Aborted
10129 || mData->mMachineState == MachineState_Teleported)
10130 && ( property.mFlags & guestProp::TRANSIENT
10131 || property.mFlags & guestProp::TRANSRESET))
10132 continue;
10133 settings::GuestProperty prop;
10134 prop.strName = it->first;
10135 prop.strValue = property.strValue;
10136 prop.timestamp = property.mTimestamp;
10137 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10138 guestProp::writeFlags(property.mFlags, szFlags);
10139 prop.strFlags = szFlags;
10140
10141 data.llGuestProperties.push_back(prop);
10142 }
10143
10144 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10145 /* I presume this doesn't require a backup(). */
10146 mData->mGuestPropertiesModified = FALSE;
10147#endif /* VBOX_WITH_GUEST_PROPS defined */
10148
10149 *pDbg = mHWData->mDebugging;
10150 *pAutostart = mHWData->mAutostart;
10151
10152 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10153 }
10154 catch(std::bad_alloc &)
10155 {
10156 return E_OUTOFMEMORY;
10157 }
10158
10159 AssertComRC(rc);
10160 return rc;
10161}
10162
10163/**
10164 * Saves the storage controller configuration.
10165 *
10166 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10167 */
10168HRESULT Machine::saveStorageControllers(settings::Storage &data)
10169{
10170 data.llStorageControllers.clear();
10171
10172 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10173 it != mStorageControllers->end();
10174 ++it)
10175 {
10176 HRESULT rc;
10177 ComObjPtr<StorageController> pCtl = *it;
10178
10179 settings::StorageController ctl;
10180 ctl.strName = pCtl->getName();
10181 ctl.controllerType = pCtl->getControllerType();
10182 ctl.storageBus = pCtl->getStorageBus();
10183 ctl.ulInstance = pCtl->getInstance();
10184 ctl.fBootable = pCtl->getBootable();
10185
10186 /* Save the port count. */
10187 ULONG portCount;
10188 rc = pCtl->COMGETTER(PortCount)(&portCount);
10189 ComAssertComRCRet(rc, rc);
10190 ctl.ulPortCount = portCount;
10191
10192 /* Save fUseHostIOCache */
10193 BOOL fUseHostIOCache;
10194 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10195 ComAssertComRCRet(rc, rc);
10196 ctl.fUseHostIOCache = !!fUseHostIOCache;
10197
10198 /* Save IDE emulation settings. */
10199 if (ctl.controllerType == StorageControllerType_IntelAhci)
10200 {
10201 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10202 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10203 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10204 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10205 )
10206 ComAssertComRCRet(rc, rc);
10207 }
10208
10209 /* save the devices now. */
10210 rc = saveStorageDevices(pCtl, ctl);
10211 ComAssertComRCRet(rc, rc);
10212
10213 data.llStorageControllers.push_back(ctl);
10214 }
10215
10216 return S_OK;
10217}
10218
10219/**
10220 * Saves the hard disk configuration.
10221 */
10222HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10223 settings::StorageController &data)
10224{
10225 MediaData::AttachmentList atts;
10226
10227 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
10228 if (FAILED(rc)) return rc;
10229
10230 data.llAttachedDevices.clear();
10231 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10232 it != atts.end();
10233 ++it)
10234 {
10235 settings::AttachedDevice dev;
10236
10237 MediumAttachment *pAttach = *it;
10238 Medium *pMedium = pAttach->getMedium();
10239
10240 dev.deviceType = pAttach->getType();
10241 dev.lPort = pAttach->getPort();
10242 dev.lDevice = pAttach->getDevice();
10243 if (pMedium)
10244 {
10245 if (pMedium->isHostDrive())
10246 dev.strHostDriveSrc = pMedium->getLocationFull();
10247 else
10248 dev.uuid = pMedium->getId();
10249 dev.fPassThrough = pAttach->getPassthrough();
10250 dev.fTempEject = pAttach->getTempEject();
10251 dev.fNonRotational = pAttach->getNonRotational();
10252 dev.fDiscard = pAttach->getDiscard();
10253 }
10254
10255 dev.strBwGroup = pAttach->getBandwidthGroup();
10256
10257 data.llAttachedDevices.push_back(dev);
10258 }
10259
10260 return S_OK;
10261}
10262
10263/**
10264 * Saves machine state settings as defined by aFlags
10265 * (SaveSTS_* values).
10266 *
10267 * @param aFlags Combination of SaveSTS_* flags.
10268 *
10269 * @note Locks objects for writing.
10270 */
10271HRESULT Machine::saveStateSettings(int aFlags)
10272{
10273 if (aFlags == 0)
10274 return S_OK;
10275
10276 AutoCaller autoCaller(this);
10277 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10278
10279 /* This object's write lock is also necessary to serialize file access
10280 * (prevent concurrent reads and writes) */
10281 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10282
10283 HRESULT rc = S_OK;
10284
10285 Assert(mData->pMachineConfigFile);
10286
10287 try
10288 {
10289 if (aFlags & SaveSTS_CurStateModified)
10290 mData->pMachineConfigFile->fCurrentStateModified = true;
10291
10292 if (aFlags & SaveSTS_StateFilePath)
10293 {
10294 if (!mSSData->strStateFilePath.isEmpty())
10295 /* try to make the file name relative to the settings file dir */
10296 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10297 else
10298 mData->pMachineConfigFile->strStateFile.setNull();
10299 }
10300
10301 if (aFlags & SaveSTS_StateTimeStamp)
10302 {
10303 Assert( mData->mMachineState != MachineState_Aborted
10304 || mSSData->strStateFilePath.isEmpty());
10305
10306 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10307
10308 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10309//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10310 }
10311
10312 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10313 }
10314 catch (...)
10315 {
10316 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10317 }
10318
10319 return rc;
10320}
10321
10322/**
10323 * Ensures that the given medium is added to a media registry. If this machine
10324 * was created with 4.0 or later, then the machine registry is used. Otherwise
10325 * the global VirtualBox media registry is used.
10326 *
10327 * Caller must NOT hold machine lock, media tree or any medium locks!
10328 *
10329 * @param pMedium
10330 */
10331void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10332{
10333 /* Paranoia checks: do not hold machine or media tree locks. */
10334 AssertReturnVoid(!isWriteLockOnCurrentThread());
10335 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10336
10337 ComObjPtr<Medium> pBase;
10338 {
10339 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10340 pBase = pMedium->getBase();
10341 }
10342
10343 /* Paranoia checks: do not hold medium locks. */
10344 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10345 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10346
10347 // decide which medium registry to use now that the medium is attached:
10348 Guid uuid;
10349 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10350 // machine XML is VirtualBox 4.0 or higher:
10351 uuid = getId(); // machine UUID
10352 else
10353 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10354
10355 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10356 mParent->markRegistryModified(uuid);
10357
10358 /* For more complex hard disk structures it can happen that the base
10359 * medium isn't yet associated with any medium registry. Do that now. */
10360 if (pMedium != pBase)
10361 {
10362 if (pBase->addRegistry(uuid, true /* fRecurse */))
10363 mParent->markRegistryModified(uuid);
10364 }
10365}
10366
10367/**
10368 * Creates differencing hard disks for all normal hard disks attached to this
10369 * machine and a new set of attachments to refer to created disks.
10370 *
10371 * Used when taking a snapshot or when deleting the current state. Gets called
10372 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10373 *
10374 * This method assumes that mMediaData contains the original hard disk attachments
10375 * it needs to create diffs for. On success, these attachments will be replaced
10376 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10377 * called to delete created diffs which will also rollback mMediaData and restore
10378 * whatever was backed up before calling this method.
10379 *
10380 * Attachments with non-normal hard disks are left as is.
10381 *
10382 * If @a aOnline is @c false then the original hard disks that require implicit
10383 * diffs will be locked for reading. Otherwise it is assumed that they are
10384 * already locked for writing (when the VM was started). Note that in the latter
10385 * case it is responsibility of the caller to lock the newly created diffs for
10386 * writing if this method succeeds.
10387 *
10388 * @param aProgress Progress object to run (must contain at least as
10389 * many operations left as the number of hard disks
10390 * attached).
10391 * @param aOnline Whether the VM was online prior to this operation.
10392 *
10393 * @note The progress object is not marked as completed, neither on success nor
10394 * on failure. This is a responsibility of the caller.
10395 *
10396 * @note Locks this object and the media tree for writing.
10397 */
10398HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10399 ULONG aWeight,
10400 bool aOnline)
10401{
10402 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10403
10404 AutoCaller autoCaller(this);
10405 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10406
10407 AutoMultiWriteLock2 alock(this->lockHandle(),
10408 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10409
10410 /* must be in a protective state because we release the lock below */
10411 AssertReturn( mData->mMachineState == MachineState_Saving
10412 || mData->mMachineState == MachineState_LiveSnapshotting
10413 || mData->mMachineState == MachineState_RestoringSnapshot
10414 || mData->mMachineState == MachineState_DeletingSnapshot
10415 , E_FAIL);
10416
10417 HRESULT rc = S_OK;
10418
10419 // use appropriate locked media map (online or offline)
10420 MediumLockListMap lockedMediaOffline;
10421 MediumLockListMap *lockedMediaMap;
10422 if (aOnline)
10423 lockedMediaMap = &mData->mSession.mLockedMedia;
10424 else
10425 lockedMediaMap = &lockedMediaOffline;
10426
10427 try
10428 {
10429 if (!aOnline)
10430 {
10431 /* lock all attached hard disks early to detect "in use"
10432 * situations before creating actual diffs */
10433 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10434 it != mMediaData->mAttachments.end();
10435 ++it)
10436 {
10437 MediumAttachment* pAtt = *it;
10438 if (pAtt->getType() == DeviceType_HardDisk)
10439 {
10440 Medium* pMedium = pAtt->getMedium();
10441 Assert(pMedium);
10442
10443 MediumLockList *pMediumLockList(new MediumLockList());
10444 alock.release();
10445 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10446 false /* fMediumLockWrite */,
10447 NULL,
10448 *pMediumLockList);
10449 alock.acquire();
10450 if (FAILED(rc))
10451 {
10452 delete pMediumLockList;
10453 throw rc;
10454 }
10455 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10456 if (FAILED(rc))
10457 {
10458 throw setError(rc,
10459 tr("Collecting locking information for all attached media failed"));
10460 }
10461 }
10462 }
10463
10464 /* Now lock all media. If this fails, nothing is locked. */
10465 alock.release();
10466 rc = lockedMediaMap->Lock();
10467 alock.acquire();
10468 if (FAILED(rc))
10469 {
10470 throw setError(rc,
10471 tr("Locking of attached media failed"));
10472 }
10473 }
10474
10475 /* remember the current list (note that we don't use backup() since
10476 * mMediaData may be already backed up) */
10477 MediaData::AttachmentList atts = mMediaData->mAttachments;
10478
10479 /* start from scratch */
10480 mMediaData->mAttachments.clear();
10481
10482 /* go through remembered attachments and create diffs for normal hard
10483 * disks and attach them */
10484 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10485 it != atts.end();
10486 ++it)
10487 {
10488 MediumAttachment* pAtt = *it;
10489
10490 DeviceType_T devType = pAtt->getType();
10491 Medium* pMedium = pAtt->getMedium();
10492
10493 if ( devType != DeviceType_HardDisk
10494 || pMedium == NULL
10495 || pMedium->getType() != MediumType_Normal)
10496 {
10497 /* copy the attachment as is */
10498
10499 /** @todo the progress object created in Console::TakeSnaphot
10500 * only expects operations for hard disks. Later other
10501 * device types need to show up in the progress as well. */
10502 if (devType == DeviceType_HardDisk)
10503 {
10504 if (pMedium == NULL)
10505 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10506 aWeight); // weight
10507 else
10508 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10509 pMedium->getBase()->getName().c_str()).raw(),
10510 aWeight); // weight
10511 }
10512
10513 mMediaData->mAttachments.push_back(pAtt);
10514 continue;
10515 }
10516
10517 /* need a diff */
10518 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10519 pMedium->getBase()->getName().c_str()).raw(),
10520 aWeight); // weight
10521
10522 Utf8Str strFullSnapshotFolder;
10523 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10524
10525 ComObjPtr<Medium> diff;
10526 diff.createObject();
10527 // store the diff in the same registry as the parent
10528 // (this cannot fail here because we can't create implicit diffs for
10529 // unregistered images)
10530 Guid uuidRegistryParent;
10531 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10532 Assert(fInRegistry); NOREF(fInRegistry);
10533 rc = diff->init(mParent,
10534 pMedium->getPreferredDiffFormat(),
10535 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10536 uuidRegistryParent);
10537 if (FAILED(rc)) throw rc;
10538
10539 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10540 * the push_back? Looks like we're going to release medium with the
10541 * wrong kind of lock (general issue with if we fail anywhere at all)
10542 * and an orphaned VDI in the snapshots folder. */
10543
10544 /* update the appropriate lock list */
10545 MediumLockList *pMediumLockList;
10546 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10547 AssertComRCThrowRC(rc);
10548 if (aOnline)
10549 {
10550 alock.release();
10551 /* The currently attached medium will be read-only, change
10552 * the lock type to read. */
10553 rc = pMediumLockList->Update(pMedium, false);
10554 alock.acquire();
10555 AssertComRCThrowRC(rc);
10556 }
10557
10558 /* release the locks before the potentially lengthy operation */
10559 alock.release();
10560 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10561 pMediumLockList,
10562 NULL /* aProgress */,
10563 true /* aWait */);
10564 alock.acquire();
10565 if (FAILED(rc)) throw rc;
10566
10567 rc = lockedMediaMap->Unlock();
10568 AssertComRCThrowRC(rc);
10569 alock.release();
10570 rc = pMediumLockList->Append(diff, true);
10571 alock.acquire();
10572 AssertComRCThrowRC(rc);
10573 alock.release();
10574 rc = lockedMediaMap->Lock();
10575 alock.acquire();
10576 AssertComRCThrowRC(rc);
10577
10578 rc = diff->addBackReference(mData->mUuid);
10579 AssertComRCThrowRC(rc);
10580
10581 /* add a new attachment */
10582 ComObjPtr<MediumAttachment> attachment;
10583 attachment.createObject();
10584 rc = attachment->init(this,
10585 diff,
10586 pAtt->getControllerName(),
10587 pAtt->getPort(),
10588 pAtt->getDevice(),
10589 DeviceType_HardDisk,
10590 true /* aImplicit */,
10591 false /* aPassthrough */,
10592 false /* aTempEject */,
10593 pAtt->getNonRotational(),
10594 pAtt->getDiscard(),
10595 pAtt->getBandwidthGroup());
10596 if (FAILED(rc)) throw rc;
10597
10598 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10599 AssertComRCThrowRC(rc);
10600 mMediaData->mAttachments.push_back(attachment);
10601 }
10602 }
10603 catch (HRESULT aRC) { rc = aRC; }
10604
10605 /* unlock all hard disks we locked when there is no VM */
10606 if (!aOnline)
10607 {
10608 ErrorInfoKeeper eik;
10609
10610 HRESULT rc1 = lockedMediaMap->Clear();
10611 AssertComRC(rc1);
10612 }
10613
10614 return rc;
10615}
10616
10617/**
10618 * Deletes implicit differencing hard disks created either by
10619 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10620 *
10621 * Note that to delete hard disks created by #AttachDevice() this method is
10622 * called from #fixupMedia() when the changes are rolled back.
10623 *
10624 * @note Locks this object and the media tree for writing.
10625 */
10626HRESULT Machine::deleteImplicitDiffs(bool aOnline)
10627{
10628 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10629
10630 AutoCaller autoCaller(this);
10631 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10632
10633 AutoMultiWriteLock2 alock(this->lockHandle(),
10634 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10635
10636 /* We absolutely must have backed up state. */
10637 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10638
10639 /* Check if there are any implicitly created diff images. */
10640 bool fImplicitDiffs = false;
10641 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10642 it != mMediaData->mAttachments.end();
10643 ++it)
10644 {
10645 const ComObjPtr<MediumAttachment> &pAtt = *it;
10646 if (pAtt->isImplicit())
10647 {
10648 fImplicitDiffs = true;
10649 break;
10650 }
10651 }
10652 /* If there is nothing to do, leave early. This saves lots of image locking
10653 * effort. It also avoids a MachineStateChanged event without real reason.
10654 * This is important e.g. when loading a VM config, because there should be
10655 * no events. Otherwise API clients can become thoroughly confused for
10656 * inaccessible VMs (the code for loading VM configs uses this method for
10657 * cleanup if the config makes no sense), as they take such events as an
10658 * indication that the VM is alive, and they would force the VM config to
10659 * be reread, leading to an endless loop. */
10660 if (!fImplicitDiffs)
10661 return S_OK;
10662
10663 HRESULT rc = S_OK;
10664 MachineState_T oldState = mData->mMachineState;
10665
10666 /* will release the lock before the potentially lengthy operation,
10667 * so protect with the special state (unless already protected) */
10668 if ( oldState != MachineState_Saving
10669 && oldState != MachineState_LiveSnapshotting
10670 && oldState != MachineState_RestoringSnapshot
10671 && oldState != MachineState_DeletingSnapshot
10672 && oldState != MachineState_DeletingSnapshotOnline
10673 && oldState != MachineState_DeletingSnapshotPaused
10674 )
10675 setMachineState(MachineState_SettingUp);
10676
10677 // use appropriate locked media map (online or offline)
10678 MediumLockListMap lockedMediaOffline;
10679 MediumLockListMap *lockedMediaMap;
10680 if (aOnline)
10681 lockedMediaMap = &mData->mSession.mLockedMedia;
10682 else
10683 lockedMediaMap = &lockedMediaOffline;
10684
10685 try
10686 {
10687 if (!aOnline)
10688 {
10689 /* lock all attached hard disks early to detect "in use"
10690 * situations before deleting actual diffs */
10691 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10692 it != mMediaData->mAttachments.end();
10693 ++it)
10694 {
10695 MediumAttachment* pAtt = *it;
10696 if (pAtt->getType() == DeviceType_HardDisk)
10697 {
10698 Medium* pMedium = pAtt->getMedium();
10699 Assert(pMedium);
10700
10701 MediumLockList *pMediumLockList(new MediumLockList());
10702 alock.release();
10703 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10704 false /* fMediumLockWrite */,
10705 NULL,
10706 *pMediumLockList);
10707 alock.acquire();
10708
10709 if (FAILED(rc))
10710 {
10711 delete pMediumLockList;
10712 throw rc;
10713 }
10714
10715 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10716 if (FAILED(rc))
10717 throw rc;
10718 }
10719 }
10720
10721 if (FAILED(rc))
10722 throw rc;
10723 } // end of offline
10724
10725 /* Lock lists are now up to date and include implicitly created media */
10726
10727 /* Go through remembered attachments and delete all implicitly created
10728 * diffs and fix up the attachment information */
10729 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10730 MediaData::AttachmentList implicitAtts;
10731 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10732 it != mMediaData->mAttachments.end();
10733 ++it)
10734 {
10735 ComObjPtr<MediumAttachment> pAtt = *it;
10736 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10737 if (pMedium.isNull())
10738 continue;
10739
10740 // Implicit attachments go on the list for deletion and back references are removed.
10741 if (pAtt->isImplicit())
10742 {
10743 /* Deassociate and mark for deletion */
10744 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
10745 rc = pMedium->removeBackReference(mData->mUuid);
10746 if (FAILED(rc))
10747 throw rc;
10748 implicitAtts.push_back(pAtt);
10749 continue;
10750 }
10751
10752 /* Was this medium attached before? */
10753 if (!findAttachment(oldAtts, pMedium))
10754 {
10755 /* no: de-associate */
10756 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
10757 rc = pMedium->removeBackReference(mData->mUuid);
10758 if (FAILED(rc))
10759 throw rc;
10760 continue;
10761 }
10762 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
10763 }
10764
10765 /* If there are implicit attachments to delete, throw away the lock
10766 * map contents (which will unlock all media) since the medium
10767 * attachments will be rolled back. Below we need to completely
10768 * recreate the lock map anyway since it is infinitely complex to
10769 * do this incrementally (would need reconstructing each attachment
10770 * change, which would be extremely hairy). */
10771 if (implicitAtts.size() != 0)
10772 {
10773 ErrorInfoKeeper eik;
10774
10775 HRESULT rc1 = lockedMediaMap->Clear();
10776 AssertComRC(rc1);
10777 }
10778
10779 /* rollback hard disk changes */
10780 mMediaData.rollback();
10781
10782 MultiResult mrc(S_OK);
10783
10784 // Delete unused implicit diffs.
10785 if (implicitAtts.size() != 0)
10786 {
10787 alock.release();
10788
10789 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
10790 it != implicitAtts.end();
10791 ++it)
10792 {
10793 // Remove medium associated with this attachment.
10794 ComObjPtr<MediumAttachment> pAtt = *it;
10795 Assert(pAtt);
10796 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
10797 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10798 Assert(pMedium);
10799
10800 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10801 // continue on delete failure, just collect error messages
10802 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
10803 mrc = rc;
10804 }
10805
10806 alock.acquire();
10807
10808 /* if there is a VM recreate media lock map as mentioned above,
10809 * otherwise it is a waste of time and we leave things unlocked */
10810 if (aOnline)
10811 {
10812 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10813 /* must never be NULL, but better safe than sorry */
10814 if (!pMachine.isNull())
10815 {
10816 alock.release();
10817 rc = mData->mSession.mMachine->lockMedia();
10818 alock.acquire();
10819 if (FAILED(rc))
10820 throw rc;
10821 }
10822 }
10823 }
10824 }
10825 catch (HRESULT aRC) {rc = aRC;}
10826
10827 if (mData->mMachineState == MachineState_SettingUp)
10828 setMachineState(oldState);
10829
10830 /* unlock all hard disks we locked when there is no VM */
10831 if (!aOnline)
10832 {
10833 ErrorInfoKeeper eik;
10834
10835 HRESULT rc1 = lockedMediaMap->Clear();
10836 AssertComRC(rc1);
10837 }
10838
10839 return rc;
10840}
10841
10842
10843/**
10844 * Looks through the given list of media attachments for one with the given parameters
10845 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10846 * can be searched as well if needed.
10847 *
10848 * @param list
10849 * @param aControllerName
10850 * @param aControllerPort
10851 * @param aDevice
10852 * @return
10853 */
10854MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10855 IN_BSTR aControllerName,
10856 LONG aControllerPort,
10857 LONG aDevice)
10858{
10859 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10860 it != ll.end();
10861 ++it)
10862 {
10863 MediumAttachment *pAttach = *it;
10864 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
10865 return pAttach;
10866 }
10867
10868 return NULL;
10869}
10870
10871/**
10872 * Looks through the given list of media attachments for one with the given parameters
10873 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10874 * can be searched as well if needed.
10875 *
10876 * @param list
10877 * @param aControllerName
10878 * @param aControllerPort
10879 * @param aDevice
10880 * @return
10881 */
10882MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10883 ComObjPtr<Medium> pMedium)
10884{
10885 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10886 it != ll.end();
10887 ++it)
10888 {
10889 MediumAttachment *pAttach = *it;
10890 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10891 if (pMediumThis == pMedium)
10892 return pAttach;
10893 }
10894
10895 return NULL;
10896}
10897
10898/**
10899 * Looks through the given list of media attachments for one with the given parameters
10900 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10901 * can be searched as well if needed.
10902 *
10903 * @param list
10904 * @param aControllerName
10905 * @param aControllerPort
10906 * @param aDevice
10907 * @return
10908 */
10909MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10910 Guid &id)
10911{
10912 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10913 it != ll.end();
10914 ++it)
10915 {
10916 MediumAttachment *pAttach = *it;
10917 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10918 if (pMediumThis->getId() == id)
10919 return pAttach;
10920 }
10921
10922 return NULL;
10923}
10924
10925/**
10926 * Main implementation for Machine::DetachDevice. This also gets called
10927 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10928 *
10929 * @param pAttach Medium attachment to detach.
10930 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10931 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
10932 * @return
10933 */
10934HRESULT Machine::detachDevice(MediumAttachment *pAttach,
10935 AutoWriteLock &writeLock,
10936 Snapshot *pSnapshot)
10937{
10938 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
10939 DeviceType_T mediumType = pAttach->getType();
10940
10941 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
10942
10943 if (pAttach->isImplicit())
10944 {
10945 /* attempt to implicitly delete the implicitly created diff */
10946
10947 /// @todo move the implicit flag from MediumAttachment to Medium
10948 /// and forbid any hard disk operation when it is implicit. Or maybe
10949 /// a special media state for it to make it even more simple.
10950
10951 Assert(mMediaData.isBackedUp());
10952
10953 /* will release the lock before the potentially lengthy operation, so
10954 * protect with the special state */
10955 MachineState_T oldState = mData->mMachineState;
10956 setMachineState(MachineState_SettingUp);
10957
10958 writeLock.release();
10959
10960 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
10961 true /*aWait*/);
10962
10963 writeLock.acquire();
10964
10965 setMachineState(oldState);
10966
10967 if (FAILED(rc)) return rc;
10968 }
10969
10970 setModified(IsModified_Storage);
10971 mMediaData.backup();
10972 mMediaData->mAttachments.remove(pAttach);
10973
10974 if (!oldmedium.isNull())
10975 {
10976 // if this is from a snapshot, do not defer detachment to commitMedia()
10977 if (pSnapshot)
10978 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
10979 // else if non-hard disk media, do not defer detachment to commitMedia() either
10980 else if (mediumType != DeviceType_HardDisk)
10981 oldmedium->removeBackReference(mData->mUuid);
10982 }
10983
10984 return S_OK;
10985}
10986
10987/**
10988 * Goes thru all media of the given list and
10989 *
10990 * 1) calls detachDevice() on each of them for this machine and
10991 * 2) adds all Medium objects found in the process to the given list,
10992 * depending on cleanupMode.
10993 *
10994 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10995 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10996 * media to the list.
10997 *
10998 * This gets called from Machine::Unregister, both for the actual Machine and
10999 * the SnapshotMachine objects that might be found in the snapshots.
11000 *
11001 * Requires caller and locking. The machine lock must be passed in because it
11002 * will be passed on to detachDevice which needs it for temporary unlocking.
11003 *
11004 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
11005 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11006 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
11007 * otherwise no media get added.
11008 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11009 * @return
11010 */
11011HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
11012 Snapshot *pSnapshot,
11013 CleanupMode_T cleanupMode,
11014 MediaList &llMedia)
11015{
11016 Assert(isWriteLockOnCurrentThread());
11017
11018 HRESULT rc;
11019
11020 // make a temporary list because detachDevice invalidates iterators into
11021 // mMediaData->mAttachments
11022 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11023
11024 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
11025 it != llAttachments2.end();
11026 ++it)
11027 {
11028 ComObjPtr<MediumAttachment> &pAttach = *it;
11029 ComObjPtr<Medium> pMedium = pAttach->getMedium();
11030
11031 if (!pMedium.isNull())
11032 {
11033 AutoCaller mac(pMedium);
11034 if (FAILED(mac.rc())) return mac.rc();
11035 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11036 DeviceType_T devType = pMedium->getDeviceType();
11037 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11038 && devType == DeviceType_HardDisk)
11039 || (cleanupMode == CleanupMode_Full)
11040 )
11041 {
11042 llMedia.push_back(pMedium);
11043 ComObjPtr<Medium> pParent = pMedium->getParent();
11044 /*
11045 * Search for medias which are not attached to any machine, but
11046 * in the chain to an attached disk. Mediums are only consided
11047 * if they are:
11048 * - have only one child
11049 * - no references to any machines
11050 * - are of normal medium type
11051 */
11052 while (!pParent.isNull())
11053 {
11054 AutoCaller mac1(pParent);
11055 if (FAILED(mac1.rc())) return mac1.rc();
11056 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11057 if (pParent->getChildren().size() == 1)
11058 {
11059 if ( pParent->getMachineBackRefCount() == 0
11060 && pParent->getType() == MediumType_Normal
11061 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11062 llMedia.push_back(pParent);
11063 }
11064 else
11065 break;
11066 pParent = pParent->getParent();
11067 }
11068 }
11069 }
11070
11071 // real machine: then we need to use the proper method
11072 rc = detachDevice(pAttach, writeLock, pSnapshot);
11073
11074 if (FAILED(rc))
11075 return rc;
11076 }
11077
11078 return S_OK;
11079}
11080
11081/**
11082 * Perform deferred hard disk detachments.
11083 *
11084 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11085 * backed up).
11086 *
11087 * If @a aOnline is @c true then this method will also unlock the old hard disks
11088 * for which the new implicit diffs were created and will lock these new diffs for
11089 * writing.
11090 *
11091 * @param aOnline Whether the VM was online prior to this operation.
11092 *
11093 * @note Locks this object for writing!
11094 */
11095void Machine::commitMedia(bool aOnline /*= false*/)
11096{
11097 AutoCaller autoCaller(this);
11098 AssertComRCReturnVoid(autoCaller.rc());
11099
11100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11101
11102 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11103
11104 HRESULT rc = S_OK;
11105
11106 /* no attach/detach operations -- nothing to do */
11107 if (!mMediaData.isBackedUp())
11108 return;
11109
11110 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11111 bool fMediaNeedsLocking = false;
11112
11113 /* enumerate new attachments */
11114 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11115 it != mMediaData->mAttachments.end();
11116 ++it)
11117 {
11118 MediumAttachment *pAttach = *it;
11119
11120 pAttach->commit();
11121
11122 Medium* pMedium = pAttach->getMedium();
11123 bool fImplicit = pAttach->isImplicit();
11124
11125 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11126 (pMedium) ? pMedium->getName().c_str() : "NULL",
11127 fImplicit));
11128
11129 /** @todo convert all this Machine-based voodoo to MediumAttachment
11130 * based commit logic. */
11131 if (fImplicit)
11132 {
11133 /* convert implicit attachment to normal */
11134 pAttach->setImplicit(false);
11135
11136 if ( aOnline
11137 && pMedium
11138 && pAttach->getType() == DeviceType_HardDisk
11139 )
11140 {
11141 ComObjPtr<Medium> parent = pMedium->getParent();
11142 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11143
11144 /* update the appropriate lock list */
11145 MediumLockList *pMediumLockList;
11146 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11147 AssertComRC(rc);
11148 if (pMediumLockList)
11149 {
11150 /* unlock if there's a need to change the locking */
11151 if (!fMediaNeedsLocking)
11152 {
11153 rc = mData->mSession.mLockedMedia.Unlock();
11154 AssertComRC(rc);
11155 fMediaNeedsLocking = true;
11156 }
11157 rc = pMediumLockList->Update(parent, false);
11158 AssertComRC(rc);
11159 rc = pMediumLockList->Append(pMedium, true);
11160 AssertComRC(rc);
11161 }
11162 }
11163
11164 continue;
11165 }
11166
11167 if (pMedium)
11168 {
11169 /* was this medium attached before? */
11170 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11171 oldIt != oldAtts.end();
11172 ++oldIt)
11173 {
11174 MediumAttachment *pOldAttach = *oldIt;
11175 if (pOldAttach->getMedium() == pMedium)
11176 {
11177 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
11178
11179 /* yes: remove from old to avoid de-association */
11180 oldAtts.erase(oldIt);
11181 break;
11182 }
11183 }
11184 }
11185 }
11186
11187 /* enumerate remaining old attachments and de-associate from the
11188 * current machine state */
11189 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11190 it != oldAtts.end();
11191 ++it)
11192 {
11193 MediumAttachment *pAttach = *it;
11194 Medium* pMedium = pAttach->getMedium();
11195
11196 /* Detach only hard disks, since DVD/floppy media is detached
11197 * instantly in MountMedium. */
11198 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
11199 {
11200 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
11201
11202 /* now de-associate from the current machine state */
11203 rc = pMedium->removeBackReference(mData->mUuid);
11204 AssertComRC(rc);
11205
11206 if (aOnline)
11207 {
11208 /* unlock since medium is not used anymore */
11209 MediumLockList *pMediumLockList;
11210 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11211 AssertComRC(rc);
11212 if (pMediumLockList)
11213 {
11214 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11215 AssertComRC(rc);
11216 }
11217 }
11218 }
11219 }
11220
11221 /* take media locks again so that the locking state is consistent */
11222 if (fMediaNeedsLocking)
11223 {
11224 Assert(aOnline);
11225 rc = mData->mSession.mLockedMedia.Lock();
11226 AssertComRC(rc);
11227 }
11228
11229 /* commit the hard disk changes */
11230 mMediaData.commit();
11231
11232 if (isSessionMachine())
11233 {
11234 /*
11235 * Update the parent machine to point to the new owner.
11236 * This is necessary because the stored parent will point to the
11237 * session machine otherwise and cause crashes or errors later
11238 * when the session machine gets invalid.
11239 */
11240 /** @todo Change the MediumAttachment class to behave like any other
11241 * class in this regard by creating peer MediumAttachment
11242 * objects for session machines and share the data with the peer
11243 * machine.
11244 */
11245 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11246 it != mMediaData->mAttachments.end();
11247 ++it)
11248 {
11249 (*it)->updateParentMachine(mPeer);
11250 }
11251
11252 /* attach new data to the primary machine and reshare it */
11253 mPeer->mMediaData.attach(mMediaData);
11254 }
11255
11256 return;
11257}
11258
11259/**
11260 * Perform deferred deletion of implicitly created diffs.
11261 *
11262 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11263 * backed up).
11264 *
11265 * @note Locks this object for writing!
11266 */
11267void Machine::rollbackMedia()
11268{
11269 AutoCaller autoCaller(this);
11270 AssertComRCReturnVoid(autoCaller.rc());
11271
11272 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11273 LogFlowThisFunc(("Entering rollbackMedia\n"));
11274
11275 HRESULT rc = S_OK;
11276
11277 /* no attach/detach operations -- nothing to do */
11278 if (!mMediaData.isBackedUp())
11279 return;
11280
11281 /* enumerate new attachments */
11282 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11283 it != mMediaData->mAttachments.end();
11284 ++it)
11285 {
11286 MediumAttachment *pAttach = *it;
11287 /* Fix up the backrefs for DVD/floppy media. */
11288 if (pAttach->getType() != DeviceType_HardDisk)
11289 {
11290 Medium* pMedium = pAttach->getMedium();
11291 if (pMedium)
11292 {
11293 rc = pMedium->removeBackReference(mData->mUuid);
11294 AssertComRC(rc);
11295 }
11296 }
11297
11298 (*it)->rollback();
11299
11300 pAttach = *it;
11301 /* Fix up the backrefs for DVD/floppy media. */
11302 if (pAttach->getType() != DeviceType_HardDisk)
11303 {
11304 Medium* pMedium = pAttach->getMedium();
11305 if (pMedium)
11306 {
11307 rc = pMedium->addBackReference(mData->mUuid);
11308 AssertComRC(rc);
11309 }
11310 }
11311 }
11312
11313 /** @todo convert all this Machine-based voodoo to MediumAttachment
11314 * based rollback logic. */
11315 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11316
11317 return;
11318}
11319
11320/**
11321 * Returns true if the settings file is located in the directory named exactly
11322 * as the machine; this means, among other things, that the machine directory
11323 * should be auto-renamed.
11324 *
11325 * @param aSettingsDir if not NULL, the full machine settings file directory
11326 * name will be assigned there.
11327 *
11328 * @note Doesn't lock anything.
11329 * @note Not thread safe (must be called from this object's lock).
11330 */
11331bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11332{
11333 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11334 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11335 if (aSettingsDir)
11336 *aSettingsDir = strMachineDirName;
11337 strMachineDirName.stripPath(); // vmname
11338 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11339 strConfigFileOnly.stripPath() // vmname.vbox
11340 .stripExt(); // vmname
11341 /** @todo hack, make somehow use of ComposeMachineFilename */
11342 if (mUserData->s.fDirectoryIncludesUUID)
11343 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11344
11345 AssertReturn(!strMachineDirName.isEmpty(), false);
11346 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11347
11348 return strMachineDirName == strConfigFileOnly;
11349}
11350
11351/**
11352 * Discards all changes to machine settings.
11353 *
11354 * @param aNotify Whether to notify the direct session about changes or not.
11355 *
11356 * @note Locks objects for writing!
11357 */
11358void Machine::rollback(bool aNotify)
11359{
11360 AutoCaller autoCaller(this);
11361 AssertComRCReturn(autoCaller.rc(), (void)0);
11362
11363 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11364
11365 if (!mStorageControllers.isNull())
11366 {
11367 if (mStorageControllers.isBackedUp())
11368 {
11369 /* unitialize all new devices (absent in the backed up list). */
11370 StorageControllerList::const_iterator it = mStorageControllers->begin();
11371 StorageControllerList *backedList = mStorageControllers.backedUpData();
11372 while (it != mStorageControllers->end())
11373 {
11374 if ( std::find(backedList->begin(), backedList->end(), *it)
11375 == backedList->end()
11376 )
11377 {
11378 (*it)->uninit();
11379 }
11380 ++it;
11381 }
11382
11383 /* restore the list */
11384 mStorageControllers.rollback();
11385 }
11386
11387 /* rollback any changes to devices after restoring the list */
11388 if (mData->flModifications & IsModified_Storage)
11389 {
11390 StorageControllerList::const_iterator it = mStorageControllers->begin();
11391 while (it != mStorageControllers->end())
11392 {
11393 (*it)->rollback();
11394 ++it;
11395 }
11396 }
11397 }
11398
11399 mUserData.rollback();
11400
11401 mHWData.rollback();
11402
11403 if (mData->flModifications & IsModified_Storage)
11404 rollbackMedia();
11405
11406 if (mBIOSSettings)
11407 mBIOSSettings->rollback();
11408
11409 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11410 mVRDEServer->rollback();
11411
11412 if (mAudioAdapter)
11413 mAudioAdapter->rollback();
11414
11415 if (mUSBController && (mData->flModifications & IsModified_USB))
11416 mUSBController->rollback();
11417
11418 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11419 mBandwidthControl->rollback();
11420
11421 if (!mHWData.isNull())
11422 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11423 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11424 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11425 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11426
11427 if (mData->flModifications & IsModified_NetworkAdapters)
11428 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11429 if ( mNetworkAdapters[slot]
11430 && mNetworkAdapters[slot]->isModified())
11431 {
11432 mNetworkAdapters[slot]->rollback();
11433 networkAdapters[slot] = mNetworkAdapters[slot];
11434 }
11435
11436 if (mData->flModifications & IsModified_SerialPorts)
11437 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11438 if ( mSerialPorts[slot]
11439 && mSerialPorts[slot]->isModified())
11440 {
11441 mSerialPorts[slot]->rollback();
11442 serialPorts[slot] = mSerialPorts[slot];
11443 }
11444
11445 if (mData->flModifications & IsModified_ParallelPorts)
11446 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11447 if ( mParallelPorts[slot]
11448 && mParallelPorts[slot]->isModified())
11449 {
11450 mParallelPorts[slot]->rollback();
11451 parallelPorts[slot] = mParallelPorts[slot];
11452 }
11453
11454 if (aNotify)
11455 {
11456 /* inform the direct session about changes */
11457
11458 ComObjPtr<Machine> that = this;
11459 uint32_t flModifications = mData->flModifications;
11460 alock.release();
11461
11462 if (flModifications & IsModified_SharedFolders)
11463 that->onSharedFolderChange();
11464
11465 if (flModifications & IsModified_VRDEServer)
11466 that->onVRDEServerChange(/* aRestart */ TRUE);
11467 if (flModifications & IsModified_USB)
11468 that->onUSBControllerChange();
11469
11470 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
11471 if (networkAdapters[slot])
11472 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
11473 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
11474 if (serialPorts[slot])
11475 that->onSerialPortChange(serialPorts[slot]);
11476 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
11477 if (parallelPorts[slot])
11478 that->onParallelPortChange(parallelPorts[slot]);
11479
11480 if (flModifications & IsModified_Storage)
11481 that->onStorageControllerChange();
11482
11483#if 0
11484 if (flModifications & IsModified_BandwidthControl)
11485 that->onBandwidthControlChange();
11486#endif
11487 }
11488}
11489
11490/**
11491 * Commits all the changes to machine settings.
11492 *
11493 * Note that this operation is supposed to never fail.
11494 *
11495 * @note Locks this object and children for writing.
11496 */
11497void Machine::commit()
11498{
11499 AutoCaller autoCaller(this);
11500 AssertComRCReturnVoid(autoCaller.rc());
11501
11502 AutoCaller peerCaller(mPeer);
11503 AssertComRCReturnVoid(peerCaller.rc());
11504
11505 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11506
11507 /*
11508 * use safe commit to ensure Snapshot machines (that share mUserData)
11509 * will still refer to a valid memory location
11510 */
11511 mUserData.commitCopy();
11512
11513 mHWData.commit();
11514
11515 if (mMediaData.isBackedUp())
11516 commitMedia(Global::IsOnline(mData->mMachineState));
11517
11518 mBIOSSettings->commit();
11519 mVRDEServer->commit();
11520 mAudioAdapter->commit();
11521 mUSBController->commit();
11522 mBandwidthControl->commit();
11523
11524 /* Since mNetworkAdapters is a list which might have been changed (resized)
11525 * without using the Backupable<> template we need to handle the copying
11526 * of the list entries manually, including the creation of peers for the
11527 * new objects. */
11528 bool commitNetworkAdapters = false;
11529 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11530 if (mPeer)
11531 {
11532 /* commit everything, even the ones which will go away */
11533 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11534 mNetworkAdapters[slot]->commit();
11535 /* copy over the new entries, creating a peer and uninit the original */
11536 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11537 for (size_t slot = 0; slot < newSize; slot++)
11538 {
11539 /* look if this adapter has a peer device */
11540 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
11541 if (!peer)
11542 {
11543 /* no peer means the adapter is a newly created one;
11544 * create a peer owning data this data share it with */
11545 peer.createObject();
11546 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11547 }
11548 mPeer->mNetworkAdapters[slot] = peer;
11549 }
11550 /* uninit any no longer needed network adapters */
11551 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
11552 mNetworkAdapters[slot]->uninit();
11553 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
11554 {
11555 if (mPeer->mNetworkAdapters[slot])
11556 mPeer->mNetworkAdapters[slot]->uninit();
11557 }
11558 /* Keep the original network adapter count until this point, so that
11559 * discarding a chipset type change will not lose settings. */
11560 mNetworkAdapters.resize(newSize);
11561 mPeer->mNetworkAdapters.resize(newSize);
11562 }
11563 else
11564 {
11565 /* we have no peer (our parent is the newly created machine);
11566 * just commit changes to the network adapters */
11567 commitNetworkAdapters = true;
11568 }
11569 if (commitNetworkAdapters)
11570 {
11571 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11572 mNetworkAdapters[slot]->commit();
11573 }
11574
11575 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11576 mSerialPorts[slot]->commit();
11577 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11578 mParallelPorts[slot]->commit();
11579
11580 bool commitStorageControllers = false;
11581
11582 if (mStorageControllers.isBackedUp())
11583 {
11584 mStorageControllers.commit();
11585
11586 if (mPeer)
11587 {
11588 /* Commit all changes to new controllers (this will reshare data with
11589 * peers for those who have peers) */
11590 StorageControllerList *newList = new StorageControllerList();
11591 StorageControllerList::const_iterator it = mStorageControllers->begin();
11592 while (it != mStorageControllers->end())
11593 {
11594 (*it)->commit();
11595
11596 /* look if this controller has a peer device */
11597 ComObjPtr<StorageController> peer = (*it)->getPeer();
11598 if (!peer)
11599 {
11600 /* no peer means the device is a newly created one;
11601 * create a peer owning data this device share it with */
11602 peer.createObject();
11603 peer->init(mPeer, *it, true /* aReshare */);
11604 }
11605 else
11606 {
11607 /* remove peer from the old list */
11608 mPeer->mStorageControllers->remove(peer);
11609 }
11610 /* and add it to the new list */
11611 newList->push_back(peer);
11612
11613 ++it;
11614 }
11615
11616 /* uninit old peer's controllers that are left */
11617 it = mPeer->mStorageControllers->begin();
11618 while (it != mPeer->mStorageControllers->end())
11619 {
11620 (*it)->uninit();
11621 ++it;
11622 }
11623
11624 /* attach new list of controllers to our peer */
11625 mPeer->mStorageControllers.attach(newList);
11626 }
11627 else
11628 {
11629 /* we have no peer (our parent is the newly created machine);
11630 * just commit changes to devices */
11631 commitStorageControllers = true;
11632 }
11633 }
11634 else
11635 {
11636 /* the list of controllers itself is not changed,
11637 * just commit changes to controllers themselves */
11638 commitStorageControllers = true;
11639 }
11640
11641 if (commitStorageControllers)
11642 {
11643 StorageControllerList::const_iterator it = mStorageControllers->begin();
11644 while (it != mStorageControllers->end())
11645 {
11646 (*it)->commit();
11647 ++it;
11648 }
11649 }
11650
11651 if (isSessionMachine())
11652 {
11653 /* attach new data to the primary machine and reshare it */
11654 mPeer->mUserData.attach(mUserData);
11655 mPeer->mHWData.attach(mHWData);
11656 /* mMediaData is reshared by fixupMedia */
11657 // mPeer->mMediaData.attach(mMediaData);
11658 Assert(mPeer->mMediaData.data() == mMediaData.data());
11659 }
11660}
11661
11662/**
11663 * Copies all the hardware data from the given machine.
11664 *
11665 * Currently, only called when the VM is being restored from a snapshot. In
11666 * particular, this implies that the VM is not running during this method's
11667 * call.
11668 *
11669 * @note This method must be called from under this object's lock.
11670 *
11671 * @note This method doesn't call #commit(), so all data remains backed up and
11672 * unsaved.
11673 */
11674void Machine::copyFrom(Machine *aThat)
11675{
11676 AssertReturnVoid(!isSnapshotMachine());
11677 AssertReturnVoid(aThat->isSnapshotMachine());
11678
11679 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11680
11681 mHWData.assignCopy(aThat->mHWData);
11682
11683 // create copies of all shared folders (mHWData after attaching a copy
11684 // contains just references to original objects)
11685 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11686 it != mHWData->mSharedFolders.end();
11687 ++it)
11688 {
11689 ComObjPtr<SharedFolder> folder;
11690 folder.createObject();
11691 HRESULT rc = folder->initCopy(getMachine(), *it);
11692 AssertComRC(rc);
11693 *it = folder;
11694 }
11695
11696 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
11697 mVRDEServer->copyFrom(aThat->mVRDEServer);
11698 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
11699 mUSBController->copyFrom(aThat->mUSBController);
11700 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
11701
11702 /* create private copies of all controllers */
11703 mStorageControllers.backup();
11704 mStorageControllers->clear();
11705 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11706 it != aThat->mStorageControllers->end();
11707 ++it)
11708 {
11709 ComObjPtr<StorageController> ctrl;
11710 ctrl.createObject();
11711 ctrl->initCopy(this, *it);
11712 mStorageControllers->push_back(ctrl);
11713 }
11714
11715 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11716 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11717 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
11718 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11719 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
11720 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11721 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
11722}
11723
11724/**
11725 * Returns whether the given storage controller is hotplug capable.
11726 *
11727 * @returns true if the controller supports hotplugging
11728 * false otherwise.
11729 * @param enmCtrlType The controller type to check for.
11730 */
11731bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11732{
11733 switch (enmCtrlType)
11734 {
11735 case StorageControllerType_IntelAhci:
11736 return true;
11737 case StorageControllerType_LsiLogic:
11738 case StorageControllerType_LsiLogicSas:
11739 case StorageControllerType_BusLogic:
11740 case StorageControllerType_PIIX3:
11741 case StorageControllerType_PIIX4:
11742 case StorageControllerType_ICH6:
11743 case StorageControllerType_I82078:
11744 default:
11745 return false;
11746 }
11747}
11748
11749#ifdef VBOX_WITH_RESOURCE_USAGE_API
11750
11751void Machine::getDiskList(MediaList &list)
11752{
11753 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11754 it != mMediaData->mAttachments.end();
11755 ++it)
11756 {
11757 MediumAttachment* pAttach = *it;
11758 /* just in case */
11759 AssertStmt(pAttach, continue);
11760
11761 AutoCaller localAutoCallerA(pAttach);
11762 if (FAILED(localAutoCallerA.rc())) continue;
11763
11764 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11765
11766 if (pAttach->getType() == DeviceType_HardDisk)
11767 list.push_back(pAttach->getMedium());
11768 }
11769}
11770
11771void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11772{
11773 AssertReturnVoid(isWriteLockOnCurrentThread());
11774 AssertPtrReturnVoid(aCollector);
11775
11776 pm::CollectorHAL *hal = aCollector->getHAL();
11777 /* Create sub metrics */
11778 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11779 "Percentage of processor time spent in user mode by the VM process.");
11780 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11781 "Percentage of processor time spent in kernel mode by the VM process.");
11782 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11783 "Size of resident portion of VM process in memory.");
11784 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11785 "Actual size of all VM disks combined.");
11786 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11787 "Network receive rate.");
11788 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11789 "Network transmit rate.");
11790 /* Create and register base metrics */
11791 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11792 cpuLoadUser, cpuLoadKernel);
11793 aCollector->registerBaseMetric(cpuLoad);
11794 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11795 ramUsageUsed);
11796 aCollector->registerBaseMetric(ramUsage);
11797 MediaList disks;
11798 getDiskList(disks);
11799 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11800 diskUsageUsed);
11801 aCollector->registerBaseMetric(diskUsage);
11802
11803 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11804 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11805 new pm::AggregateAvg()));
11806 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11807 new pm::AggregateMin()));
11808 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11809 new pm::AggregateMax()));
11810 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11811 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11812 new pm::AggregateAvg()));
11813 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11814 new pm::AggregateMin()));
11815 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11816 new pm::AggregateMax()));
11817
11818 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11819 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11820 new pm::AggregateAvg()));
11821 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11822 new pm::AggregateMin()));
11823 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11824 new pm::AggregateMax()));
11825
11826 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11827 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11828 new pm::AggregateAvg()));
11829 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11830 new pm::AggregateMin()));
11831 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11832 new pm::AggregateMax()));
11833
11834
11835 /* Guest metrics collector */
11836 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11837 aCollector->registerGuest(mCollectorGuest);
11838 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11839 this, __PRETTY_FUNCTION__, mCollectorGuest));
11840
11841 /* Create sub metrics */
11842 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11843 "Percentage of processor time spent in user mode as seen by the guest.");
11844 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11845 "Percentage of processor time spent in kernel mode as seen by the guest.");
11846 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11847 "Percentage of processor time spent idling as seen by the guest.");
11848
11849 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11850 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11851 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11852 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11853 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11854 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11855
11856 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11857
11858 /* Create and register base metrics */
11859 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
11860 machineNetRx, machineNetTx);
11861 aCollector->registerBaseMetric(machineNetRate);
11862
11863 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11864 guestLoadUser, guestLoadKernel, guestLoadIdle);
11865 aCollector->registerBaseMetric(guestCpuLoad);
11866
11867 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11868 guestMemTotal, guestMemFree,
11869 guestMemBalloon, guestMemShared,
11870 guestMemCache, guestPagedTotal);
11871 aCollector->registerBaseMetric(guestCpuMem);
11872
11873 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
11874 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
11875 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
11876 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
11877
11878 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
11879 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
11880 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
11881 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
11882
11883 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11884 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11885 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11886 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11887
11888 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11889 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11890 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11891 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11892
11893 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11894 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11895 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11896 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11897
11898 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11899 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11900 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11901 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11902
11903 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11904 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11905 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11906 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11907
11908 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11909 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11910 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11911 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11912
11913 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11914 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11915 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11916 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11917
11918 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11919 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11920 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11921 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11922
11923 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11924 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11925 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11926 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11927}
11928
11929void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11930{
11931 AssertReturnVoid(isWriteLockOnCurrentThread());
11932
11933 if (aCollector)
11934 {
11935 aCollector->unregisterMetricsFor(aMachine);
11936 aCollector->unregisterBaseMetricsFor(aMachine);
11937 }
11938}
11939
11940#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11941
11942
11943////////////////////////////////////////////////////////////////////////////////
11944
11945DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11946
11947HRESULT SessionMachine::FinalConstruct()
11948{
11949 LogFlowThisFunc(("\n"));
11950
11951#if defined(RT_OS_WINDOWS)
11952 mIPCSem = NULL;
11953#elif defined(RT_OS_OS2)
11954 mIPCSem = NULLHANDLE;
11955#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11956 mIPCSem = -1;
11957#else
11958# error "Port me!"
11959#endif
11960
11961 return BaseFinalConstruct();
11962}
11963
11964void SessionMachine::FinalRelease()
11965{
11966 LogFlowThisFunc(("\n"));
11967
11968 uninit(Uninit::Unexpected);
11969
11970 BaseFinalRelease();
11971}
11972
11973/**
11974 * @note Must be called only by Machine::openSession() from its own write lock.
11975 */
11976HRESULT SessionMachine::init(Machine *aMachine)
11977{
11978 LogFlowThisFuncEnter();
11979 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
11980
11981 AssertReturn(aMachine, E_INVALIDARG);
11982
11983 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
11984
11985 /* Enclose the state transition NotReady->InInit->Ready */
11986 AutoInitSpan autoInitSpan(this);
11987 AssertReturn(autoInitSpan.isOk(), E_FAIL);
11988
11989 /* create the interprocess semaphore */
11990#if defined(RT_OS_WINDOWS)
11991 mIPCSemName = aMachine->mData->m_strConfigFileFull;
11992 for (size_t i = 0; i < mIPCSemName.length(); i++)
11993 if (mIPCSemName.raw()[i] == '\\')
11994 mIPCSemName.raw()[i] = '/';
11995 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
11996 ComAssertMsgRet(mIPCSem,
11997 ("Cannot create IPC mutex '%ls', err=%d",
11998 mIPCSemName.raw(), ::GetLastError()),
11999 E_FAIL);
12000#elif defined(RT_OS_OS2)
12001 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
12002 aMachine->mData->mUuid.raw());
12003 mIPCSemName = ipcSem;
12004 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
12005 ComAssertMsgRet(arc == NO_ERROR,
12006 ("Cannot create IPC mutex '%s', arc=%ld",
12007 ipcSem.c_str(), arc),
12008 E_FAIL);
12009#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12010# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12011# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
12012 /** @todo Check that this still works correctly. */
12013 AssertCompileSize(key_t, 8);
12014# else
12015 AssertCompileSize(key_t, 4);
12016# endif
12017 key_t key;
12018 mIPCSem = -1;
12019 mIPCKey = "0";
12020 for (uint32_t i = 0; i < 1 << 24; i++)
12021 {
12022 key = ((uint32_t)'V' << 24) | i;
12023 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
12024 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
12025 {
12026 mIPCSem = sem;
12027 if (sem >= 0)
12028 mIPCKey = BstrFmt("%u", key);
12029 break;
12030 }
12031 }
12032# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12033 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
12034 char *pszSemName = NULL;
12035 RTStrUtf8ToCurrentCP(&pszSemName, semName);
12036 key_t key = ::ftok(pszSemName, 'V');
12037 RTStrFree(pszSemName);
12038
12039 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
12040# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12041
12042 int errnoSave = errno;
12043 if (mIPCSem < 0 && errnoSave == ENOSYS)
12044 {
12045 setError(E_FAIL,
12046 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
12047 "support for SysV IPC. Check the host kernel configuration for "
12048 "CONFIG_SYSVIPC=y"));
12049 return E_FAIL;
12050 }
12051 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
12052 * the IPC semaphores */
12053 if (mIPCSem < 0 && errnoSave == ENOSPC)
12054 {
12055#ifdef RT_OS_LINUX
12056 setError(E_FAIL,
12057 tr("Cannot create IPC semaphore because the system limit for the "
12058 "maximum number of semaphore sets (SEMMNI), or the system wide "
12059 "maximum number of semaphores (SEMMNS) would be exceeded. The "
12060 "current set of SysV IPC semaphores can be determined from "
12061 "the file /proc/sysvipc/sem"));
12062#else
12063 setError(E_FAIL,
12064 tr("Cannot create IPC semaphore because the system-imposed limit "
12065 "on the maximum number of allowed semaphores or semaphore "
12066 "identifiers system-wide would be exceeded"));
12067#endif
12068 return E_FAIL;
12069 }
12070 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
12071 E_FAIL);
12072 /* set the initial value to 1 */
12073 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
12074 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
12075 E_FAIL);
12076#else
12077# error "Port me!"
12078#endif
12079
12080 /* memorize the peer Machine */
12081 unconst(mPeer) = aMachine;
12082 /* share the parent pointer */
12083 unconst(mParent) = aMachine->mParent;
12084
12085 /* take the pointers to data to share */
12086 mData.share(aMachine->mData);
12087 mSSData.share(aMachine->mSSData);
12088
12089 mUserData.share(aMachine->mUserData);
12090 mHWData.share(aMachine->mHWData);
12091 mMediaData.share(aMachine->mMediaData);
12092
12093 mStorageControllers.allocate();
12094 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12095 it != aMachine->mStorageControllers->end();
12096 ++it)
12097 {
12098 ComObjPtr<StorageController> ctl;
12099 ctl.createObject();
12100 ctl->init(this, *it);
12101 mStorageControllers->push_back(ctl);
12102 }
12103
12104 unconst(mBIOSSettings).createObject();
12105 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12106 /* create another VRDEServer object that will be mutable */
12107 unconst(mVRDEServer).createObject();
12108 mVRDEServer->init(this, aMachine->mVRDEServer);
12109 /* create another audio adapter object that will be mutable */
12110 unconst(mAudioAdapter).createObject();
12111 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12112 /* create a list of serial ports that will be mutable */
12113 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12114 {
12115 unconst(mSerialPorts[slot]).createObject();
12116 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12117 }
12118 /* create a list of parallel ports that will be mutable */
12119 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12120 {
12121 unconst(mParallelPorts[slot]).createObject();
12122 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12123 }
12124 /* create another USB controller object that will be mutable */
12125 unconst(mUSBController).createObject();
12126 mUSBController->init(this, aMachine->mUSBController);
12127
12128 /* create a list of network adapters that will be mutable */
12129 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12130 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12131 {
12132 unconst(mNetworkAdapters[slot]).createObject();
12133 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12134 }
12135
12136 /* create another bandwidth control object that will be mutable */
12137 unconst(mBandwidthControl).createObject();
12138 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12139
12140 /* default is to delete saved state on Saved -> PoweredOff transition */
12141 mRemoveSavedState = true;
12142
12143 /* Confirm a successful initialization when it's the case */
12144 autoInitSpan.setSucceeded();
12145
12146 LogFlowThisFuncLeave();
12147 return S_OK;
12148}
12149
12150/**
12151 * Uninitializes this session object. If the reason is other than
12152 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
12153 *
12154 * @param aReason uninitialization reason
12155 *
12156 * @note Locks mParent + this object for writing.
12157 */
12158void SessionMachine::uninit(Uninit::Reason aReason)
12159{
12160 LogFlowThisFuncEnter();
12161 LogFlowThisFunc(("reason=%d\n", aReason));
12162
12163 /*
12164 * Strongly reference ourselves to prevent this object deletion after
12165 * mData->mSession.mMachine.setNull() below (which can release the last
12166 * reference and call the destructor). Important: this must be done before
12167 * accessing any members (and before AutoUninitSpan that does it as well).
12168 * This self reference will be released as the very last step on return.
12169 */
12170 ComObjPtr<SessionMachine> selfRef = this;
12171
12172 /* Enclose the state transition Ready->InUninit->NotReady */
12173 AutoUninitSpan autoUninitSpan(this);
12174 if (autoUninitSpan.uninitDone())
12175 {
12176 LogFlowThisFunc(("Already uninitialized\n"));
12177 LogFlowThisFuncLeave();
12178 return;
12179 }
12180
12181 if (autoUninitSpan.initFailed())
12182 {
12183 /* We've been called by init() because it's failed. It's not really
12184 * necessary (nor it's safe) to perform the regular uninit sequence
12185 * below, the following is enough.
12186 */
12187 LogFlowThisFunc(("Initialization failed.\n"));
12188#if defined(RT_OS_WINDOWS)
12189 if (mIPCSem)
12190 ::CloseHandle(mIPCSem);
12191 mIPCSem = NULL;
12192#elif defined(RT_OS_OS2)
12193 if (mIPCSem != NULLHANDLE)
12194 ::DosCloseMutexSem(mIPCSem);
12195 mIPCSem = NULLHANDLE;
12196#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12197 if (mIPCSem >= 0)
12198 ::semctl(mIPCSem, 0, IPC_RMID);
12199 mIPCSem = -1;
12200# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12201 mIPCKey = "0";
12202# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12203#else
12204# error "Port me!"
12205#endif
12206 uninitDataAndChildObjects();
12207 mData.free();
12208 unconst(mParent) = NULL;
12209 unconst(mPeer) = NULL;
12210 LogFlowThisFuncLeave();
12211 return;
12212 }
12213
12214 MachineState_T lastState;
12215 {
12216 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12217 lastState = mData->mMachineState;
12218 }
12219 NOREF(lastState);
12220
12221#ifdef VBOX_WITH_USB
12222 // release all captured USB devices, but do this before requesting the locks below
12223 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12224 {
12225 /* Console::captureUSBDevices() is called in the VM process only after
12226 * setting the machine state to Starting or Restoring.
12227 * Console::detachAllUSBDevices() will be called upon successful
12228 * termination. So, we need to release USB devices only if there was
12229 * an abnormal termination of a running VM.
12230 *
12231 * This is identical to SessionMachine::DetachAllUSBDevices except
12232 * for the aAbnormal argument. */
12233 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12234 AssertComRC(rc);
12235 NOREF(rc);
12236
12237 USBProxyService *service = mParent->host()->usbProxyService();
12238 if (service)
12239 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12240 }
12241#endif /* VBOX_WITH_USB */
12242
12243 // we need to lock this object in uninit() because the lock is shared
12244 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12245 // and others need mParent lock, and USB needs host lock.
12246 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12247
12248#if 0
12249 // Trigger async cleanup tasks, avoid doing things here which are not
12250 // vital to be done immediately and maybe need more locks. This calls
12251 // Machine::unregisterMetrics().
12252 mParent->onMachineUninit(mPeer);
12253#else
12254 /*
12255 * It is safe to call Machine::unregisterMetrics() here because
12256 * PerformanceCollector::samplerCallback no longer accesses guest methods
12257 * holding the lock.
12258 */
12259 unregisterMetrics(mParent->performanceCollector(), mPeer);
12260#endif
12261 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12262 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12263 this, __PRETTY_FUNCTION__, mCollectorGuest));
12264 if (mCollectorGuest)
12265 {
12266 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
12267 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12268 mCollectorGuest = NULL;
12269 }
12270
12271 if (aReason == Uninit::Abnormal)
12272 {
12273 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12274 Global::IsOnlineOrTransient(lastState)));
12275
12276 /* reset the state to Aborted */
12277 if (mData->mMachineState != MachineState_Aborted)
12278 setMachineState(MachineState_Aborted);
12279 }
12280
12281 // any machine settings modified?
12282 if (mData->flModifications)
12283 {
12284 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12285 rollback(false /* aNotify */);
12286 }
12287
12288 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12289 || !mConsoleTaskData.mSnapshot);
12290 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12291 {
12292 LogWarningThisFunc(("canceling failed save state request!\n"));
12293 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12294 }
12295 else if (!mConsoleTaskData.mSnapshot.isNull())
12296 {
12297 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12298
12299 /* delete all differencing hard disks created (this will also attach
12300 * their parents back by rolling back mMediaData) */
12301 rollbackMedia();
12302
12303 // delete the saved state file (it might have been already created)
12304 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12305 // think it's still in use
12306 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12307 mConsoleTaskData.mSnapshot->uninit();
12308 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12309 }
12310
12311 if (!mData->mSession.mType.isEmpty())
12312 {
12313 /* mType is not null when this machine's process has been started by
12314 * Machine::LaunchVMProcess(), therefore it is our child. We
12315 * need to queue the PID to reap the process (and avoid zombies on
12316 * Linux). */
12317 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12318 mParent->addProcessToReap(mData->mSession.mPID);
12319 }
12320
12321 mData->mSession.mPID = NIL_RTPROCESS;
12322
12323 if (aReason == Uninit::Unexpected)
12324 {
12325 /* Uninitialization didn't come from #checkForDeath(), so tell the
12326 * client watcher thread to update the set of machines that have open
12327 * sessions. */
12328 mParent->updateClientWatcher();
12329 }
12330
12331 /* uninitialize all remote controls */
12332 if (mData->mSession.mRemoteControls.size())
12333 {
12334 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12335 mData->mSession.mRemoteControls.size()));
12336
12337 Data::Session::RemoteControlList::iterator it =
12338 mData->mSession.mRemoteControls.begin();
12339 while (it != mData->mSession.mRemoteControls.end())
12340 {
12341 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12342 HRESULT rc = (*it)->Uninitialize();
12343 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12344 if (FAILED(rc))
12345 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12346 ++it;
12347 }
12348 mData->mSession.mRemoteControls.clear();
12349 }
12350
12351 /*
12352 * An expected uninitialization can come only from #checkForDeath().
12353 * Otherwise it means that something's gone really wrong (for example,
12354 * the Session implementation has released the VirtualBox reference
12355 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12356 * etc). However, it's also possible, that the client releases the IPC
12357 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12358 * but the VirtualBox release event comes first to the server process.
12359 * This case is practically possible, so we should not assert on an
12360 * unexpected uninit, just log a warning.
12361 */
12362
12363 if ((aReason == Uninit::Unexpected))
12364 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12365
12366 if (aReason != Uninit::Normal)
12367 {
12368 mData->mSession.mDirectControl.setNull();
12369 }
12370 else
12371 {
12372 /* this must be null here (see #OnSessionEnd()) */
12373 Assert(mData->mSession.mDirectControl.isNull());
12374 Assert(mData->mSession.mState == SessionState_Unlocking);
12375 Assert(!mData->mSession.mProgress.isNull());
12376 }
12377 if (mData->mSession.mProgress)
12378 {
12379 if (aReason == Uninit::Normal)
12380 mData->mSession.mProgress->notifyComplete(S_OK);
12381 else
12382 mData->mSession.mProgress->notifyComplete(E_FAIL,
12383 COM_IIDOF(ISession),
12384 getComponentName(),
12385 tr("The VM session was aborted"));
12386 mData->mSession.mProgress.setNull();
12387 }
12388
12389 /* remove the association between the peer machine and this session machine */
12390 Assert( (SessionMachine*)mData->mSession.mMachine == this
12391 || aReason == Uninit::Unexpected);
12392
12393 /* reset the rest of session data */
12394 mData->mSession.mMachine.setNull();
12395 mData->mSession.mState = SessionState_Unlocked;
12396 mData->mSession.mType.setNull();
12397
12398 /* close the interprocess semaphore before leaving the exclusive lock */
12399#if defined(RT_OS_WINDOWS)
12400 if (mIPCSem)
12401 ::CloseHandle(mIPCSem);
12402 mIPCSem = NULL;
12403#elif defined(RT_OS_OS2)
12404 if (mIPCSem != NULLHANDLE)
12405 ::DosCloseMutexSem(mIPCSem);
12406 mIPCSem = NULLHANDLE;
12407#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12408 if (mIPCSem >= 0)
12409 ::semctl(mIPCSem, 0, IPC_RMID);
12410 mIPCSem = -1;
12411# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12412 mIPCKey = "0";
12413# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12414#else
12415# error "Port me!"
12416#endif
12417
12418 /* fire an event */
12419 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12420
12421 uninitDataAndChildObjects();
12422
12423 /* free the essential data structure last */
12424 mData.free();
12425
12426 /* release the exclusive lock before setting the below two to NULL */
12427 multilock.release();
12428
12429 unconst(mParent) = NULL;
12430 unconst(mPeer) = NULL;
12431
12432 LogFlowThisFuncLeave();
12433}
12434
12435// util::Lockable interface
12436////////////////////////////////////////////////////////////////////////////////
12437
12438/**
12439 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12440 * with the primary Machine instance (mPeer).
12441 */
12442RWLockHandle *SessionMachine::lockHandle() const
12443{
12444 AssertReturn(mPeer != NULL, NULL);
12445 return mPeer->lockHandle();
12446}
12447
12448// IInternalMachineControl methods
12449////////////////////////////////////////////////////////////////////////////////
12450
12451/**
12452 * Passes collected guest statistics to performance collector object
12453 */
12454STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12455 ULONG aCpuKernel, ULONG aCpuIdle,
12456 ULONG aMemTotal, ULONG aMemFree,
12457 ULONG aMemBalloon, ULONG aMemShared,
12458 ULONG aMemCache, ULONG aPageTotal,
12459 ULONG aAllocVMM, ULONG aFreeVMM,
12460 ULONG aBalloonedVMM, ULONG aSharedVMM,
12461 ULONG aVmNetRx, ULONG aVmNetTx)
12462{
12463 if (mCollectorGuest)
12464 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12465 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12466 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12467 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12468
12469 return S_OK;
12470}
12471
12472/**
12473 * @note Locks this object for writing.
12474 */
12475STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12476{
12477 AutoCaller autoCaller(this);
12478 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12479
12480 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12481
12482 mRemoveSavedState = aRemove;
12483
12484 return S_OK;
12485}
12486
12487/**
12488 * @note Locks the same as #setMachineState() does.
12489 */
12490STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12491{
12492 return setMachineState(aMachineState);
12493}
12494
12495/**
12496 * @note Locks this object for reading.
12497 */
12498STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
12499{
12500 AutoCaller autoCaller(this);
12501 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12502
12503 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12504
12505#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
12506 mIPCSemName.cloneTo(aId);
12507 return S_OK;
12508#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12509# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12510 mIPCKey.cloneTo(aId);
12511# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12512 mData->m_strConfigFileFull.cloneTo(aId);
12513# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12514 return S_OK;
12515#else
12516# error "Port me!"
12517#endif
12518}
12519
12520/**
12521 * @note Locks this object for writing.
12522 */
12523STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12524{
12525 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12526 AutoCaller autoCaller(this);
12527 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12528
12529 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12530
12531 if (mData->mSession.mState != SessionState_Locked)
12532 return VBOX_E_INVALID_OBJECT_STATE;
12533
12534 if (!mData->mSession.mProgress.isNull())
12535 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12536
12537 LogFlowThisFunc(("returns S_OK.\n"));
12538 return S_OK;
12539}
12540
12541/**
12542 * @note Locks this object for writing.
12543 */
12544STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12545{
12546 AutoCaller autoCaller(this);
12547 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12548
12549 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12550
12551 if (mData->mSession.mState != SessionState_Locked)
12552 return VBOX_E_INVALID_OBJECT_STATE;
12553
12554 /* Finalize the LaunchVMProcess progress object. */
12555 if (mData->mSession.mProgress)
12556 {
12557 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12558 mData->mSession.mProgress.setNull();
12559 }
12560
12561 if (SUCCEEDED((HRESULT)iResult))
12562 {
12563#ifdef VBOX_WITH_RESOURCE_USAGE_API
12564 /* The VM has been powered up successfully, so it makes sense
12565 * now to offer the performance metrics for a running machine
12566 * object. Doing it earlier wouldn't be safe. */
12567 registerMetrics(mParent->performanceCollector(), mPeer,
12568 mData->mSession.mPID);
12569#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12570 }
12571
12572 return S_OK;
12573}
12574
12575/**
12576 * @note Locks this object for writing.
12577 */
12578STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12579{
12580 LogFlowThisFuncEnter();
12581
12582 CheckComArgOutPointerValid(aProgress);
12583
12584 AutoCaller autoCaller(this);
12585 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12586
12587 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12588
12589 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12590 E_FAIL);
12591
12592 /* create a progress object to track operation completion */
12593 ComObjPtr<Progress> pProgress;
12594 pProgress.createObject();
12595 pProgress->init(getVirtualBox(),
12596 static_cast<IMachine *>(this) /* aInitiator */,
12597 Bstr(tr("Stopping the virtual machine")).raw(),
12598 FALSE /* aCancelable */);
12599
12600 /* fill in the console task data */
12601 mConsoleTaskData.mLastState = mData->mMachineState;
12602 mConsoleTaskData.mProgress = pProgress;
12603
12604 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12605 setMachineState(MachineState_Stopping);
12606
12607 pProgress.queryInterfaceTo(aProgress);
12608
12609 return S_OK;
12610}
12611
12612/**
12613 * @note Locks this object for writing.
12614 */
12615STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12616{
12617 LogFlowThisFuncEnter();
12618
12619 AutoCaller autoCaller(this);
12620 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12621
12622 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12623
12624 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12625 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12626 && mConsoleTaskData.mLastState != MachineState_Null,
12627 E_FAIL);
12628
12629 /*
12630 * On failure, set the state to the state we had when BeginPoweringDown()
12631 * was called (this is expected by Console::PowerDown() and the associated
12632 * task). On success the VM process already changed the state to
12633 * MachineState_PoweredOff, so no need to do anything.
12634 */
12635 if (FAILED(iResult))
12636 setMachineState(mConsoleTaskData.mLastState);
12637
12638 /* notify the progress object about operation completion */
12639 Assert(mConsoleTaskData.mProgress);
12640 if (SUCCEEDED(iResult))
12641 mConsoleTaskData.mProgress->notifyComplete(S_OK);
12642 else
12643 {
12644 Utf8Str strErrMsg(aErrMsg);
12645 if (strErrMsg.length())
12646 mConsoleTaskData.mProgress->notifyComplete(iResult,
12647 COM_IIDOF(ISession),
12648 getComponentName(),
12649 strErrMsg.c_str());
12650 else
12651 mConsoleTaskData.mProgress->notifyComplete(iResult);
12652 }
12653
12654 /* clear out the temporary saved state data */
12655 mConsoleTaskData.mLastState = MachineState_Null;
12656 mConsoleTaskData.mProgress.setNull();
12657
12658 LogFlowThisFuncLeave();
12659 return S_OK;
12660}
12661
12662
12663/**
12664 * Goes through the USB filters of the given machine to see if the given
12665 * device matches any filter or not.
12666 *
12667 * @note Locks the same as USBController::hasMatchingFilter() does.
12668 */
12669STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12670 BOOL *aMatched,
12671 ULONG *aMaskedIfs)
12672{
12673 LogFlowThisFunc(("\n"));
12674
12675 CheckComArgNotNull(aUSBDevice);
12676 CheckComArgOutPointerValid(aMatched);
12677
12678 AutoCaller autoCaller(this);
12679 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12680
12681#ifdef VBOX_WITH_USB
12682 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
12683#else
12684 NOREF(aUSBDevice);
12685 NOREF(aMaskedIfs);
12686 *aMatched = FALSE;
12687#endif
12688
12689 return S_OK;
12690}
12691
12692/**
12693 * @note Locks the same as Host::captureUSBDevice() does.
12694 */
12695STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12696{
12697 LogFlowThisFunc(("\n"));
12698
12699 AutoCaller autoCaller(this);
12700 AssertComRCReturnRC(autoCaller.rc());
12701
12702#ifdef VBOX_WITH_USB
12703 /* if captureDeviceForVM() fails, it must have set extended error info */
12704 clearError();
12705 MultiResult rc = mParent->host()->checkUSBProxyService();
12706 if (FAILED(rc)) return rc;
12707
12708 USBProxyService *service = mParent->host()->usbProxyService();
12709 AssertReturn(service, E_FAIL);
12710 return service->captureDeviceForVM(this, Guid(aId).ref());
12711#else
12712 NOREF(aId);
12713 return E_NOTIMPL;
12714#endif
12715}
12716
12717/**
12718 * @note Locks the same as Host::detachUSBDevice() does.
12719 */
12720STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12721{
12722 LogFlowThisFunc(("\n"));
12723
12724 AutoCaller autoCaller(this);
12725 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12726
12727#ifdef VBOX_WITH_USB
12728 USBProxyService *service = mParent->host()->usbProxyService();
12729 AssertReturn(service, E_FAIL);
12730 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12731#else
12732 NOREF(aId);
12733 NOREF(aDone);
12734 return E_NOTIMPL;
12735#endif
12736}
12737
12738/**
12739 * Inserts all machine filters to the USB proxy service and then calls
12740 * Host::autoCaptureUSBDevices().
12741 *
12742 * Called by Console from the VM process upon VM startup.
12743 *
12744 * @note Locks what called methods lock.
12745 */
12746STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12747{
12748 LogFlowThisFunc(("\n"));
12749
12750 AutoCaller autoCaller(this);
12751 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12752
12753#ifdef VBOX_WITH_USB
12754 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
12755 AssertComRC(rc);
12756 NOREF(rc);
12757
12758 USBProxyService *service = mParent->host()->usbProxyService();
12759 AssertReturn(service, E_FAIL);
12760 return service->autoCaptureDevicesForVM(this);
12761#else
12762 return S_OK;
12763#endif
12764}
12765
12766/**
12767 * Removes all machine filters from the USB proxy service and then calls
12768 * Host::detachAllUSBDevices().
12769 *
12770 * Called by Console from the VM process upon normal VM termination or by
12771 * SessionMachine::uninit() upon abnormal VM termination (from under the
12772 * Machine/SessionMachine lock).
12773 *
12774 * @note Locks what called methods lock.
12775 */
12776STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12777{
12778 LogFlowThisFunc(("\n"));
12779
12780 AutoCaller autoCaller(this);
12781 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12782
12783#ifdef VBOX_WITH_USB
12784 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12785 AssertComRC(rc);
12786 NOREF(rc);
12787
12788 USBProxyService *service = mParent->host()->usbProxyService();
12789 AssertReturn(service, E_FAIL);
12790 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12791#else
12792 NOREF(aDone);
12793 return S_OK;
12794#endif
12795}
12796
12797/**
12798 * @note Locks this object for writing.
12799 */
12800STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12801 IProgress **aProgress)
12802{
12803 LogFlowThisFuncEnter();
12804
12805 AssertReturn(aSession, E_INVALIDARG);
12806 AssertReturn(aProgress, E_INVALIDARG);
12807
12808 AutoCaller autoCaller(this);
12809
12810 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
12811 /*
12812 * We don't assert below because it might happen that a non-direct session
12813 * informs us it is closed right after we've been uninitialized -- it's ok.
12814 */
12815 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12816
12817 /* get IInternalSessionControl interface */
12818 ComPtr<IInternalSessionControl> control(aSession);
12819
12820 ComAssertRet(!control.isNull(), E_INVALIDARG);
12821
12822 /* Creating a Progress object requires the VirtualBox lock, and
12823 * thus locking it here is required by the lock order rules. */
12824 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12825
12826 if (control == mData->mSession.mDirectControl)
12827 {
12828 ComAssertRet(aProgress, E_POINTER);
12829
12830 /* The direct session is being normally closed by the client process
12831 * ----------------------------------------------------------------- */
12832
12833 /* go to the closing state (essential for all open*Session() calls and
12834 * for #checkForDeath()) */
12835 Assert(mData->mSession.mState == SessionState_Locked);
12836 mData->mSession.mState = SessionState_Unlocking;
12837
12838 /* set direct control to NULL to release the remote instance */
12839 mData->mSession.mDirectControl.setNull();
12840 LogFlowThisFunc(("Direct control is set to NULL\n"));
12841
12842 if (mData->mSession.mProgress)
12843 {
12844 /* finalize the progress, someone might wait if a frontend
12845 * closes the session before powering on the VM. */
12846 mData->mSession.mProgress->notifyComplete(E_FAIL,
12847 COM_IIDOF(ISession),
12848 getComponentName(),
12849 tr("The VM session was closed before any attempt to power it on"));
12850 mData->mSession.mProgress.setNull();
12851 }
12852
12853 /* Create the progress object the client will use to wait until
12854 * #checkForDeath() is called to uninitialize this session object after
12855 * it releases the IPC semaphore.
12856 * Note! Because we're "reusing" mProgress here, this must be a proxy
12857 * object just like for LaunchVMProcess. */
12858 Assert(mData->mSession.mProgress.isNull());
12859 ComObjPtr<ProgressProxy> progress;
12860 progress.createObject();
12861 ComPtr<IUnknown> pPeer(mPeer);
12862 progress->init(mParent, pPeer,
12863 Bstr(tr("Closing session")).raw(),
12864 FALSE /* aCancelable */);
12865 progress.queryInterfaceTo(aProgress);
12866 mData->mSession.mProgress = progress;
12867 }
12868 else
12869 {
12870 /* the remote session is being normally closed */
12871 Data::Session::RemoteControlList::iterator it =
12872 mData->mSession.mRemoteControls.begin();
12873 while (it != mData->mSession.mRemoteControls.end())
12874 {
12875 if (control == *it)
12876 break;
12877 ++it;
12878 }
12879 BOOL found = it != mData->mSession.mRemoteControls.end();
12880 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12881 E_INVALIDARG);
12882 // This MUST be erase(it), not remove(*it) as the latter triggers a
12883 // very nasty use after free due to the place where the value "lives".
12884 mData->mSession.mRemoteControls.erase(it);
12885 }
12886
12887 /* signal the client watcher thread, because the client is going away */
12888 mParent->updateClientWatcher();
12889
12890 LogFlowThisFuncLeave();
12891 return S_OK;
12892}
12893
12894/**
12895 * @note Locks this object for writing.
12896 */
12897STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12898{
12899 LogFlowThisFuncEnter();
12900
12901 CheckComArgOutPointerValid(aProgress);
12902 CheckComArgOutPointerValid(aStateFilePath);
12903
12904 AutoCaller autoCaller(this);
12905 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12906
12907 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12908
12909 AssertReturn( mData->mMachineState == MachineState_Paused
12910 && mConsoleTaskData.mLastState == MachineState_Null
12911 && mConsoleTaskData.strStateFilePath.isEmpty(),
12912 E_FAIL);
12913
12914 /* create a progress object to track operation completion */
12915 ComObjPtr<Progress> pProgress;
12916 pProgress.createObject();
12917 pProgress->init(getVirtualBox(),
12918 static_cast<IMachine *>(this) /* aInitiator */,
12919 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12920 FALSE /* aCancelable */);
12921
12922 Utf8Str strStateFilePath;
12923 /* stateFilePath is null when the machine is not running */
12924 if (mData->mMachineState == MachineState_Paused)
12925 composeSavedStateFilename(strStateFilePath);
12926
12927 /* fill in the console task data */
12928 mConsoleTaskData.mLastState = mData->mMachineState;
12929 mConsoleTaskData.strStateFilePath = strStateFilePath;
12930 mConsoleTaskData.mProgress = pProgress;
12931
12932 /* set the state to Saving (this is expected by Console::SaveState()) */
12933 setMachineState(MachineState_Saving);
12934
12935 strStateFilePath.cloneTo(aStateFilePath);
12936 pProgress.queryInterfaceTo(aProgress);
12937
12938 return S_OK;
12939}
12940
12941/**
12942 * @note Locks mParent + this object for writing.
12943 */
12944STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12945{
12946 LogFlowThisFunc(("\n"));
12947
12948 AutoCaller autoCaller(this);
12949 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12950
12951 /* endSavingState() need mParent lock */
12952 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12953
12954 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12955 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12956 && mConsoleTaskData.mLastState != MachineState_Null
12957 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12958 E_FAIL);
12959
12960 /*
12961 * On failure, set the state to the state we had when BeginSavingState()
12962 * was called (this is expected by Console::SaveState() and the associated
12963 * task). On success the VM process already changed the state to
12964 * MachineState_Saved, so no need to do anything.
12965 */
12966 if (FAILED(iResult))
12967 setMachineState(mConsoleTaskData.mLastState);
12968
12969 return endSavingState(iResult, aErrMsg);
12970}
12971
12972/**
12973 * @note Locks this object for writing.
12974 */
12975STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12976{
12977 LogFlowThisFunc(("\n"));
12978
12979 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12980
12981 AutoCaller autoCaller(this);
12982 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12983
12984 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12985
12986 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12987 || mData->mMachineState == MachineState_Teleported
12988 || mData->mMachineState == MachineState_Aborted
12989 , E_FAIL); /** @todo setError. */
12990
12991 Utf8Str stateFilePathFull = aSavedStateFile;
12992 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
12993 if (RT_FAILURE(vrc))
12994 return setError(VBOX_E_FILE_ERROR,
12995 tr("Invalid saved state file path '%ls' (%Rrc)"),
12996 aSavedStateFile,
12997 vrc);
12998
12999 mSSData->strStateFilePath = stateFilePathFull;
13000
13001 /* The below setMachineState() will detect the state transition and will
13002 * update the settings file */
13003
13004 return setMachineState(MachineState_Saved);
13005}
13006
13007STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13008 ComSafeArrayOut(BSTR, aValues),
13009 ComSafeArrayOut(LONG64, aTimestamps),
13010 ComSafeArrayOut(BSTR, aFlags))
13011{
13012 LogFlowThisFunc(("\n"));
13013
13014#ifdef VBOX_WITH_GUEST_PROPS
13015 using namespace guestProp;
13016
13017 AutoCaller autoCaller(this);
13018 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13019
13020 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13021
13022 CheckComArgOutSafeArrayPointerValid(aNames);
13023 CheckComArgOutSafeArrayPointerValid(aValues);
13024 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13025 CheckComArgOutSafeArrayPointerValid(aFlags);
13026
13027 size_t cEntries = mHWData->mGuestProperties.size();
13028 com::SafeArray<BSTR> names(cEntries);
13029 com::SafeArray<BSTR> values(cEntries);
13030 com::SafeArray<LONG64> timestamps(cEntries);
13031 com::SafeArray<BSTR> flags(cEntries);
13032 unsigned i = 0;
13033 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13034 it != mHWData->mGuestProperties.end();
13035 ++it)
13036 {
13037 char szFlags[MAX_FLAGS_LEN + 1];
13038 it->first.cloneTo(&names[i]);
13039 it->second.strValue.cloneTo(&values[i]);
13040 timestamps[i] = it->second.mTimestamp;
13041 /* If it is NULL, keep it NULL. */
13042 if (it->second.mFlags)
13043 {
13044 writeFlags(it->second.mFlags, szFlags);
13045 Bstr(szFlags).cloneTo(&flags[i]);
13046 }
13047 else
13048 flags[i] = NULL;
13049 ++i;
13050 }
13051 names.detachTo(ComSafeArrayOutArg(aNames));
13052 values.detachTo(ComSafeArrayOutArg(aValues));
13053 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13054 flags.detachTo(ComSafeArrayOutArg(aFlags));
13055 return S_OK;
13056#else
13057 ReturnComNotImplemented();
13058#endif
13059}
13060
13061STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13062 IN_BSTR aValue,
13063 LONG64 aTimestamp,
13064 IN_BSTR aFlags)
13065{
13066 LogFlowThisFunc(("\n"));
13067
13068#ifdef VBOX_WITH_GUEST_PROPS
13069 using namespace guestProp;
13070
13071 CheckComArgStrNotEmptyOrNull(aName);
13072 CheckComArgNotNull(aValue);
13073 CheckComArgNotNull(aFlags);
13074
13075 try
13076 {
13077 /*
13078 * Convert input up front.
13079 */
13080 Utf8Str utf8Name(aName);
13081 uint32_t fFlags = NILFLAG;
13082 if (aFlags)
13083 {
13084 Utf8Str utf8Flags(aFlags);
13085 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13086 AssertRCReturn(vrc, E_INVALIDARG);
13087 }
13088
13089 /*
13090 * Now grab the object lock, validate the state and do the update.
13091 */
13092 AutoCaller autoCaller(this);
13093 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13094
13095 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13096
13097 switch (mData->mMachineState)
13098 {
13099 case MachineState_Paused:
13100 case MachineState_Running:
13101 case MachineState_Teleporting:
13102 case MachineState_TeleportingPausedVM:
13103 case MachineState_LiveSnapshotting:
13104 case MachineState_DeletingSnapshotOnline:
13105 case MachineState_DeletingSnapshotPaused:
13106 case MachineState_Saving:
13107 case MachineState_Stopping:
13108 break;
13109
13110 default:
13111 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13112 VBOX_E_INVALID_VM_STATE);
13113 }
13114
13115 setModified(IsModified_MachineData);
13116 mHWData.backup();
13117
13118 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13119 if (it != mHWData->mGuestProperties.end())
13120 {
13121 if (RT_VALID_PTR(aValue) && *(aValue) != '\0')
13122 {
13123 it->second.strValue = aValue;
13124 it->second.mFlags = fFlags;
13125 it->second.mTimestamp = aTimestamp;
13126 }
13127 else
13128 mHWData->mGuestProperties.erase(it);
13129
13130 mData->mGuestPropertiesModified = TRUE;
13131 }
13132
13133 /*
13134 * Send a callback notification if appropriate
13135 */
13136 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13137 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13138 RTSTR_MAX,
13139 utf8Name.c_str(),
13140 RTSTR_MAX, NULL)
13141 )
13142 {
13143 alock.release();
13144
13145 mParent->onGuestPropertyChange(mData->mUuid,
13146 aName,
13147 aValue,
13148 aFlags);
13149 }
13150 }
13151 catch (...)
13152 {
13153 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13154 }
13155 return S_OK;
13156#else
13157 ReturnComNotImplemented();
13158#endif
13159}
13160
13161STDMETHODIMP SessionMachine::LockMedia()
13162{
13163 AutoCaller autoCaller(this);
13164 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13165
13166 AutoMultiWriteLock2 alock(this->lockHandle(),
13167 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13168
13169 AssertReturn( mData->mMachineState == MachineState_Starting
13170 || mData->mMachineState == MachineState_Restoring
13171 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13172
13173 clearError();
13174 alock.release();
13175 return lockMedia();
13176}
13177
13178STDMETHODIMP SessionMachine::UnlockMedia()
13179{
13180 unlockMedia();
13181 return S_OK;
13182}
13183
13184STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13185 IMediumAttachment **aNewAttachment)
13186{
13187 CheckComArgNotNull(aAttachment);
13188 CheckComArgOutPointerValid(aNewAttachment);
13189
13190 AutoCaller autoCaller(this);
13191 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13192
13193 // request the host lock first, since might be calling Host methods for getting host drives;
13194 // next, protect the media tree all the while we're in here, as well as our member variables
13195 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13196 this->lockHandle(),
13197 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13198
13199 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13200
13201 Bstr ctrlName;
13202 LONG lPort;
13203 LONG lDevice;
13204 bool fTempEject;
13205 {
13206 AutoCaller autoAttachCaller(this);
13207 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13208
13209 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13210
13211 /* Need to query the details first, as the IMediumAttachment reference
13212 * might be to the original settings, which we are going to change. */
13213 ctrlName = pAttach->getControllerName();
13214 lPort = pAttach->getPort();
13215 lDevice = pAttach->getDevice();
13216 fTempEject = pAttach->getTempEject();
13217 }
13218
13219 if (!fTempEject)
13220 {
13221 /* Remember previously mounted medium. The medium before taking the
13222 * backup is not necessarily the same thing. */
13223 ComObjPtr<Medium> oldmedium;
13224 oldmedium = pAttach->getMedium();
13225
13226 setModified(IsModified_Storage);
13227 mMediaData.backup();
13228
13229 // The backup operation makes the pAttach reference point to the
13230 // old settings. Re-get the correct reference.
13231 pAttach = findAttachment(mMediaData->mAttachments,
13232 ctrlName.raw(),
13233 lPort,
13234 lDevice);
13235
13236 {
13237 AutoCaller autoAttachCaller(this);
13238 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13239
13240 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13241 if (!oldmedium.isNull())
13242 oldmedium->removeBackReference(mData->mUuid);
13243
13244 pAttach->updateMedium(NULL);
13245 pAttach->updateEjected();
13246 }
13247
13248 setModified(IsModified_Storage);
13249 }
13250 else
13251 {
13252 {
13253 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13254 pAttach->updateEjected();
13255 }
13256 }
13257
13258 pAttach.queryInterfaceTo(aNewAttachment);
13259
13260 return S_OK;
13261}
13262
13263// public methods only for internal purposes
13264/////////////////////////////////////////////////////////////////////////////
13265
13266/**
13267 * Called from the client watcher thread to check for expected or unexpected
13268 * death of the client process that has a direct session to this machine.
13269 *
13270 * On Win32 and on OS/2, this method is called only when we've got the
13271 * mutex (i.e. the client has either died or terminated normally) so it always
13272 * returns @c true (the client is terminated, the session machine is
13273 * uninitialized).
13274 *
13275 * On other platforms, the method returns @c true if the client process has
13276 * terminated normally or abnormally and the session machine was uninitialized,
13277 * and @c false if the client process is still alive.
13278 *
13279 * @note Locks this object for writing.
13280 */
13281bool SessionMachine::checkForDeath()
13282{
13283 Uninit::Reason reason;
13284 bool terminated = false;
13285
13286 /* Enclose autoCaller with a block because calling uninit() from under it
13287 * will deadlock. */
13288 {
13289 AutoCaller autoCaller(this);
13290 if (!autoCaller.isOk())
13291 {
13292 /* return true if not ready, to cause the client watcher to exclude
13293 * the corresponding session from watching */
13294 LogFlowThisFunc(("Already uninitialized!\n"));
13295 return true;
13296 }
13297
13298 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13299
13300 /* Determine the reason of death: if the session state is Closing here,
13301 * everything is fine. Otherwise it means that the client did not call
13302 * OnSessionEnd() before it released the IPC semaphore. This may happen
13303 * either because the client process has abnormally terminated, or
13304 * because it simply forgot to call ISession::Close() before exiting. We
13305 * threat the latter also as an abnormal termination (see
13306 * Session::uninit() for details). */
13307 reason = mData->mSession.mState == SessionState_Unlocking ?
13308 Uninit::Normal :
13309 Uninit::Abnormal;
13310
13311#if defined(RT_OS_WINDOWS)
13312
13313 AssertMsg(mIPCSem, ("semaphore must be created"));
13314
13315 /* release the IPC mutex */
13316 ::ReleaseMutex(mIPCSem);
13317
13318 terminated = true;
13319
13320#elif defined(RT_OS_OS2)
13321
13322 AssertMsg(mIPCSem, ("semaphore must be created"));
13323
13324 /* release the IPC mutex */
13325 ::DosReleaseMutexSem(mIPCSem);
13326
13327 terminated = true;
13328
13329#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
13330
13331 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
13332
13333 int val = ::semctl(mIPCSem, 0, GETVAL);
13334 if (val > 0)
13335 {
13336 /* the semaphore is signaled, meaning the session is terminated */
13337 terminated = true;
13338 }
13339
13340#else
13341# error "Port me!"
13342#endif
13343
13344 } /* AutoCaller block */
13345
13346 if (terminated)
13347 uninit(reason);
13348
13349 return terminated;
13350}
13351
13352/**
13353 * @note Locks this object for reading.
13354 */
13355HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13356{
13357 LogFlowThisFunc(("\n"));
13358
13359 AutoCaller autoCaller(this);
13360 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13361
13362 ComPtr<IInternalSessionControl> directControl;
13363 {
13364 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13365 directControl = mData->mSession.mDirectControl;
13366 }
13367
13368 /* ignore notifications sent after #OnSessionEnd() is called */
13369 if (!directControl)
13370 return S_OK;
13371
13372 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13373}
13374
13375/**
13376 * @note Locks this object for reading.
13377 */
13378HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13379 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
13380{
13381 LogFlowThisFunc(("\n"));
13382
13383 AutoCaller autoCaller(this);
13384 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13385
13386 ComPtr<IInternalSessionControl> directControl;
13387 {
13388 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13389 directControl = mData->mSession.mDirectControl;
13390 }
13391
13392 /* ignore notifications sent after #OnSessionEnd() is called */
13393 if (!directControl)
13394 return S_OK;
13395 /*
13396 * instead acting like callback we ask IVirtualBox deliver corresponding event
13397 */
13398
13399 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13400 return S_OK;
13401}
13402
13403/**
13404 * @note Locks this object for reading.
13405 */
13406HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
13407{
13408 LogFlowThisFunc(("\n"));
13409
13410 AutoCaller autoCaller(this);
13411 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13412
13413 ComPtr<IInternalSessionControl> directControl;
13414 {
13415 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13416 directControl = mData->mSession.mDirectControl;
13417 }
13418
13419 /* ignore notifications sent after #OnSessionEnd() is called */
13420 if (!directControl)
13421 return S_OK;
13422
13423 return directControl->OnSerialPortChange(serialPort);
13424}
13425
13426/**
13427 * @note Locks this object for reading.
13428 */
13429HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
13430{
13431 LogFlowThisFunc(("\n"));
13432
13433 AutoCaller autoCaller(this);
13434 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13435
13436 ComPtr<IInternalSessionControl> directControl;
13437 {
13438 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13439 directControl = mData->mSession.mDirectControl;
13440 }
13441
13442 /* ignore notifications sent after #OnSessionEnd() is called */
13443 if (!directControl)
13444 return S_OK;
13445
13446 return directControl->OnParallelPortChange(parallelPort);
13447}
13448
13449/**
13450 * @note Locks this object for reading.
13451 */
13452HRESULT SessionMachine::onStorageControllerChange()
13453{
13454 LogFlowThisFunc(("\n"));
13455
13456 AutoCaller autoCaller(this);
13457 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13458
13459 ComPtr<IInternalSessionControl> directControl;
13460 {
13461 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13462 directControl = mData->mSession.mDirectControl;
13463 }
13464
13465 /* ignore notifications sent after #OnSessionEnd() is called */
13466 if (!directControl)
13467 return S_OK;
13468
13469 return directControl->OnStorageControllerChange();
13470}
13471
13472/**
13473 * @note Locks this object for reading.
13474 */
13475HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13476{
13477 LogFlowThisFunc(("\n"));
13478
13479 AutoCaller autoCaller(this);
13480 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13481
13482 ComPtr<IInternalSessionControl> directControl;
13483 {
13484 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13485 directControl = mData->mSession.mDirectControl;
13486 }
13487
13488 /* ignore notifications sent after #OnSessionEnd() is called */
13489 if (!directControl)
13490 return S_OK;
13491
13492 return directControl->OnMediumChange(aAttachment, aForce);
13493}
13494
13495/**
13496 * @note Locks this object for reading.
13497 */
13498HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
13499{
13500 LogFlowThisFunc(("\n"));
13501
13502 AutoCaller autoCaller(this);
13503 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13504
13505 ComPtr<IInternalSessionControl> directControl;
13506 {
13507 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13508 directControl = mData->mSession.mDirectControl;
13509 }
13510
13511 /* ignore notifications sent after #OnSessionEnd() is called */
13512 if (!directControl)
13513 return S_OK;
13514
13515 return directControl->OnCPUChange(aCPU, aRemove);
13516}
13517
13518HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
13519{
13520 LogFlowThisFunc(("\n"));
13521
13522 AutoCaller autoCaller(this);
13523 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13524
13525 ComPtr<IInternalSessionControl> directControl;
13526 {
13527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13528 directControl = mData->mSession.mDirectControl;
13529 }
13530
13531 /* ignore notifications sent after #OnSessionEnd() is called */
13532 if (!directControl)
13533 return S_OK;
13534
13535 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13536}
13537
13538/**
13539 * @note Locks this object for reading.
13540 */
13541HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
13542{
13543 LogFlowThisFunc(("\n"));
13544
13545 AutoCaller autoCaller(this);
13546 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13547
13548 ComPtr<IInternalSessionControl> directControl;
13549 {
13550 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13551 directControl = mData->mSession.mDirectControl;
13552 }
13553
13554 /* ignore notifications sent after #OnSessionEnd() is called */
13555 if (!directControl)
13556 return S_OK;
13557
13558 return directControl->OnVRDEServerChange(aRestart);
13559}
13560
13561/**
13562 * @note Locks this object for reading.
13563 */
13564HRESULT SessionMachine::onUSBControllerChange()
13565{
13566 LogFlowThisFunc(("\n"));
13567
13568 AutoCaller autoCaller(this);
13569 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13570
13571 ComPtr<IInternalSessionControl> directControl;
13572 {
13573 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13574 directControl = mData->mSession.mDirectControl;
13575 }
13576
13577 /* ignore notifications sent after #OnSessionEnd() is called */
13578 if (!directControl)
13579 return S_OK;
13580
13581 return directControl->OnUSBControllerChange();
13582}
13583
13584/**
13585 * @note Locks this object for reading.
13586 */
13587HRESULT SessionMachine::onSharedFolderChange()
13588{
13589 LogFlowThisFunc(("\n"));
13590
13591 AutoCaller autoCaller(this);
13592 AssertComRCReturnRC(autoCaller.rc());
13593
13594 ComPtr<IInternalSessionControl> directControl;
13595 {
13596 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13597 directControl = mData->mSession.mDirectControl;
13598 }
13599
13600 /* ignore notifications sent after #OnSessionEnd() is called */
13601 if (!directControl)
13602 return S_OK;
13603
13604 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13605}
13606
13607/**
13608 * @note Locks this object for reading.
13609 */
13610HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
13611{
13612 LogFlowThisFunc(("\n"));
13613
13614 AutoCaller autoCaller(this);
13615 AssertComRCReturnRC(autoCaller.rc());
13616
13617 ComPtr<IInternalSessionControl> directControl;
13618 {
13619 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13620 directControl = mData->mSession.mDirectControl;
13621 }
13622
13623 /* ignore notifications sent after #OnSessionEnd() is called */
13624 if (!directControl)
13625 return S_OK;
13626
13627 return directControl->OnClipboardModeChange(aClipboardMode);
13628}
13629
13630/**
13631 * @note Locks this object for reading.
13632 */
13633HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
13634{
13635 LogFlowThisFunc(("\n"));
13636
13637 AutoCaller autoCaller(this);
13638 AssertComRCReturnRC(autoCaller.rc());
13639
13640 ComPtr<IInternalSessionControl> directControl;
13641 {
13642 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13643 directControl = mData->mSession.mDirectControl;
13644 }
13645
13646 /* ignore notifications sent after #OnSessionEnd() is called */
13647 if (!directControl)
13648 return S_OK;
13649
13650 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
13651}
13652
13653/**
13654 * @note Locks this object for reading.
13655 */
13656HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13657{
13658 LogFlowThisFunc(("\n"));
13659
13660 AutoCaller autoCaller(this);
13661 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13662
13663 ComPtr<IInternalSessionControl> directControl;
13664 {
13665 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13666 directControl = mData->mSession.mDirectControl;
13667 }
13668
13669 /* ignore notifications sent after #OnSessionEnd() is called */
13670 if (!directControl)
13671 return S_OK;
13672
13673 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13674}
13675
13676/**
13677 * @note Locks this object for reading.
13678 */
13679HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13680{
13681 LogFlowThisFunc(("\n"));
13682
13683 AutoCaller autoCaller(this);
13684 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13685
13686 ComPtr<IInternalSessionControl> directControl;
13687 {
13688 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13689 directControl = mData->mSession.mDirectControl;
13690 }
13691
13692 /* ignore notifications sent after #OnSessionEnd() is called */
13693 if (!directControl)
13694 return S_OK;
13695
13696 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13697}
13698
13699/**
13700 * Returns @c true if this machine's USB controller reports it has a matching
13701 * filter for the given USB device and @c false otherwise.
13702 *
13703 * @note locks this object for reading.
13704 */
13705bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13706{
13707 AutoCaller autoCaller(this);
13708 /* silently return if not ready -- this method may be called after the
13709 * direct machine session has been called */
13710 if (!autoCaller.isOk())
13711 return false;
13712
13713#ifdef VBOX_WITH_USB
13714 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13715
13716 switch (mData->mMachineState)
13717 {
13718 case MachineState_Starting:
13719 case MachineState_Restoring:
13720 case MachineState_TeleportingIn:
13721 case MachineState_Paused:
13722 case MachineState_Running:
13723 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13724 * elsewhere... */
13725 alock.release();
13726 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
13727 default: break;
13728 }
13729#else
13730 NOREF(aDevice);
13731 NOREF(aMaskedIfs);
13732#endif
13733 return false;
13734}
13735
13736/**
13737 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13738 */
13739HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
13740 IVirtualBoxErrorInfo *aError,
13741 ULONG aMaskedIfs)
13742{
13743 LogFlowThisFunc(("\n"));
13744
13745 AutoCaller autoCaller(this);
13746
13747 /* This notification may happen after the machine object has been
13748 * uninitialized (the session was closed), so don't assert. */
13749 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13750
13751 ComPtr<IInternalSessionControl> directControl;
13752 {
13753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13754 directControl = mData->mSession.mDirectControl;
13755 }
13756
13757 /* fail on notifications sent after #OnSessionEnd() is called, it is
13758 * expected by the caller */
13759 if (!directControl)
13760 return E_FAIL;
13761
13762 /* No locks should be held at this point. */
13763 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13764 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13765
13766 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13767}
13768
13769/**
13770 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13771 */
13772HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
13773 IVirtualBoxErrorInfo *aError)
13774{
13775 LogFlowThisFunc(("\n"));
13776
13777 AutoCaller autoCaller(this);
13778
13779 /* This notification may happen after the machine object has been
13780 * uninitialized (the session was closed), so don't assert. */
13781 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13782
13783 ComPtr<IInternalSessionControl> directControl;
13784 {
13785 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13786 directControl = mData->mSession.mDirectControl;
13787 }
13788
13789 /* fail on notifications sent after #OnSessionEnd() is called, it is
13790 * expected by the caller */
13791 if (!directControl)
13792 return E_FAIL;
13793
13794 /* No locks should be held at this point. */
13795 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13796 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13797
13798 return directControl->OnUSBDeviceDetach(aId, aError);
13799}
13800
13801// protected methods
13802/////////////////////////////////////////////////////////////////////////////
13803
13804/**
13805 * Helper method to finalize saving the state.
13806 *
13807 * @note Must be called from under this object's lock.
13808 *
13809 * @param aRc S_OK if the snapshot has been taken successfully
13810 * @param aErrMsg human readable error message for failure
13811 *
13812 * @note Locks mParent + this objects for writing.
13813 */
13814HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13815{
13816 LogFlowThisFuncEnter();
13817
13818 AutoCaller autoCaller(this);
13819 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13820
13821 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13822
13823 HRESULT rc = S_OK;
13824
13825 if (SUCCEEDED(aRc))
13826 {
13827 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13828
13829 /* save all VM settings */
13830 rc = saveSettings(NULL);
13831 // no need to check whether VirtualBox.xml needs saving also since
13832 // we can't have a name change pending at this point
13833 }
13834 else
13835 {
13836 // delete the saved state file (it might have been already created);
13837 // we need not check whether this is shared with a snapshot here because
13838 // we certainly created this saved state file here anew
13839 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13840 }
13841
13842 /* notify the progress object about operation completion */
13843 Assert(mConsoleTaskData.mProgress);
13844 if (SUCCEEDED(aRc))
13845 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13846 else
13847 {
13848 if (aErrMsg.length())
13849 mConsoleTaskData.mProgress->notifyComplete(aRc,
13850 COM_IIDOF(ISession),
13851 getComponentName(),
13852 aErrMsg.c_str());
13853 else
13854 mConsoleTaskData.mProgress->notifyComplete(aRc);
13855 }
13856
13857 /* clear out the temporary saved state data */
13858 mConsoleTaskData.mLastState = MachineState_Null;
13859 mConsoleTaskData.strStateFilePath.setNull();
13860 mConsoleTaskData.mProgress.setNull();
13861
13862 LogFlowThisFuncLeave();
13863 return rc;
13864}
13865
13866/**
13867 * Deletes the given file if it is no longer in use by either the current machine state
13868 * (if the machine is "saved") or any of the machine's snapshots.
13869 *
13870 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13871 * but is different for each SnapshotMachine. When calling this, the order of calling this
13872 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13873 * is therefore critical. I know, it's all rather messy.
13874 *
13875 * @param strStateFile
13876 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
13877 */
13878void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
13879 Snapshot *pSnapshotToIgnore)
13880{
13881 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13882 if ( (strStateFile.isNotEmpty())
13883 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13884 )
13885 // ... and it must also not be shared with other snapshots
13886 if ( !mData->mFirstSnapshot
13887 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13888 // this checks the SnapshotMachine's state file paths
13889 )
13890 RTFileDelete(strStateFile.c_str());
13891}
13892
13893/**
13894 * Locks the attached media.
13895 *
13896 * All attached hard disks are locked for writing and DVD/floppy are locked for
13897 * reading. Parents of attached hard disks (if any) are locked for reading.
13898 *
13899 * This method also performs accessibility check of all media it locks: if some
13900 * media is inaccessible, the method will return a failure and a bunch of
13901 * extended error info objects per each inaccessible medium.
13902 *
13903 * Note that this method is atomic: if it returns a success, all media are
13904 * locked as described above; on failure no media is locked at all (all
13905 * succeeded individual locks will be undone).
13906 *
13907 * The caller is responsible for doing the necessary state sanity checks.
13908 *
13909 * The locks made by this method must be undone by calling #unlockMedia() when
13910 * no more needed.
13911 */
13912HRESULT SessionMachine::lockMedia()
13913{
13914 AutoCaller autoCaller(this);
13915 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13916
13917 AutoMultiWriteLock2 alock(this->lockHandle(),
13918 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13919
13920 /* bail out if trying to lock things with already set up locking */
13921 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13922
13923 MultiResult mrc(S_OK);
13924
13925 /* Collect locking information for all medium objects attached to the VM. */
13926 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13927 it != mMediaData->mAttachments.end();
13928 ++it)
13929 {
13930 MediumAttachment* pAtt = *it;
13931 DeviceType_T devType = pAtt->getType();
13932 Medium *pMedium = pAtt->getMedium();
13933
13934 MediumLockList *pMediumLockList(new MediumLockList());
13935 // There can be attachments without a medium (floppy/dvd), and thus
13936 // it's impossible to create a medium lock list. It still makes sense
13937 // to have the empty medium lock list in the map in case a medium is
13938 // attached later.
13939 if (pMedium != NULL)
13940 {
13941 MediumType_T mediumType = pMedium->getType();
13942 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13943 || mediumType == MediumType_Shareable;
13944 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13945
13946 alock.release();
13947 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13948 !fIsReadOnlyLock /* fMediumLockWrite */,
13949 NULL,
13950 *pMediumLockList);
13951 alock.acquire();
13952 if (FAILED(mrc))
13953 {
13954 delete pMediumLockList;
13955 mData->mSession.mLockedMedia.Clear();
13956 break;
13957 }
13958 }
13959
13960 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13961 if (FAILED(rc))
13962 {
13963 mData->mSession.mLockedMedia.Clear();
13964 mrc = setError(rc,
13965 tr("Collecting locking information for all attached media failed"));
13966 break;
13967 }
13968 }
13969
13970 if (SUCCEEDED(mrc))
13971 {
13972 /* Now lock all media. If this fails, nothing is locked. */
13973 alock.release();
13974 HRESULT rc = mData->mSession.mLockedMedia.Lock();
13975 alock.acquire();
13976 if (FAILED(rc))
13977 {
13978 mrc = setError(rc,
13979 tr("Locking of attached media failed"));
13980 }
13981 }
13982
13983 return mrc;
13984}
13985
13986/**
13987 * Undoes the locks made by by #lockMedia().
13988 */
13989void SessionMachine::unlockMedia()
13990{
13991 AutoCaller autoCaller(this);
13992 AssertComRCReturnVoid(autoCaller.rc());
13993
13994 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13995
13996 /* we may be holding important error info on the current thread;
13997 * preserve it */
13998 ErrorInfoKeeper eik;
13999
14000 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14001 AssertComRC(rc);
14002}
14003
14004/**
14005 * Helper to change the machine state (reimplementation).
14006 *
14007 * @note Locks this object for writing.
14008 * @note This method must not call saveSettings or SaveSettings, otherwise
14009 * it can cause crashes in random places due to unexpectedly committing
14010 * the current settings. The caller is responsible for that. The call
14011 * to saveStateSettings is fine, because this method does not commit.
14012 */
14013HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
14014{
14015 LogFlowThisFuncEnter();
14016 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14017
14018 AutoCaller autoCaller(this);
14019 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14020
14021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14022
14023 MachineState_T oldMachineState = mData->mMachineState;
14024
14025 AssertMsgReturn(oldMachineState != aMachineState,
14026 ("oldMachineState=%s, aMachineState=%s\n",
14027 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14028 E_FAIL);
14029
14030 HRESULT rc = S_OK;
14031
14032 int stsFlags = 0;
14033 bool deleteSavedState = false;
14034
14035 /* detect some state transitions */
14036
14037 if ( ( oldMachineState == MachineState_Saved
14038 && aMachineState == MachineState_Restoring)
14039 || ( ( oldMachineState == MachineState_PoweredOff
14040 || oldMachineState == MachineState_Teleported
14041 || oldMachineState == MachineState_Aborted
14042 )
14043 && ( aMachineState == MachineState_TeleportingIn
14044 || aMachineState == MachineState_Starting
14045 )
14046 )
14047 )
14048 {
14049 /* The EMT thread is about to start */
14050
14051 /* Nothing to do here for now... */
14052
14053 /// @todo NEWMEDIA don't let mDVDDrive and other children
14054 /// change anything when in the Starting/Restoring state
14055 }
14056 else if ( ( oldMachineState == MachineState_Running
14057 || oldMachineState == MachineState_Paused
14058 || oldMachineState == MachineState_Teleporting
14059 || oldMachineState == MachineState_LiveSnapshotting
14060 || oldMachineState == MachineState_Stuck
14061 || oldMachineState == MachineState_Starting
14062 || oldMachineState == MachineState_Stopping
14063 || oldMachineState == MachineState_Saving
14064 || oldMachineState == MachineState_Restoring
14065 || oldMachineState == MachineState_TeleportingPausedVM
14066 || oldMachineState == MachineState_TeleportingIn
14067 )
14068 && ( aMachineState == MachineState_PoweredOff
14069 || aMachineState == MachineState_Saved
14070 || aMachineState == MachineState_Teleported
14071 || aMachineState == MachineState_Aborted
14072 )
14073 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14074 * snapshot */
14075 && ( mConsoleTaskData.mSnapshot.isNull()
14076 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14077 )
14078 )
14079 {
14080 /* The EMT thread has just stopped, unlock attached media. Note that as
14081 * opposed to locking that is done from Console, we do unlocking here
14082 * because the VM process may have aborted before having a chance to
14083 * properly unlock all media it locked. */
14084
14085 unlockMedia();
14086 }
14087
14088 if (oldMachineState == MachineState_Restoring)
14089 {
14090 if (aMachineState != MachineState_Saved)
14091 {
14092 /*
14093 * delete the saved state file once the machine has finished
14094 * restoring from it (note that Console sets the state from
14095 * Restoring to Saved if the VM couldn't restore successfully,
14096 * to give the user an ability to fix an error and retry --
14097 * we keep the saved state file in this case)
14098 */
14099 deleteSavedState = true;
14100 }
14101 }
14102 else if ( oldMachineState == MachineState_Saved
14103 && ( aMachineState == MachineState_PoweredOff
14104 || aMachineState == MachineState_Aborted
14105 || aMachineState == MachineState_Teleported
14106 )
14107 )
14108 {
14109 /*
14110 * delete the saved state after Console::ForgetSavedState() is called
14111 * or if the VM process (owning a direct VM session) crashed while the
14112 * VM was Saved
14113 */
14114
14115 /// @todo (dmik)
14116 // Not sure that deleting the saved state file just because of the
14117 // client death before it attempted to restore the VM is a good
14118 // thing. But when it crashes we need to go to the Aborted state
14119 // which cannot have the saved state file associated... The only
14120 // way to fix this is to make the Aborted condition not a VM state
14121 // but a bool flag: i.e., when a crash occurs, set it to true and
14122 // change the state to PoweredOff or Saved depending on the
14123 // saved state presence.
14124
14125 deleteSavedState = true;
14126 mData->mCurrentStateModified = TRUE;
14127 stsFlags |= SaveSTS_CurStateModified;
14128 }
14129
14130 if ( aMachineState == MachineState_Starting
14131 || aMachineState == MachineState_Restoring
14132 || aMachineState == MachineState_TeleportingIn
14133 )
14134 {
14135 /* set the current state modified flag to indicate that the current
14136 * state is no more identical to the state in the
14137 * current snapshot */
14138 if (!mData->mCurrentSnapshot.isNull())
14139 {
14140 mData->mCurrentStateModified = TRUE;
14141 stsFlags |= SaveSTS_CurStateModified;
14142 }
14143 }
14144
14145 if (deleteSavedState)
14146 {
14147 if (mRemoveSavedState)
14148 {
14149 Assert(!mSSData->strStateFilePath.isEmpty());
14150
14151 // it is safe to delete the saved state file if ...
14152 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14153 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14154 // ... none of the snapshots share the saved state file
14155 )
14156 RTFileDelete(mSSData->strStateFilePath.c_str());
14157 }
14158
14159 mSSData->strStateFilePath.setNull();
14160 stsFlags |= SaveSTS_StateFilePath;
14161 }
14162
14163 /* redirect to the underlying peer machine */
14164 mPeer->setMachineState(aMachineState);
14165
14166 if ( aMachineState == MachineState_PoweredOff
14167 || aMachineState == MachineState_Teleported
14168 || aMachineState == MachineState_Aborted
14169 || aMachineState == MachineState_Saved)
14170 {
14171 /* the machine has stopped execution
14172 * (or the saved state file was adopted) */
14173 stsFlags |= SaveSTS_StateTimeStamp;
14174 }
14175
14176 if ( ( oldMachineState == MachineState_PoweredOff
14177 || oldMachineState == MachineState_Aborted
14178 || oldMachineState == MachineState_Teleported
14179 )
14180 && aMachineState == MachineState_Saved)
14181 {
14182 /* the saved state file was adopted */
14183 Assert(!mSSData->strStateFilePath.isEmpty());
14184 stsFlags |= SaveSTS_StateFilePath;
14185 }
14186
14187#ifdef VBOX_WITH_GUEST_PROPS
14188 if ( aMachineState == MachineState_PoweredOff
14189 || aMachineState == MachineState_Aborted
14190 || aMachineState == MachineState_Teleported)
14191 {
14192 /* Make sure any transient guest properties get removed from the
14193 * property store on shutdown. */
14194
14195 HWData::GuestPropertyMap::const_iterator it;
14196 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14197 if (!fNeedsSaving)
14198 for (it = mHWData->mGuestProperties.begin();
14199 it != mHWData->mGuestProperties.end(); ++it)
14200 if ( (it->second.mFlags & guestProp::TRANSIENT)
14201 || (it->second.mFlags & guestProp::TRANSRESET))
14202 {
14203 fNeedsSaving = true;
14204 break;
14205 }
14206 if (fNeedsSaving)
14207 {
14208 mData->mCurrentStateModified = TRUE;
14209 stsFlags |= SaveSTS_CurStateModified;
14210 }
14211 }
14212#endif
14213
14214 rc = saveStateSettings(stsFlags);
14215
14216 if ( ( oldMachineState != MachineState_PoweredOff
14217 && oldMachineState != MachineState_Aborted
14218 && oldMachineState != MachineState_Teleported
14219 )
14220 && ( aMachineState == MachineState_PoweredOff
14221 || aMachineState == MachineState_Aborted
14222 || aMachineState == MachineState_Teleported
14223 )
14224 )
14225 {
14226 /* we've been shut down for any reason */
14227 /* no special action so far */
14228 }
14229
14230 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14231 LogFlowThisFuncLeave();
14232 return rc;
14233}
14234
14235/**
14236 * Sends the current machine state value to the VM process.
14237 *
14238 * @note Locks this object for reading, then calls a client process.
14239 */
14240HRESULT SessionMachine::updateMachineStateOnClient()
14241{
14242 AutoCaller autoCaller(this);
14243 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14244
14245 ComPtr<IInternalSessionControl> directControl;
14246 {
14247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14248 AssertReturn(!!mData, E_FAIL);
14249 directControl = mData->mSession.mDirectControl;
14250
14251 /* directControl may be already set to NULL here in #OnSessionEnd()
14252 * called too early by the direct session process while there is still
14253 * some operation (like deleting the snapshot) in progress. The client
14254 * process in this case is waiting inside Session::close() for the
14255 * "end session" process object to complete, while #uninit() called by
14256 * #checkForDeath() on the Watcher thread is waiting for the pending
14257 * operation to complete. For now, we accept this inconsistent behavior
14258 * and simply do nothing here. */
14259
14260 if (mData->mSession.mState == SessionState_Unlocking)
14261 return S_OK;
14262
14263 AssertReturn(!directControl.isNull(), E_FAIL);
14264 }
14265
14266 return directControl->UpdateMachineState(mData->mMachineState);
14267}
Note: See TracBrowser for help on using the repository browser.

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