VirtualBox

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

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

forgotten file

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 478.1 KB
Line 
1/* $Id: MachineImpl.cpp 45661 2013-04-22 08:45:15Z 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 = 640;
171 mVideoCaptureHeight = 480;
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_VBoxVGA:
1812 break;
1813 default:
1814 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1815 }
1816
1817 AutoCaller autoCaller(this);
1818 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1819
1820 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1821
1822 HRESULT rc = checkStateDependency(MutableStateDep);
1823 if (FAILED(rc)) return rc;
1824
1825 setModified(IsModified_MachineData);
1826 mHWData.backup();
1827 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1828
1829 return S_OK;
1830}
1831
1832STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1833{
1834 CheckComArgOutPointerValid(memorySize);
1835
1836 AutoCaller autoCaller(this);
1837 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1838
1839 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1840
1841 *memorySize = mHWData->mVRAMSize;
1842
1843 return S_OK;
1844}
1845
1846STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1847{
1848 /* check VRAM limits */
1849 if (memorySize < SchemaDefs::MinGuestVRAM ||
1850 memorySize > SchemaDefs::MaxGuestVRAM)
1851 return setError(E_INVALIDARG,
1852 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1853 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1854
1855 AutoCaller autoCaller(this);
1856 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1857
1858 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1859
1860 HRESULT rc = checkStateDependency(MutableStateDep);
1861 if (FAILED(rc)) return rc;
1862
1863 setModified(IsModified_MachineData);
1864 mHWData.backup();
1865 mHWData->mVRAMSize = memorySize;
1866
1867 return S_OK;
1868}
1869
1870/** @todo this method should not be public */
1871STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1872{
1873 CheckComArgOutPointerValid(memoryBalloonSize);
1874
1875 AutoCaller autoCaller(this);
1876 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1877
1878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1879
1880 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
1881
1882 return S_OK;
1883}
1884
1885/**
1886 * Set the memory balloon size.
1887 *
1888 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1889 * we have to make sure that we never call IGuest from here.
1890 */
1891STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
1892{
1893 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1894#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1895 /* check limits */
1896 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1897 return setError(E_INVALIDARG,
1898 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1899 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1900
1901 AutoCaller autoCaller(this);
1902 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1903
1904 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1905
1906 setModified(IsModified_MachineData);
1907 mHWData.backup();
1908 mHWData->mMemoryBalloonSize = memoryBalloonSize;
1909
1910 return S_OK;
1911#else
1912 NOREF(memoryBalloonSize);
1913 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1914#endif
1915}
1916
1917STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *enabled)
1918{
1919 CheckComArgOutPointerValid(enabled);
1920
1921 AutoCaller autoCaller(this);
1922 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1923
1924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1925
1926 *enabled = mHWData->mPageFusionEnabled;
1927 return S_OK;
1928}
1929
1930STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL enabled)
1931{
1932#ifdef VBOX_WITH_PAGE_SHARING
1933 AutoCaller autoCaller(this);
1934 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1935
1936 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1937
1938 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1939 setModified(IsModified_MachineData);
1940 mHWData.backup();
1941 mHWData->mPageFusionEnabled = enabled;
1942 return S_OK;
1943#else
1944 NOREF(enabled);
1945 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1946#endif
1947}
1948
1949STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *enabled)
1950{
1951 CheckComArgOutPointerValid(enabled);
1952
1953 AutoCaller autoCaller(this);
1954 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1955
1956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1957
1958 *enabled = mHWData->mAccelerate3DEnabled;
1959
1960 return S_OK;
1961}
1962
1963STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
1964{
1965 AutoCaller autoCaller(this);
1966 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1967
1968 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1969
1970 HRESULT rc = checkStateDependency(MutableStateDep);
1971 if (FAILED(rc)) return rc;
1972
1973 /** @todo check validity! */
1974
1975 setModified(IsModified_MachineData);
1976 mHWData.backup();
1977 mHWData->mAccelerate3DEnabled = enable;
1978
1979 return S_OK;
1980}
1981
1982
1983STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *enabled)
1984{
1985 CheckComArgOutPointerValid(enabled);
1986
1987 AutoCaller autoCaller(this);
1988 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1989
1990 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1991
1992 *enabled = mHWData->mAccelerate2DVideoEnabled;
1993
1994 return S_OK;
1995}
1996
1997STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
1998{
1999 AutoCaller autoCaller(this);
2000 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2001
2002 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2003
2004 HRESULT rc = checkStateDependency(MutableStateDep);
2005 if (FAILED(rc)) return rc;
2006
2007 /** @todo check validity! */
2008
2009 setModified(IsModified_MachineData);
2010 mHWData.backup();
2011 mHWData->mAccelerate2DVideoEnabled = enable;
2012
2013 return S_OK;
2014}
2015
2016STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
2017{
2018 CheckComArgOutPointerValid(monitorCount);
2019
2020 AutoCaller autoCaller(this);
2021 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2022
2023 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2024
2025 *monitorCount = mHWData->mMonitorCount;
2026
2027 return S_OK;
2028}
2029
2030STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
2031{
2032 /* make sure monitor count is a sensible number */
2033 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
2034 return setError(E_INVALIDARG,
2035 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2036 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
2037
2038 AutoCaller autoCaller(this);
2039 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2040
2041 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2042
2043 HRESULT rc = checkStateDependency(MutableStateDep);
2044 if (FAILED(rc)) return rc;
2045
2046 setModified(IsModified_MachineData);
2047 mHWData.backup();
2048 mHWData->mMonitorCount = monitorCount;
2049
2050 return S_OK;
2051}
2052
2053STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2054{
2055 CheckComArgOutPointerValid(biosSettings);
2056
2057 AutoCaller autoCaller(this);
2058 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2059
2060 /* mBIOSSettings is constant during life time, no need to lock */
2061 mBIOSSettings.queryInterfaceTo(biosSettings);
2062
2063 return S_OK;
2064}
2065
2066STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2067{
2068 CheckComArgOutPointerValid(aVal);
2069
2070 AutoCaller autoCaller(this);
2071 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2072
2073 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2074
2075 switch (property)
2076 {
2077 case CPUPropertyType_PAE:
2078 *aVal = mHWData->mPAEEnabled;
2079 break;
2080
2081 case CPUPropertyType_Synthetic:
2082 *aVal = mHWData->mSyntheticCpu;
2083 break;
2084
2085 case CPUPropertyType_LongMode:
2086 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2087 *aVal = TRUE;
2088 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2089 *aVal = FALSE;
2090#if HC_ARCH_BITS == 64
2091 else
2092 *aVal = TRUE;
2093#else
2094 else
2095 {
2096 *aVal = FALSE;
2097
2098 ComPtr<IGuestOSType> ptrGuestOSType;
2099 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2100 if (SUCCEEDED(hrc2))
2101 {
2102 BOOL fIs64Bit = FALSE;
2103 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2104 if (SUCCEEDED(hrc2) && fIs64Bit)
2105 {
2106 ComObjPtr<Host> ptrHost = mParent->host();
2107 alock.release();
2108
2109 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal); AssertComRC(hrc2);
2110 if (FAILED(hrc2))
2111 *aVal = FALSE;
2112 }
2113 }
2114 }
2115#endif
2116 break;
2117
2118 default:
2119 return E_INVALIDARG;
2120 }
2121 return S_OK;
2122}
2123
2124STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2125{
2126 AutoCaller autoCaller(this);
2127 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2128
2129 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2130
2131 HRESULT rc = checkStateDependency(MutableStateDep);
2132 if (FAILED(rc)) return rc;
2133
2134 switch (property)
2135 {
2136 case CPUPropertyType_PAE:
2137 setModified(IsModified_MachineData);
2138 mHWData.backup();
2139 mHWData->mPAEEnabled = !!aVal;
2140 break;
2141
2142 case CPUPropertyType_Synthetic:
2143 setModified(IsModified_MachineData);
2144 mHWData.backup();
2145 mHWData->mSyntheticCpu = !!aVal;
2146 break;
2147
2148 case CPUPropertyType_LongMode:
2149 setModified(IsModified_MachineData);
2150 mHWData.backup();
2151 mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2152 break;
2153
2154 default:
2155 return E_INVALIDARG;
2156 }
2157 return S_OK;
2158}
2159
2160STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2161{
2162 CheckComArgOutPointerValid(aValEax);
2163 CheckComArgOutPointerValid(aValEbx);
2164 CheckComArgOutPointerValid(aValEcx);
2165 CheckComArgOutPointerValid(aValEdx);
2166
2167 AutoCaller autoCaller(this);
2168 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2169
2170 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2171
2172 switch(aId)
2173 {
2174 case 0x0:
2175 case 0x1:
2176 case 0x2:
2177 case 0x3:
2178 case 0x4:
2179 case 0x5:
2180 case 0x6:
2181 case 0x7:
2182 case 0x8:
2183 case 0x9:
2184 case 0xA:
2185 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2186 return E_INVALIDARG;
2187
2188 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2189 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2190 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2191 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2192 break;
2193
2194 case 0x80000000:
2195 case 0x80000001:
2196 case 0x80000002:
2197 case 0x80000003:
2198 case 0x80000004:
2199 case 0x80000005:
2200 case 0x80000006:
2201 case 0x80000007:
2202 case 0x80000008:
2203 case 0x80000009:
2204 case 0x8000000A:
2205 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2206 return E_INVALIDARG;
2207
2208 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2209 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2210 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2211 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2212 break;
2213
2214 default:
2215 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2216 }
2217 return S_OK;
2218}
2219
2220STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2221{
2222 AutoCaller autoCaller(this);
2223 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2224
2225 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2226
2227 HRESULT rc = checkStateDependency(MutableStateDep);
2228 if (FAILED(rc)) return rc;
2229
2230 switch(aId)
2231 {
2232 case 0x0:
2233 case 0x1:
2234 case 0x2:
2235 case 0x3:
2236 case 0x4:
2237 case 0x5:
2238 case 0x6:
2239 case 0x7:
2240 case 0x8:
2241 case 0x9:
2242 case 0xA:
2243 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2244 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2245 setModified(IsModified_MachineData);
2246 mHWData.backup();
2247 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2248 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2249 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2250 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2251 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2252 break;
2253
2254 case 0x80000000:
2255 case 0x80000001:
2256 case 0x80000002:
2257 case 0x80000003:
2258 case 0x80000004:
2259 case 0x80000005:
2260 case 0x80000006:
2261 case 0x80000007:
2262 case 0x80000008:
2263 case 0x80000009:
2264 case 0x8000000A:
2265 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2266 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2267 setModified(IsModified_MachineData);
2268 mHWData.backup();
2269 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2270 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2271 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2272 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2273 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2274 break;
2275
2276 default:
2277 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2278 }
2279 return S_OK;
2280}
2281
2282STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2283{
2284 AutoCaller autoCaller(this);
2285 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2286
2287 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2288
2289 HRESULT rc = checkStateDependency(MutableStateDep);
2290 if (FAILED(rc)) return rc;
2291
2292 switch(aId)
2293 {
2294 case 0x0:
2295 case 0x1:
2296 case 0x2:
2297 case 0x3:
2298 case 0x4:
2299 case 0x5:
2300 case 0x6:
2301 case 0x7:
2302 case 0x8:
2303 case 0x9:
2304 case 0xA:
2305 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2306 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2307 setModified(IsModified_MachineData);
2308 mHWData.backup();
2309 /* Invalidate leaf. */
2310 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2311 break;
2312
2313 case 0x80000000:
2314 case 0x80000001:
2315 case 0x80000002:
2316 case 0x80000003:
2317 case 0x80000004:
2318 case 0x80000005:
2319 case 0x80000006:
2320 case 0x80000007:
2321 case 0x80000008:
2322 case 0x80000009:
2323 case 0x8000000A:
2324 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2325 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2326 setModified(IsModified_MachineData);
2327 mHWData.backup();
2328 /* Invalidate leaf. */
2329 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2330 break;
2331
2332 default:
2333 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2334 }
2335 return S_OK;
2336}
2337
2338STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2339{
2340 AutoCaller autoCaller(this);
2341 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2342
2343 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2344
2345 HRESULT rc = checkStateDependency(MutableStateDep);
2346 if (FAILED(rc)) return rc;
2347
2348 setModified(IsModified_MachineData);
2349 mHWData.backup();
2350
2351 /* Invalidate all standard leafs. */
2352 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2353 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2354
2355 /* Invalidate all extended leafs. */
2356 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2357 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2358
2359 return S_OK;
2360}
2361
2362STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2363{
2364 CheckComArgOutPointerValid(aVal);
2365
2366 AutoCaller autoCaller(this);
2367 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2368
2369 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2370
2371 switch(property)
2372 {
2373 case HWVirtExPropertyType_Enabled:
2374 *aVal = mHWData->mHWVirtExEnabled;
2375 break;
2376
2377 case HWVirtExPropertyType_Exclusive:
2378 *aVal = mHWData->mHWVirtExExclusive;
2379 break;
2380
2381 case HWVirtExPropertyType_VPID:
2382 *aVal = mHWData->mHWVirtExVPIDEnabled;
2383 break;
2384
2385 case HWVirtExPropertyType_NestedPaging:
2386 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2387 break;
2388
2389 case HWVirtExPropertyType_LargePages:
2390 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2391#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2392 *aVal = FALSE;
2393#endif
2394 break;
2395
2396 case HWVirtExPropertyType_Force:
2397 *aVal = mHWData->mHWVirtExForceEnabled;
2398 break;
2399
2400 default:
2401 return E_INVALIDARG;
2402 }
2403 return S_OK;
2404}
2405
2406STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2407{
2408 AutoCaller autoCaller(this);
2409 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2410
2411 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2412
2413 HRESULT rc = checkStateDependency(MutableStateDep);
2414 if (FAILED(rc)) return rc;
2415
2416 switch(property)
2417 {
2418 case HWVirtExPropertyType_Enabled:
2419 setModified(IsModified_MachineData);
2420 mHWData.backup();
2421 mHWData->mHWVirtExEnabled = !!aVal;
2422 break;
2423
2424 case HWVirtExPropertyType_Exclusive:
2425 setModified(IsModified_MachineData);
2426 mHWData.backup();
2427 mHWData->mHWVirtExExclusive = !!aVal;
2428 break;
2429
2430 case HWVirtExPropertyType_VPID:
2431 setModified(IsModified_MachineData);
2432 mHWData.backup();
2433 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2434 break;
2435
2436 case HWVirtExPropertyType_NestedPaging:
2437 setModified(IsModified_MachineData);
2438 mHWData.backup();
2439 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2440 break;
2441
2442 case HWVirtExPropertyType_LargePages:
2443 setModified(IsModified_MachineData);
2444 mHWData.backup();
2445 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2446 break;
2447
2448 case HWVirtExPropertyType_Force:
2449 setModified(IsModified_MachineData);
2450 mHWData.backup();
2451 mHWData->mHWVirtExForceEnabled = !!aVal;
2452 break;
2453
2454 default:
2455 return E_INVALIDARG;
2456 }
2457
2458 return S_OK;
2459}
2460
2461STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2462{
2463 CheckComArgOutPointerValid(aSnapshotFolder);
2464
2465 AutoCaller autoCaller(this);
2466 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2467
2468 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2469
2470 Utf8Str strFullSnapshotFolder;
2471 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2472 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2473
2474 return S_OK;
2475}
2476
2477STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2478{
2479 /* @todo (r=dmik):
2480 * 1. Allow to change the name of the snapshot folder containing snapshots
2481 * 2. Rename the folder on disk instead of just changing the property
2482 * value (to be smart and not to leave garbage). Note that it cannot be
2483 * done here because the change may be rolled back. Thus, the right
2484 * place is #saveSettings().
2485 */
2486
2487 AutoCaller autoCaller(this);
2488 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2489
2490 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2491
2492 HRESULT rc = checkStateDependency(MutableStateDep);
2493 if (FAILED(rc)) return rc;
2494
2495 if (!mData->mCurrentSnapshot.isNull())
2496 return setError(E_FAIL,
2497 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2498
2499 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2500
2501 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2502 if (strSnapshotFolder.isEmpty())
2503 strSnapshotFolder = "Snapshots";
2504 int vrc = calculateFullPath(strSnapshotFolder,
2505 strSnapshotFolder);
2506 if (RT_FAILURE(vrc))
2507 return setError(E_FAIL,
2508 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2509 aSnapshotFolder, vrc);
2510
2511 setModified(IsModified_MachineData);
2512 mUserData.backup();
2513
2514 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2515
2516 return S_OK;
2517}
2518
2519STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2520{
2521 CheckComArgOutSafeArrayPointerValid(aAttachments);
2522
2523 AutoCaller autoCaller(this);
2524 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2525
2526 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2527
2528 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2529 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2530
2531 return S_OK;
2532}
2533
2534STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2535{
2536 CheckComArgOutPointerValid(vrdeServer);
2537
2538 AutoCaller autoCaller(this);
2539 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2540
2541 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2542
2543 Assert(!!mVRDEServer);
2544 mVRDEServer.queryInterfaceTo(vrdeServer);
2545
2546 return S_OK;
2547}
2548
2549STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2550{
2551 CheckComArgOutPointerValid(audioAdapter);
2552
2553 AutoCaller autoCaller(this);
2554 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2555
2556 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2557
2558 mAudioAdapter.queryInterfaceTo(audioAdapter);
2559 return S_OK;
2560}
2561
2562STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2563{
2564#ifdef VBOX_WITH_VUSB
2565 CheckComArgOutPointerValid(aUSBController);
2566
2567 AutoCaller autoCaller(this);
2568 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2569
2570 clearError();
2571 MultiResult rc(S_OK);
2572
2573# ifdef VBOX_WITH_USB
2574 rc = mParent->host()->checkUSBProxyService();
2575 if (FAILED(rc)) return rc;
2576# endif
2577
2578 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2579
2580 return rc = mUSBController.queryInterfaceTo(aUSBController);
2581#else
2582 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2583 * extended error info to indicate that USB is simply not available
2584 * (w/o treating it as a failure), for example, as in OSE */
2585 NOREF(aUSBController);
2586 ReturnComNotImplemented();
2587#endif /* VBOX_WITH_VUSB */
2588}
2589
2590STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2591{
2592 CheckComArgOutPointerValid(aFilePath);
2593
2594 AutoLimitedCaller autoCaller(this);
2595 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2596
2597 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2598
2599 mData->m_strConfigFileFull.cloneTo(aFilePath);
2600 return S_OK;
2601}
2602
2603STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2604{
2605 CheckComArgOutPointerValid(aModified);
2606
2607 AutoCaller autoCaller(this);
2608 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2609
2610 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2611
2612 HRESULT rc = checkStateDependency(MutableStateDep);
2613 if (FAILED(rc)) return rc;
2614
2615 if (!mData->pMachineConfigFile->fileExists())
2616 // this is a new machine, and no config file exists yet:
2617 *aModified = TRUE;
2618 else
2619 *aModified = (mData->flModifications != 0);
2620
2621 return S_OK;
2622}
2623
2624STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2625{
2626 CheckComArgOutPointerValid(aSessionState);
2627
2628 AutoCaller autoCaller(this);
2629 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2630
2631 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2632
2633 *aSessionState = mData->mSession.mState;
2634
2635 return S_OK;
2636}
2637
2638STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2639{
2640 CheckComArgOutPointerValid(aSessionType);
2641
2642 AutoCaller autoCaller(this);
2643 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2644
2645 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2646
2647 mData->mSession.mType.cloneTo(aSessionType);
2648
2649 return S_OK;
2650}
2651
2652STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2653{
2654 CheckComArgOutPointerValid(aSessionPID);
2655
2656 AutoCaller autoCaller(this);
2657 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2658
2659 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2660
2661 *aSessionPID = mData->mSession.mPID;
2662
2663 return S_OK;
2664}
2665
2666STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2667{
2668 CheckComArgOutPointerValid(machineState);
2669
2670 AutoCaller autoCaller(this);
2671 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2672
2673 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2674
2675 *machineState = mData->mMachineState;
2676
2677 return S_OK;
2678}
2679
2680STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2681{
2682 CheckComArgOutPointerValid(aLastStateChange);
2683
2684 AutoCaller autoCaller(this);
2685 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2686
2687 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2688
2689 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2690
2691 return S_OK;
2692}
2693
2694STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2695{
2696 CheckComArgOutPointerValid(aStateFilePath);
2697
2698 AutoCaller autoCaller(this);
2699 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2700
2701 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2702
2703 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2704
2705 return S_OK;
2706}
2707
2708STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2709{
2710 CheckComArgOutPointerValid(aLogFolder);
2711
2712 AutoCaller autoCaller(this);
2713 AssertComRCReturnRC(autoCaller.rc());
2714
2715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2716
2717 Utf8Str logFolder;
2718 getLogFolder(logFolder);
2719 logFolder.cloneTo(aLogFolder);
2720
2721 return S_OK;
2722}
2723
2724STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2725{
2726 CheckComArgOutPointerValid(aCurrentSnapshot);
2727
2728 AutoCaller autoCaller(this);
2729 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2730
2731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2732
2733 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2734
2735 return S_OK;
2736}
2737
2738STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2739{
2740 CheckComArgOutPointerValid(aSnapshotCount);
2741
2742 AutoCaller autoCaller(this);
2743 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2744
2745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2746
2747 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2748 ? 0
2749 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2750
2751 return S_OK;
2752}
2753
2754STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2755{
2756 CheckComArgOutPointerValid(aCurrentStateModified);
2757
2758 AutoCaller autoCaller(this);
2759 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2760
2761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2762
2763 /* Note: for machines with no snapshots, we always return FALSE
2764 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2765 * reasons :) */
2766
2767 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2768 ? FALSE
2769 : mData->mCurrentStateModified;
2770
2771 return S_OK;
2772}
2773
2774STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2775{
2776 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2777
2778 AutoCaller autoCaller(this);
2779 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2780
2781 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2782
2783 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2784 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2785
2786 return S_OK;
2787}
2788
2789STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2790{
2791 CheckComArgOutPointerValid(aClipboardMode);
2792
2793 AutoCaller autoCaller(this);
2794 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2795
2796 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2797
2798 *aClipboardMode = mHWData->mClipboardMode;
2799
2800 return S_OK;
2801}
2802
2803STDMETHODIMP
2804Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2805{
2806 HRESULT rc = S_OK;
2807
2808 AutoCaller autoCaller(this);
2809 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2810
2811 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2812
2813 alock.release();
2814 rc = onClipboardModeChange(aClipboardMode);
2815 alock.acquire();
2816 if (FAILED(rc)) return rc;
2817
2818 setModified(IsModified_MachineData);
2819 mHWData.backup();
2820 mHWData->mClipboardMode = aClipboardMode;
2821
2822 /* Save settings if online - todo why is this required?? */
2823 if (Global::IsOnline(mData->mMachineState))
2824 saveSettings(NULL);
2825
2826 return S_OK;
2827}
2828
2829STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
2830{
2831 CheckComArgOutPointerValid(aDragAndDropMode);
2832
2833 AutoCaller autoCaller(this);
2834 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2835
2836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2837
2838 *aDragAndDropMode = mHWData->mDragAndDropMode;
2839
2840 return S_OK;
2841}
2842
2843STDMETHODIMP
2844Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
2845{
2846 HRESULT rc = S_OK;
2847
2848 AutoCaller autoCaller(this);
2849 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2850
2851 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2852
2853 alock.release();
2854 rc = onDragAndDropModeChange(aDragAndDropMode);
2855 alock.acquire();
2856 if (FAILED(rc)) return rc;
2857
2858 setModified(IsModified_MachineData);
2859 mHWData.backup();
2860 mHWData->mDragAndDropMode = aDragAndDropMode;
2861
2862 /* Save settings if online - todo why is this required?? */
2863 if (Global::IsOnline(mData->mMachineState))
2864 saveSettings(NULL);
2865
2866 return S_OK;
2867}
2868
2869STDMETHODIMP
2870Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
2871{
2872 CheckComArgOutPointerValid(aPatterns);
2873
2874 AutoCaller autoCaller(this);
2875 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2876
2877 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2878
2879 try
2880 {
2881 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
2882 }
2883 catch (...)
2884 {
2885 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2886 }
2887
2888 return S_OK;
2889}
2890
2891STDMETHODIMP
2892Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
2893{
2894 AutoCaller autoCaller(this);
2895 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2896
2897 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2898
2899 HRESULT rc = checkStateDependency(MutableStateDep);
2900 if (FAILED(rc)) return rc;
2901
2902 setModified(IsModified_MachineData);
2903 mHWData.backup();
2904 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
2905 return rc;
2906}
2907
2908STDMETHODIMP
2909Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
2910{
2911 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
2912
2913 AutoCaller autoCaller(this);
2914 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2915
2916 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2917
2918 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
2919 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
2920
2921 return S_OK;
2922}
2923
2924STDMETHODIMP
2925Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
2926{
2927 CheckComArgOutPointerValid(aEnabled);
2928
2929 AutoCaller autoCaller(this);
2930 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2931
2932 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2933
2934 *aEnabled = mUserData->s.fTeleporterEnabled;
2935
2936 return S_OK;
2937}
2938
2939STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
2940{
2941 AutoCaller autoCaller(this);
2942 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2943
2944 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2945
2946 /* Only allow it to be set to true when PoweredOff or Aborted.
2947 (Clearing it is always permitted.) */
2948 if ( aEnabled
2949 && mData->mRegistered
2950 && ( !isSessionMachine()
2951 || ( mData->mMachineState != MachineState_PoweredOff
2952 && mData->mMachineState != MachineState_Teleported
2953 && mData->mMachineState != MachineState_Aborted
2954 )
2955 )
2956 )
2957 return setError(VBOX_E_INVALID_VM_STATE,
2958 tr("The machine is not powered off (state is %s)"),
2959 Global::stringifyMachineState(mData->mMachineState));
2960
2961 setModified(IsModified_MachineData);
2962 mUserData.backup();
2963 mUserData->s.fTeleporterEnabled = !!aEnabled;
2964
2965 return S_OK;
2966}
2967
2968STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
2969{
2970 CheckComArgOutPointerValid(aPort);
2971
2972 AutoCaller autoCaller(this);
2973 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2974
2975 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2976
2977 *aPort = (ULONG)mUserData->s.uTeleporterPort;
2978
2979 return S_OK;
2980}
2981
2982STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
2983{
2984 if (aPort >= _64K)
2985 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
2986
2987 AutoCaller autoCaller(this);
2988 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2989
2990 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2991
2992 HRESULT rc = checkStateDependency(MutableStateDep);
2993 if (FAILED(rc)) return rc;
2994
2995 setModified(IsModified_MachineData);
2996 mUserData.backup();
2997 mUserData->s.uTeleporterPort = (uint32_t)aPort;
2998
2999 return S_OK;
3000}
3001
3002STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
3003{
3004 CheckComArgOutPointerValid(aAddress);
3005
3006 AutoCaller autoCaller(this);
3007 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3008
3009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3010
3011 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
3012
3013 return S_OK;
3014}
3015
3016STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
3017{
3018 AutoCaller autoCaller(this);
3019 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3020
3021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3022
3023 HRESULT rc = checkStateDependency(MutableStateDep);
3024 if (FAILED(rc)) return rc;
3025
3026 setModified(IsModified_MachineData);
3027 mUserData.backup();
3028 mUserData->s.strTeleporterAddress = aAddress;
3029
3030 return S_OK;
3031}
3032
3033STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
3034{
3035 CheckComArgOutPointerValid(aPassword);
3036
3037 AutoCaller autoCaller(this);
3038 HRESULT hrc = autoCaller.rc();
3039 if (SUCCEEDED(hrc))
3040 {
3041 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3042 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
3043 }
3044
3045 return hrc;
3046}
3047
3048STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
3049{
3050 /*
3051 * Hash the password first.
3052 */
3053 Utf8Str strPassword(aPassword);
3054 if (!strPassword.isEmpty())
3055 {
3056 if (VBoxIsPasswordHashed(&strPassword))
3057 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3058 VBoxHashPassword(&strPassword);
3059 }
3060
3061 /*
3062 * Do the update.
3063 */
3064 AutoCaller autoCaller(this);
3065 HRESULT hrc = autoCaller.rc();
3066 if (SUCCEEDED(hrc))
3067 {
3068 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3069 hrc = checkStateDependency(MutableStateDep);
3070 if (SUCCEEDED(hrc))
3071 {
3072 setModified(IsModified_MachineData);
3073 mUserData.backup();
3074 mUserData->s.strTeleporterPassword = strPassword;
3075 }
3076 }
3077
3078 return hrc;
3079}
3080
3081STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
3082{
3083 CheckComArgOutPointerValid(aState);
3084
3085 AutoCaller autoCaller(this);
3086 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3087
3088 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3089
3090 *aState = mUserData->s.enmFaultToleranceState;
3091 return S_OK;
3092}
3093
3094STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
3095{
3096 AutoCaller autoCaller(this);
3097 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3098
3099 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3100
3101 /* @todo deal with running state change. */
3102 HRESULT rc = checkStateDependency(MutableStateDep);
3103 if (FAILED(rc)) return rc;
3104
3105 setModified(IsModified_MachineData);
3106 mUserData.backup();
3107 mUserData->s.enmFaultToleranceState = aState;
3108 return S_OK;
3109}
3110
3111STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3112{
3113 CheckComArgOutPointerValid(aAddress);
3114
3115 AutoCaller autoCaller(this);
3116 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3117
3118 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3119
3120 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3121 return S_OK;
3122}
3123
3124STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3125{
3126 AutoCaller autoCaller(this);
3127 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3128
3129 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3130
3131 /* @todo deal with running state change. */
3132 HRESULT rc = checkStateDependency(MutableStateDep);
3133 if (FAILED(rc)) return rc;
3134
3135 setModified(IsModified_MachineData);
3136 mUserData.backup();
3137 mUserData->s.strFaultToleranceAddress = aAddress;
3138 return S_OK;
3139}
3140
3141STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3142{
3143 CheckComArgOutPointerValid(aPort);
3144
3145 AutoCaller autoCaller(this);
3146 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3147
3148 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3149
3150 *aPort = mUserData->s.uFaultTolerancePort;
3151 return S_OK;
3152}
3153
3154STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3155{
3156 AutoCaller autoCaller(this);
3157 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3158
3159 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3160
3161 /* @todo deal with running state change. */
3162 HRESULT rc = checkStateDependency(MutableStateDep);
3163 if (FAILED(rc)) return rc;
3164
3165 setModified(IsModified_MachineData);
3166 mUserData.backup();
3167 mUserData->s.uFaultTolerancePort = aPort;
3168 return S_OK;
3169}
3170
3171STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3172{
3173 CheckComArgOutPointerValid(aPassword);
3174
3175 AutoCaller autoCaller(this);
3176 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3177
3178 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3179
3180 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3181
3182 return S_OK;
3183}
3184
3185STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3186{
3187 AutoCaller autoCaller(this);
3188 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3189
3190 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3191
3192 /* @todo deal with running state change. */
3193 HRESULT rc = checkStateDependency(MutableStateDep);
3194 if (FAILED(rc)) return rc;
3195
3196 setModified(IsModified_MachineData);
3197 mUserData.backup();
3198 mUserData->s.strFaultTolerancePassword = aPassword;
3199
3200 return S_OK;
3201}
3202
3203STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3204{
3205 CheckComArgOutPointerValid(aInterval);
3206
3207 AutoCaller autoCaller(this);
3208 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3209
3210 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3211
3212 *aInterval = mUserData->s.uFaultToleranceInterval;
3213 return S_OK;
3214}
3215
3216STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3217{
3218 AutoCaller autoCaller(this);
3219 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3220
3221 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3222
3223 /* @todo deal with running state change. */
3224 HRESULT rc = checkStateDependency(MutableStateDep);
3225 if (FAILED(rc)) return rc;
3226
3227 setModified(IsModified_MachineData);
3228 mUserData.backup();
3229 mUserData->s.uFaultToleranceInterval = aInterval;
3230 return S_OK;
3231}
3232
3233STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3234{
3235 CheckComArgOutPointerValid(aEnabled);
3236
3237 AutoCaller autoCaller(this);
3238 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3239
3240 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3241
3242 *aEnabled = mUserData->s.fRTCUseUTC;
3243
3244 return S_OK;
3245}
3246
3247STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3248{
3249 AutoCaller autoCaller(this);
3250 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3251
3252 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3253
3254 /* Only allow it to be set to true when PoweredOff or Aborted.
3255 (Clearing it is always permitted.) */
3256 if ( aEnabled
3257 && mData->mRegistered
3258 && ( !isSessionMachine()
3259 || ( mData->mMachineState != MachineState_PoweredOff
3260 && mData->mMachineState != MachineState_Teleported
3261 && mData->mMachineState != MachineState_Aborted
3262 )
3263 )
3264 )
3265 return setError(VBOX_E_INVALID_VM_STATE,
3266 tr("The machine is not powered off (state is %s)"),
3267 Global::stringifyMachineState(mData->mMachineState));
3268
3269 setModified(IsModified_MachineData);
3270 mUserData.backup();
3271 mUserData->s.fRTCUseUTC = !!aEnabled;
3272
3273 return S_OK;
3274}
3275
3276STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3277{
3278 CheckComArgOutPointerValid(aEnabled);
3279
3280 AutoCaller autoCaller(this);
3281 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3282
3283 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3284
3285 *aEnabled = mHWData->mIOCacheEnabled;
3286
3287 return S_OK;
3288}
3289
3290STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3291{
3292 AutoCaller autoCaller(this);
3293 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3294
3295 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3296
3297 HRESULT rc = checkStateDependency(MutableStateDep);
3298 if (FAILED(rc)) return rc;
3299
3300 setModified(IsModified_MachineData);
3301 mHWData.backup();
3302 mHWData->mIOCacheEnabled = aEnabled;
3303
3304 return S_OK;
3305}
3306
3307STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3308{
3309 CheckComArgOutPointerValid(aIOCacheSize);
3310
3311 AutoCaller autoCaller(this);
3312 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3313
3314 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3315
3316 *aIOCacheSize = mHWData->mIOCacheSize;
3317
3318 return S_OK;
3319}
3320
3321STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3322{
3323 AutoCaller autoCaller(this);
3324 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3325
3326 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3327
3328 HRESULT rc = checkStateDependency(MutableStateDep);
3329 if (FAILED(rc)) return rc;
3330
3331 setModified(IsModified_MachineData);
3332 mHWData.backup();
3333 mHWData->mIOCacheSize = aIOCacheSize;
3334
3335 return S_OK;
3336}
3337
3338
3339/**
3340 * @note Locks objects!
3341 */
3342STDMETHODIMP Machine::LockMachine(ISession *aSession,
3343 LockType_T lockType)
3344{
3345 CheckComArgNotNull(aSession);
3346
3347 AutoCaller autoCaller(this);
3348 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3349
3350 /* check the session state */
3351 SessionState_T state;
3352 HRESULT rc = aSession->COMGETTER(State)(&state);
3353 if (FAILED(rc)) return rc;
3354
3355 if (state != SessionState_Unlocked)
3356 return setError(VBOX_E_INVALID_OBJECT_STATE,
3357 tr("The given session is busy"));
3358
3359 // get the client's IInternalSessionControl interface
3360 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3361 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3362 E_INVALIDARG);
3363
3364 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3365
3366 if (!mData->mRegistered)
3367 return setError(E_UNEXPECTED,
3368 tr("The machine '%s' is not registered"),
3369 mUserData->s.strName.c_str());
3370
3371 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3372
3373 SessionState_T oldState = mData->mSession.mState;
3374 /* Hack: in case the session is closing and there is a progress object
3375 * which allows waiting for the session to be closed, take the opportunity
3376 * and do a limited wait (max. 1 second). This helps a lot when the system
3377 * is busy and thus session closing can take a little while. */
3378 if ( mData->mSession.mState == SessionState_Unlocking
3379 && mData->mSession.mProgress)
3380 {
3381 alock.release();
3382 mData->mSession.mProgress->WaitForCompletion(1000);
3383 alock.acquire();
3384 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3385 }
3386
3387 // try again now
3388 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3389 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3390 )
3391 {
3392 // OK, share the session... we are now dealing with three processes:
3393 // 1) VBoxSVC (where this code runs);
3394 // 2) process C: the caller's client process (who wants a shared session);
3395 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3396
3397 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3398 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3399 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3400 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3401 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3402
3403 /*
3404 * Release the lock before calling the client process. It's safe here
3405 * since the only thing to do after we get the lock again is to add
3406 * the remote control to the list (which doesn't directly influence
3407 * anything).
3408 */
3409 alock.release();
3410
3411 // get the console of the session holding the write lock (this is a remote call)
3412 ComPtr<IConsole> pConsoleW;
3413 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3414 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3415 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3416 if (FAILED(rc))
3417 // the failure may occur w/o any error info (from RPC), so provide one
3418 return setError(VBOX_E_VM_ERROR,
3419 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3420
3421 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3422
3423 // share the session machine and W's console with the caller's session
3424 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3425 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3426 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3427
3428 if (FAILED(rc))
3429 // the failure may occur w/o any error info (from RPC), so provide one
3430 return setError(VBOX_E_VM_ERROR,
3431 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3432 alock.acquire();
3433
3434 // need to revalidate the state after acquiring the lock again
3435 if (mData->mSession.mState != SessionState_Locked)
3436 {
3437 pSessionControl->Uninitialize();
3438 return setError(VBOX_E_INVALID_SESSION_STATE,
3439 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3440 mUserData->s.strName.c_str());
3441 }
3442
3443 // add the caller's session to the list
3444 mData->mSession.mRemoteControls.push_back(pSessionControl);
3445 }
3446 else if ( mData->mSession.mState == SessionState_Locked
3447 || mData->mSession.mState == SessionState_Unlocking
3448 )
3449 {
3450 // sharing not permitted, or machine still unlocking:
3451 return setError(VBOX_E_INVALID_OBJECT_STATE,
3452 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3453 mUserData->s.strName.c_str());
3454 }
3455 else
3456 {
3457 // machine is not locked: then write-lock the machine (create the session machine)
3458
3459 // must not be busy
3460 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3461
3462 // get the caller's session PID
3463 RTPROCESS pid = NIL_RTPROCESS;
3464 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3465 pSessionControl->GetPID((ULONG*)&pid);
3466 Assert(pid != NIL_RTPROCESS);
3467
3468 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3469
3470 if (fLaunchingVMProcess)
3471 {
3472 // this machine is awaiting for a spawning session to be opened:
3473 // then the calling process must be the one that got started by
3474 // LaunchVMProcess()
3475
3476 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3477 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3478
3479 if (mData->mSession.mPID != pid)
3480 return setError(E_ACCESSDENIED,
3481 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3482 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3483 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3484 }
3485
3486 // create the mutable SessionMachine from the current machine
3487 ComObjPtr<SessionMachine> sessionMachine;
3488 sessionMachine.createObject();
3489 rc = sessionMachine->init(this);
3490 AssertComRC(rc);
3491
3492 /* NOTE: doing return from this function after this point but
3493 * before the end is forbidden since it may call SessionMachine::uninit()
3494 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3495 * lock while still holding the Machine lock in alock so that a deadlock
3496 * is possible due to the wrong lock order. */
3497
3498 if (SUCCEEDED(rc))
3499 {
3500 /*
3501 * Set the session state to Spawning to protect against subsequent
3502 * attempts to open a session and to unregister the machine after
3503 * we release the lock.
3504 */
3505 SessionState_T origState = mData->mSession.mState;
3506 mData->mSession.mState = SessionState_Spawning;
3507
3508 /*
3509 * Release the lock before calling the client process -- it will call
3510 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3511 * because the state is Spawning, so that LaunchVMProcess() and
3512 * LockMachine() calls will fail. This method, called before we
3513 * acquire the lock again, will fail because of the wrong PID.
3514 *
3515 * Note that mData->mSession.mRemoteControls accessed outside
3516 * the lock may not be modified when state is Spawning, so it's safe.
3517 */
3518 alock.release();
3519
3520 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3521 rc = pSessionControl->AssignMachine(sessionMachine, lockType);
3522 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3523
3524 /* The failure may occur w/o any error info (from RPC), so provide one */
3525 if (FAILED(rc))
3526 setError(VBOX_E_VM_ERROR,
3527 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3528
3529 if ( SUCCEEDED(rc)
3530 && fLaunchingVMProcess
3531 )
3532 {
3533 /* complete the remote session initialization */
3534
3535 /* get the console from the direct session */
3536 ComPtr<IConsole> console;
3537 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3538 ComAssertComRC(rc);
3539
3540 if (SUCCEEDED(rc) && !console)
3541 {
3542 ComAssert(!!console);
3543 rc = E_FAIL;
3544 }
3545
3546 /* assign machine & console to the remote session */
3547 if (SUCCEEDED(rc))
3548 {
3549 /*
3550 * after LaunchVMProcess(), the first and the only
3551 * entry in remoteControls is that remote session
3552 */
3553 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3554 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3555 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3556
3557 /* The failure may occur w/o any error info (from RPC), so provide one */
3558 if (FAILED(rc))
3559 setError(VBOX_E_VM_ERROR,
3560 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3561 }
3562
3563 if (FAILED(rc))
3564 pSessionControl->Uninitialize();
3565 }
3566
3567 /* acquire the lock again */
3568 alock.acquire();
3569
3570 /* Restore the session state */
3571 mData->mSession.mState = origState;
3572 }
3573
3574 // finalize spawning anyway (this is why we don't return on errors above)
3575 if (fLaunchingVMProcess)
3576 {
3577 /* Note that the progress object is finalized later */
3578 /** @todo Consider checking mData->mSession.mProgress for cancellation
3579 * around here. */
3580
3581 /* We don't reset mSession.mPID here because it is necessary for
3582 * SessionMachine::uninit() to reap the child process later. */
3583
3584 if (FAILED(rc))
3585 {
3586 /* Close the remote session, remove the remote control from the list
3587 * and reset session state to Closed (@note keep the code in sync
3588 * with the relevant part in openSession()). */
3589
3590 Assert(mData->mSession.mRemoteControls.size() == 1);
3591 if (mData->mSession.mRemoteControls.size() == 1)
3592 {
3593 ErrorInfoKeeper eik;
3594 mData->mSession.mRemoteControls.front()->Uninitialize();
3595 }
3596
3597 mData->mSession.mRemoteControls.clear();
3598 mData->mSession.mState = SessionState_Unlocked;
3599 }
3600 }
3601 else
3602 {
3603 /* memorize PID of the directly opened session */
3604 if (SUCCEEDED(rc))
3605 mData->mSession.mPID = pid;
3606 }
3607
3608 if (SUCCEEDED(rc))
3609 {
3610 /* memorize the direct session control and cache IUnknown for it */
3611 mData->mSession.mDirectControl = pSessionControl;
3612 mData->mSession.mState = SessionState_Locked;
3613 /* associate the SessionMachine with this Machine */
3614 mData->mSession.mMachine = sessionMachine;
3615
3616 /* request an IUnknown pointer early from the remote party for later
3617 * identity checks (it will be internally cached within mDirectControl
3618 * at least on XPCOM) */
3619 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3620 NOREF(unk);
3621 }
3622
3623 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3624 * would break the lock order */
3625 alock.release();
3626
3627 /* uninitialize the created session machine on failure */
3628 if (FAILED(rc))
3629 sessionMachine->uninit();
3630
3631 }
3632
3633 if (SUCCEEDED(rc))
3634 {
3635 /*
3636 * tell the client watcher thread to update the set of
3637 * machines that have open sessions
3638 */
3639 mParent->updateClientWatcher();
3640
3641 if (oldState != SessionState_Locked)
3642 /* fire an event */
3643 mParent->onSessionStateChange(getId(), SessionState_Locked);
3644 }
3645
3646 return rc;
3647}
3648
3649/**
3650 * @note Locks objects!
3651 */
3652STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3653 IN_BSTR aFrontend,
3654 IN_BSTR aEnvironment,
3655 IProgress **aProgress)
3656{
3657 CheckComArgStr(aFrontend);
3658 Utf8Str strFrontend(aFrontend);
3659 Utf8Str strEnvironment(aEnvironment);
3660 /* "emergencystop" doesn't need the session, so skip the checks/interface
3661 * retrieval. This code doesn't quite fit in here, but introducing a
3662 * special API method would be even more effort, and would require explicit
3663 * support by every API client. It's better to hide the feature a bit. */
3664 if (strFrontend != "emergencystop")
3665 CheckComArgNotNull(aSession);
3666 CheckComArgOutPointerValid(aProgress);
3667
3668 AutoCaller autoCaller(this);
3669 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3670
3671 HRESULT rc = S_OK;
3672 if (strFrontend.isEmpty())
3673 {
3674 Bstr bstrFrontend;
3675 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3676 if (FAILED(rc))
3677 return rc;
3678 strFrontend = bstrFrontend;
3679 if (strFrontend.isEmpty())
3680 {
3681 ComPtr<ISystemProperties> systemProperties;
3682 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3683 if (FAILED(rc))
3684 return rc;
3685 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3686 if (FAILED(rc))
3687 return rc;
3688 strFrontend = bstrFrontend;
3689 }
3690 /* paranoia - emergencystop is not a valid default */
3691 if (strFrontend == "emergencystop")
3692 strFrontend = Utf8Str::Empty;
3693 }
3694
3695 if (strFrontend != "emergencystop")
3696 {
3697 /* check the session state */
3698 SessionState_T state;
3699 rc = aSession->COMGETTER(State)(&state);
3700 if (FAILED(rc))
3701 return rc;
3702
3703 if (state != SessionState_Unlocked)
3704 return setError(VBOX_E_INVALID_OBJECT_STATE,
3705 tr("The given session is busy"));
3706
3707 /* get the IInternalSessionControl interface */
3708 ComPtr<IInternalSessionControl> control(aSession);
3709 ComAssertMsgRet(!control.isNull(),
3710 ("No IInternalSessionControl interface"),
3711 E_INVALIDARG);
3712
3713 /* get the teleporter enable state for the progress object init. */
3714 BOOL fTeleporterEnabled;
3715 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3716 if (FAILED(rc))
3717 return rc;
3718
3719 /* create a progress object */
3720 ComObjPtr<ProgressProxy> progress;
3721 progress.createObject();
3722 rc = progress->init(mParent,
3723 static_cast<IMachine*>(this),
3724 Bstr(tr("Starting VM")).raw(),
3725 TRUE /* aCancelable */,
3726 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3727 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3728 2 /* uFirstOperationWeight */,
3729 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3730
3731 if (SUCCEEDED(rc))
3732 {
3733 rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
3734 if (SUCCEEDED(rc))
3735 {
3736 progress.queryInterfaceTo(aProgress);
3737
3738 /* signal the client watcher thread */
3739 mParent->updateClientWatcher();
3740
3741 /* fire an event */
3742 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3743 }
3744 }
3745 }
3746 else
3747 {
3748 /* no progress object - either instant success or failure */
3749 *aProgress = NULL;
3750
3751 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3752
3753 if (mData->mSession.mState != SessionState_Locked)
3754 return setError(VBOX_E_INVALID_OBJECT_STATE,
3755 tr("The machine '%s' is not locked by a session"),
3756 mUserData->s.strName.c_str());
3757
3758 /* must have a VM process associated - do not kill normal API clients
3759 * with an open session */
3760 if (!Global::IsOnline(mData->mMachineState))
3761 return setError(VBOX_E_INVALID_OBJECT_STATE,
3762 tr("The machine '%s' does not have a VM process"),
3763 mUserData->s.strName.c_str());
3764
3765 /* forcibly terminate the VM process */
3766 if (mData->mSession.mPID != NIL_RTPROCESS)
3767 RTProcTerminate(mData->mSession.mPID);
3768
3769 /* signal the client watcher thread, as most likely the client has
3770 * been terminated */
3771 mParent->updateClientWatcher();
3772 }
3773
3774 return rc;
3775}
3776
3777STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3778{
3779 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3780 return setError(E_INVALIDARG,
3781 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3782 aPosition, SchemaDefs::MaxBootPosition);
3783
3784 if (aDevice == DeviceType_USB)
3785 return setError(E_NOTIMPL,
3786 tr("Booting from USB device is currently not supported"));
3787
3788 AutoCaller autoCaller(this);
3789 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3790
3791 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3792
3793 HRESULT rc = checkStateDependency(MutableStateDep);
3794 if (FAILED(rc)) return rc;
3795
3796 setModified(IsModified_MachineData);
3797 mHWData.backup();
3798 mHWData->mBootOrder[aPosition - 1] = aDevice;
3799
3800 return S_OK;
3801}
3802
3803STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3804{
3805 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3806 return setError(E_INVALIDARG,
3807 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3808 aPosition, SchemaDefs::MaxBootPosition);
3809
3810 AutoCaller autoCaller(this);
3811 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3812
3813 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3814
3815 *aDevice = mHWData->mBootOrder[aPosition - 1];
3816
3817 return S_OK;
3818}
3819
3820STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
3821 LONG aControllerPort,
3822 LONG aDevice,
3823 DeviceType_T aType,
3824 IMedium *aMedium)
3825{
3826 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3827 aControllerName, aControllerPort, aDevice, aType, aMedium));
3828
3829 CheckComArgStrNotEmptyOrNull(aControllerName);
3830
3831 AutoCaller autoCaller(this);
3832 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3833
3834 // request the host lock first, since might be calling Host methods for getting host drives;
3835 // next, protect the media tree all the while we're in here, as well as our member variables
3836 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
3837 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3838
3839 HRESULT rc = checkStateDependency(MutableStateDep);
3840 if (FAILED(rc)) return rc;
3841
3842 /// @todo NEWMEDIA implicit machine registration
3843 if (!mData->mRegistered)
3844 return setError(VBOX_E_INVALID_OBJECT_STATE,
3845 tr("Cannot attach storage devices to an unregistered machine"));
3846
3847 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3848
3849 /* Check for an existing controller. */
3850 ComObjPtr<StorageController> ctl;
3851 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
3852 if (FAILED(rc)) return rc;
3853
3854 StorageControllerType_T ctrlType;
3855 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3856 if (FAILED(rc))
3857 return setError(E_FAIL,
3858 tr("Could not get type of controller '%ls'"),
3859 aControllerName);
3860
3861 bool fSilent = false;
3862 Utf8Str strReconfig;
3863
3864 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3865 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3866 if (FAILED(rc))
3867 return rc;
3868 if ( mData->mMachineState == MachineState_Paused
3869 && strReconfig == "1")
3870 fSilent = true;
3871
3872 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3873 bool fHotplug = false;
3874 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3875 fHotplug = true;
3876
3877 if (fHotplug && !isControllerHotplugCapable(ctrlType))
3878 return setError(VBOX_E_INVALID_VM_STATE,
3879 tr("Controller '%ls' does not support hotplugging"),
3880 aControllerName);
3881
3882 // check that the port and device are not out of range
3883 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
3884 if (FAILED(rc)) return rc;
3885
3886 /* check if the device slot is already busy */
3887 MediumAttachment *pAttachTemp;
3888 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
3889 aControllerName,
3890 aControllerPort,
3891 aDevice)))
3892 {
3893 Medium *pMedium = pAttachTemp->getMedium();
3894 if (pMedium)
3895 {
3896 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3897 return setError(VBOX_E_OBJECT_IN_USE,
3898 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3899 pMedium->getLocationFull().c_str(),
3900 aControllerPort,
3901 aDevice,
3902 aControllerName);
3903 }
3904 else
3905 return setError(VBOX_E_OBJECT_IN_USE,
3906 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3907 aControllerPort, aDevice, aControllerName);
3908 }
3909
3910 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
3911 if (aMedium && medium.isNull())
3912 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3913
3914 AutoCaller mediumCaller(medium);
3915 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3916
3917 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3918
3919 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
3920 && !medium.isNull()
3921 )
3922 return setError(VBOX_E_OBJECT_IN_USE,
3923 tr("Medium '%s' is already attached to this virtual machine"),
3924 medium->getLocationFull().c_str());
3925
3926 if (!medium.isNull())
3927 {
3928 MediumType_T mtype = medium->getType();
3929 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3930 // For DVDs it's not written to the config file, so needs no global config
3931 // version bump. For floppies it's a new attribute "type", which is ignored
3932 // by older VirtualBox version, so needs no global config version bump either.
3933 // For hard disks this type is not accepted.
3934 if (mtype == MediumType_MultiAttach)
3935 {
3936 // This type is new with VirtualBox 4.0 and therefore requires settings
3937 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3938 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3939 // two reasons: The medium type is a property of the media registry tree, which
3940 // can reside in the global config file (for pre-4.0 media); we would therefore
3941 // possibly need to bump the global config version. We don't want to do that though
3942 // because that might make downgrading to pre-4.0 impossible.
3943 // As a result, we can only use these two new types if the medium is NOT in the
3944 // global registry:
3945 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
3946 if ( medium->isInRegistry(uuidGlobalRegistry)
3947 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3948 )
3949 return setError(VBOX_E_INVALID_OBJECT_STATE,
3950 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3951 "to machines that were created with VirtualBox 4.0 or later"),
3952 medium->getLocationFull().c_str());
3953 }
3954 }
3955
3956 bool fIndirect = false;
3957 if (!medium.isNull())
3958 fIndirect = medium->isReadOnly();
3959 bool associate = true;
3960
3961 do
3962 {
3963 if ( aType == DeviceType_HardDisk
3964 && mMediaData.isBackedUp())
3965 {
3966 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3967
3968 /* check if the medium was attached to the VM before we started
3969 * changing attachments in which case the attachment just needs to
3970 * be restored */
3971 if ((pAttachTemp = findAttachment(oldAtts, medium)))
3972 {
3973 AssertReturn(!fIndirect, E_FAIL);
3974
3975 /* see if it's the same bus/channel/device */
3976 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
3977 {
3978 /* the simplest case: restore the whole attachment
3979 * and return, nothing else to do */
3980 mMediaData->mAttachments.push_back(pAttachTemp);
3981 return S_OK;
3982 }
3983
3984 /* bus/channel/device differ; we need a new attachment object,
3985 * but don't try to associate it again */
3986 associate = false;
3987 break;
3988 }
3989 }
3990
3991 /* go further only if the attachment is to be indirect */
3992 if (!fIndirect)
3993 break;
3994
3995 /* perform the so called smart attachment logic for indirect
3996 * attachments. Note that smart attachment is only applicable to base
3997 * hard disks. */
3998
3999 if (medium->getParent().isNull())
4000 {
4001 /* first, investigate the backup copy of the current hard disk
4002 * attachments to make it possible to re-attach existing diffs to
4003 * another device slot w/o losing their contents */
4004 if (mMediaData.isBackedUp())
4005 {
4006 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4007
4008 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4009 uint32_t foundLevel = 0;
4010
4011 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
4012 it != oldAtts.end();
4013 ++it)
4014 {
4015 uint32_t level = 0;
4016 MediumAttachment *pAttach = *it;
4017 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4018 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4019 if (pMedium.isNull())
4020 continue;
4021
4022 if (pMedium->getBase(&level) == medium)
4023 {
4024 /* skip the hard disk if its currently attached (we
4025 * cannot attach the same hard disk twice) */
4026 if (findAttachment(mMediaData->mAttachments,
4027 pMedium))
4028 continue;
4029
4030 /* matched device, channel and bus (i.e. attached to the
4031 * same place) will win and immediately stop the search;
4032 * otherwise the attachment that has the youngest
4033 * descendant of medium will be used
4034 */
4035 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
4036 {
4037 /* the simplest case: restore the whole attachment
4038 * and return, nothing else to do */
4039 mMediaData->mAttachments.push_back(*it);
4040 return S_OK;
4041 }
4042 else if ( foundIt == oldAtts.end()
4043 || level > foundLevel /* prefer younger */
4044 )
4045 {
4046 foundIt = it;
4047 foundLevel = level;
4048 }
4049 }
4050 }
4051
4052 if (foundIt != oldAtts.end())
4053 {
4054 /* use the previously attached hard disk */
4055 medium = (*foundIt)->getMedium();
4056 mediumCaller.attach(medium);
4057 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4058 mediumLock.attach(medium);
4059 /* not implicit, doesn't require association with this VM */
4060 fIndirect = false;
4061 associate = false;
4062 /* go right to the MediumAttachment creation */
4063 break;
4064 }
4065 }
4066
4067 /* must give up the medium lock and medium tree lock as below we
4068 * go over snapshots, which needs a lock with higher lock order. */
4069 mediumLock.release();
4070 treeLock.release();
4071
4072 /* then, search through snapshots for the best diff in the given
4073 * hard disk's chain to base the new diff on */
4074
4075 ComObjPtr<Medium> base;
4076 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4077 while (snap)
4078 {
4079 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4080
4081 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
4082
4083 MediumAttachment *pAttachFound = NULL;
4084 uint32_t foundLevel = 0;
4085
4086 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
4087 it != snapAtts.end();
4088 ++it)
4089 {
4090 MediumAttachment *pAttach = *it;
4091 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4092 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4093 if (pMedium.isNull())
4094 continue;
4095
4096 uint32_t level = 0;
4097 if (pMedium->getBase(&level) == medium)
4098 {
4099 /* matched device, channel and bus (i.e. attached to the
4100 * same place) will win and immediately stop the search;
4101 * otherwise the attachment that has the youngest
4102 * descendant of medium will be used
4103 */
4104 if ( pAttach->getDevice() == aDevice
4105 && pAttach->getPort() == aControllerPort
4106 && pAttach->getControllerName() == aControllerName
4107 )
4108 {
4109 pAttachFound = pAttach;
4110 break;
4111 }
4112 else if ( !pAttachFound
4113 || level > foundLevel /* prefer younger */
4114 )
4115 {
4116 pAttachFound = pAttach;
4117 foundLevel = level;
4118 }
4119 }
4120 }
4121
4122 if (pAttachFound)
4123 {
4124 base = pAttachFound->getMedium();
4125 break;
4126 }
4127
4128 snap = snap->getParent();
4129 }
4130
4131 /* re-lock medium tree and the medium, as we need it below */
4132 treeLock.acquire();
4133 mediumLock.acquire();
4134
4135 /* found a suitable diff, use it as a base */
4136 if (!base.isNull())
4137 {
4138 medium = base;
4139 mediumCaller.attach(medium);
4140 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4141 mediumLock.attach(medium);
4142 }
4143 }
4144
4145 Utf8Str strFullSnapshotFolder;
4146 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4147
4148 ComObjPtr<Medium> diff;
4149 diff.createObject();
4150 // store this diff in the same registry as the parent
4151 Guid uuidRegistryParent;
4152 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4153 {
4154 // parent image has no registry: this can happen if we're attaching a new immutable
4155 // image that has not yet been attached (medium then points to the base and we're
4156 // creating the diff image for the immutable, and the parent is not yet registered);
4157 // put the parent in the machine registry then
4158 mediumLock.release();
4159 treeLock.release();
4160 alock.release();
4161 addMediumToRegistry(medium);
4162 alock.acquire();
4163 treeLock.acquire();
4164 mediumLock.acquire();
4165 medium->getFirstRegistryMachineId(uuidRegistryParent);
4166 }
4167 rc = diff->init(mParent,
4168 medium->getPreferredDiffFormat(),
4169 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4170 uuidRegistryParent);
4171 if (FAILED(rc)) return rc;
4172
4173 /* Apply the normal locking logic to the entire chain. */
4174 MediumLockList *pMediumLockList(new MediumLockList());
4175 mediumLock.release();
4176 treeLock.release();
4177 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4178 true /* fMediumLockWrite */,
4179 medium,
4180 *pMediumLockList);
4181 treeLock.acquire();
4182 mediumLock.acquire();
4183 if (SUCCEEDED(rc))
4184 {
4185 mediumLock.release();
4186 treeLock.release();
4187 rc = pMediumLockList->Lock();
4188 treeLock.acquire();
4189 mediumLock.acquire();
4190 if (FAILED(rc))
4191 setError(rc,
4192 tr("Could not lock medium when creating diff '%s'"),
4193 diff->getLocationFull().c_str());
4194 else
4195 {
4196 /* will release the lock before the potentially lengthy
4197 * operation, so protect with the special state */
4198 MachineState_T oldState = mData->mMachineState;
4199 setMachineState(MachineState_SettingUp);
4200
4201 mediumLock.release();
4202 treeLock.release();
4203 alock.release();
4204
4205 rc = medium->createDiffStorage(diff,
4206 MediumVariant_Standard,
4207 pMediumLockList,
4208 NULL /* aProgress */,
4209 true /* aWait */);
4210
4211 alock.acquire();
4212 treeLock.acquire();
4213 mediumLock.acquire();
4214
4215 setMachineState(oldState);
4216 }
4217 }
4218
4219 /* Unlock the media and free the associated memory. */
4220 delete pMediumLockList;
4221
4222 if (FAILED(rc)) return rc;
4223
4224 /* use the created diff for the actual attachment */
4225 medium = diff;
4226 mediumCaller.attach(medium);
4227 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4228 mediumLock.attach(medium);
4229 }
4230 while (0);
4231
4232 ComObjPtr<MediumAttachment> attachment;
4233 attachment.createObject();
4234 rc = attachment->init(this,
4235 medium,
4236 aControllerName,
4237 aControllerPort,
4238 aDevice,
4239 aType,
4240 fIndirect,
4241 false /* fPassthrough */,
4242 false /* fTempEject */,
4243 false /* fNonRotational */,
4244 false /* fDiscard */,
4245 Utf8Str::Empty);
4246 if (FAILED(rc)) return rc;
4247
4248 if (associate && !medium.isNull())
4249 {
4250 // as the last step, associate the medium to the VM
4251 rc = medium->addBackReference(mData->mUuid);
4252 // here we can fail because of Deleting, or being in process of creating a Diff
4253 if (FAILED(rc)) return rc;
4254
4255 mediumLock.release();
4256 treeLock.release();
4257 alock.release();
4258 addMediumToRegistry(medium);
4259 alock.acquire();
4260 treeLock.acquire();
4261 mediumLock.acquire();
4262 }
4263
4264 /* success: finally remember the attachment */
4265 setModified(IsModified_Storage);
4266 mMediaData.backup();
4267 mMediaData->mAttachments.push_back(attachment);
4268
4269 mediumLock.release();
4270 treeLock.release();
4271 alock.release();
4272
4273 if (fHotplug || fSilent)
4274 {
4275 MediumLockList *pMediumLockList(new MediumLockList());
4276
4277 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4278 true /* fMediumLockWrite */,
4279 NULL,
4280 *pMediumLockList);
4281 alock.acquire();
4282 if (FAILED(rc))
4283 delete pMediumLockList;
4284 else
4285 {
4286 mData->mSession.mLockedMedia.Unlock();
4287 alock.release();
4288 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4289 mData->mSession.mLockedMedia.Lock();
4290 alock.acquire();
4291 }
4292 alock.release();
4293
4294 if (SUCCEEDED(rc))
4295 {
4296 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4297 /* Remove lock list in case of error. */
4298 if (FAILED(rc))
4299 {
4300 mData->mSession.mLockedMedia.Unlock();
4301 mData->mSession.mLockedMedia.Remove(attachment);
4302 mData->mSession.mLockedMedia.Lock();
4303 }
4304 }
4305 }
4306
4307 mParent->saveModifiedRegistries();
4308
4309 return rc;
4310}
4311
4312STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4313 LONG aDevice)
4314{
4315 CheckComArgStrNotEmptyOrNull(aControllerName);
4316
4317 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4318 aControllerName, aControllerPort, aDevice));
4319
4320 AutoCaller autoCaller(this);
4321 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4322
4323 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4324
4325 HRESULT rc = checkStateDependency(MutableStateDep);
4326 if (FAILED(rc)) return rc;
4327
4328 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4329
4330 /* Check for an existing controller. */
4331 ComObjPtr<StorageController> ctl;
4332 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4333 if (FAILED(rc)) return rc;
4334
4335 StorageControllerType_T ctrlType;
4336 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4337 if (FAILED(rc))
4338 return setError(E_FAIL,
4339 tr("Could not get type of controller '%ls'"),
4340 aControllerName);
4341
4342 bool fSilent = false;
4343 Utf8Str strReconfig;
4344
4345 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4346 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4347 if (FAILED(rc))
4348 return rc;
4349 if ( mData->mMachineState == MachineState_Paused
4350 && strReconfig == "1")
4351 fSilent = true;
4352
4353 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4354 bool fHotplug = false;
4355 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4356 fHotplug = true;
4357
4358 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4359 return setError(VBOX_E_INVALID_VM_STATE,
4360 tr("Controller '%ls' does not support hotplugging"),
4361 aControllerName);
4362
4363 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4364 aControllerName,
4365 aControllerPort,
4366 aDevice);
4367 if (!pAttach)
4368 return setError(VBOX_E_OBJECT_NOT_FOUND,
4369 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4370 aDevice, aControllerPort, aControllerName);
4371
4372 /*
4373 * The VM has to detach the device before we delete any implicit diffs.
4374 * If this fails we can roll back without loosing data.
4375 */
4376 if (fHotplug || fSilent)
4377 {
4378 alock.release();
4379 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4380 alock.acquire();
4381 }
4382 if (FAILED(rc)) return rc;
4383
4384 /* If we are here everything went well and we can delete the implicit now. */
4385 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4386
4387 alock.release();
4388
4389 mParent->saveModifiedRegistries();
4390
4391 return rc;
4392}
4393
4394STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4395 LONG aDevice, BOOL aPassthrough)
4396{
4397 CheckComArgStrNotEmptyOrNull(aControllerName);
4398
4399 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4400 aControllerName, aControllerPort, aDevice, aPassthrough));
4401
4402 AutoCaller autoCaller(this);
4403 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4404
4405 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4406
4407 HRESULT rc = checkStateDependency(MutableStateDep);
4408 if (FAILED(rc)) return rc;
4409
4410 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4411
4412 if (Global::IsOnlineOrTransient(mData->mMachineState))
4413 return setError(VBOX_E_INVALID_VM_STATE,
4414 tr("Invalid machine state: %s"),
4415 Global::stringifyMachineState(mData->mMachineState));
4416
4417 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4418 aControllerName,
4419 aControllerPort,
4420 aDevice);
4421 if (!pAttach)
4422 return setError(VBOX_E_OBJECT_NOT_FOUND,
4423 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4424 aDevice, aControllerPort, aControllerName);
4425
4426
4427 setModified(IsModified_Storage);
4428 mMediaData.backup();
4429
4430 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4431
4432 if (pAttach->getType() != DeviceType_DVD)
4433 return setError(E_INVALIDARG,
4434 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4435 aDevice, aControllerPort, aControllerName);
4436 pAttach->updatePassthrough(!!aPassthrough);
4437
4438 return S_OK;
4439}
4440
4441STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4442 LONG aDevice, BOOL aTemporaryEject)
4443{
4444 CheckComArgStrNotEmptyOrNull(aControllerName);
4445
4446 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4447 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4448
4449 AutoCaller autoCaller(this);
4450 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4451
4452 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4453
4454 HRESULT rc = checkStateDependency(MutableStateDep);
4455 if (FAILED(rc)) return rc;
4456
4457 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4458 aControllerName,
4459 aControllerPort,
4460 aDevice);
4461 if (!pAttach)
4462 return setError(VBOX_E_OBJECT_NOT_FOUND,
4463 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4464 aDevice, aControllerPort, aControllerName);
4465
4466
4467 setModified(IsModified_Storage);
4468 mMediaData.backup();
4469
4470 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4471
4472 if (pAttach->getType() != DeviceType_DVD)
4473 return setError(E_INVALIDARG,
4474 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4475 aDevice, aControllerPort, aControllerName);
4476 pAttach->updateTempEject(!!aTemporaryEject);
4477
4478 return S_OK;
4479}
4480
4481STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4482 LONG aDevice, BOOL aNonRotational)
4483{
4484 CheckComArgStrNotEmptyOrNull(aControllerName);
4485
4486 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4487 aControllerName, aControllerPort, aDevice, aNonRotational));
4488
4489 AutoCaller autoCaller(this);
4490 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4491
4492 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4493
4494 HRESULT rc = checkStateDependency(MutableStateDep);
4495 if (FAILED(rc)) return rc;
4496
4497 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4498
4499 if (Global::IsOnlineOrTransient(mData->mMachineState))
4500 return setError(VBOX_E_INVALID_VM_STATE,
4501 tr("Invalid machine state: %s"),
4502 Global::stringifyMachineState(mData->mMachineState));
4503
4504 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4505 aControllerName,
4506 aControllerPort,
4507 aDevice);
4508 if (!pAttach)
4509 return setError(VBOX_E_OBJECT_NOT_FOUND,
4510 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4511 aDevice, aControllerPort, aControllerName);
4512
4513
4514 setModified(IsModified_Storage);
4515 mMediaData.backup();
4516
4517 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4518
4519 if (pAttach->getType() != DeviceType_HardDisk)
4520 return setError(E_INVALIDARG,
4521 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"),
4522 aDevice, aControllerPort, aControllerName);
4523 pAttach->updateNonRotational(!!aNonRotational);
4524
4525 return S_OK;
4526}
4527
4528STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4529 LONG aDevice, BOOL aDiscard)
4530{
4531 CheckComArgStrNotEmptyOrNull(aControllerName);
4532
4533 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4534 aControllerName, aControllerPort, aDevice, aDiscard));
4535
4536 AutoCaller autoCaller(this);
4537 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4538
4539 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4540
4541 HRESULT rc = checkStateDependency(MutableStateDep);
4542 if (FAILED(rc)) return rc;
4543
4544 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4545
4546 if (Global::IsOnlineOrTransient(mData->mMachineState))
4547 return setError(VBOX_E_INVALID_VM_STATE,
4548 tr("Invalid machine state: %s"),
4549 Global::stringifyMachineState(mData->mMachineState));
4550
4551 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4552 aControllerName,
4553 aControllerPort,
4554 aDevice);
4555 if (!pAttach)
4556 return setError(VBOX_E_OBJECT_NOT_FOUND,
4557 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4558 aDevice, aControllerPort, aControllerName);
4559
4560
4561 setModified(IsModified_Storage);
4562 mMediaData.backup();
4563
4564 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4565
4566 if (pAttach->getType() != DeviceType_HardDisk)
4567 return setError(E_INVALIDARG,
4568 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"),
4569 aDevice, aControllerPort, aControllerName);
4570 pAttach->updateDiscard(!!aDiscard);
4571
4572 return S_OK;
4573}
4574
4575STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4576 LONG aDevice)
4577{
4578 int rc = S_OK;
4579 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4580 aControllerName, aControllerPort, aDevice));
4581
4582 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4583
4584 return rc;
4585}
4586
4587STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4588 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4589{
4590 CheckComArgStrNotEmptyOrNull(aControllerName);
4591
4592 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4593 aControllerName, aControllerPort, aDevice));
4594
4595 AutoCaller autoCaller(this);
4596 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4597
4598 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4599
4600 HRESULT rc = checkStateDependency(MutableStateDep);
4601 if (FAILED(rc)) return rc;
4602
4603 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4604
4605 if (Global::IsOnlineOrTransient(mData->mMachineState))
4606 return setError(VBOX_E_INVALID_VM_STATE,
4607 tr("Invalid machine state: %s"),
4608 Global::stringifyMachineState(mData->mMachineState));
4609
4610 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4611 aControllerName,
4612 aControllerPort,
4613 aDevice);
4614 if (!pAttach)
4615 return setError(VBOX_E_OBJECT_NOT_FOUND,
4616 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4617 aDevice, aControllerPort, aControllerName);
4618
4619
4620 setModified(IsModified_Storage);
4621 mMediaData.backup();
4622
4623 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4624 if (aBandwidthGroup && group.isNull())
4625 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4626
4627 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4628
4629 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4630 if (strBandwidthGroupOld.isNotEmpty())
4631 {
4632 /* Get the bandwidth group object and release it - this must not fail. */
4633 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4634 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4635 Assert(SUCCEEDED(rc));
4636
4637 pBandwidthGroupOld->release();
4638 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4639 }
4640
4641 if (!group.isNull())
4642 {
4643 group->reference();
4644 pAttach->updateBandwidthGroup(group->getName());
4645 }
4646
4647 return S_OK;
4648}
4649
4650STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4651 LONG aControllerPort,
4652 LONG aDevice,
4653 DeviceType_T aType)
4654{
4655 HRESULT rc = S_OK;
4656
4657 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4658 aControllerName, aControllerPort, aDevice, aType));
4659
4660 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4661
4662 return rc;
4663}
4664
4665
4666
4667STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4668 LONG aControllerPort,
4669 LONG aDevice,
4670 BOOL aForce)
4671{
4672 int rc = S_OK;
4673 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4674 aControllerName, aControllerPort, aForce));
4675
4676 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4677
4678 return rc;
4679}
4680
4681STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4682 LONG aControllerPort,
4683 LONG aDevice,
4684 IMedium *aMedium,
4685 BOOL aForce)
4686{
4687 int rc = S_OK;
4688 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4689 aControllerName, aControllerPort, aDevice, aForce));
4690
4691 CheckComArgStrNotEmptyOrNull(aControllerName);
4692
4693 AutoCaller autoCaller(this);
4694 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4695
4696 // request the host lock first, since might be calling Host methods for getting host drives;
4697 // next, protect the media tree all the while we're in here, as well as our member variables
4698 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4699 this->lockHandle(),
4700 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4701
4702 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4703 aControllerName,
4704 aControllerPort,
4705 aDevice);
4706 if (pAttach.isNull())
4707 return setError(VBOX_E_OBJECT_NOT_FOUND,
4708 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4709 aDevice, aControllerPort, aControllerName);
4710
4711 /* Remember previously mounted medium. The medium before taking the
4712 * backup is not necessarily the same thing. */
4713 ComObjPtr<Medium> oldmedium;
4714 oldmedium = pAttach->getMedium();
4715
4716 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4717 if (aMedium && pMedium.isNull())
4718 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4719
4720 AutoCaller mediumCaller(pMedium);
4721 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4722
4723 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4724 if (pMedium)
4725 {
4726 DeviceType_T mediumType = pAttach->getType();
4727 switch (mediumType)
4728 {
4729 case DeviceType_DVD:
4730 case DeviceType_Floppy:
4731 break;
4732
4733 default:
4734 return setError(VBOX_E_INVALID_OBJECT_STATE,
4735 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
4736 aControllerPort,
4737 aDevice,
4738 aControllerName);
4739 }
4740 }
4741
4742 setModified(IsModified_Storage);
4743 mMediaData.backup();
4744
4745 {
4746 // The backup operation makes the pAttach reference point to the
4747 // old settings. Re-get the correct reference.
4748 pAttach = findAttachment(mMediaData->mAttachments,
4749 aControllerName,
4750 aControllerPort,
4751 aDevice);
4752 if (!oldmedium.isNull())
4753 oldmedium->removeBackReference(mData->mUuid);
4754 if (!pMedium.isNull())
4755 {
4756 pMedium->addBackReference(mData->mUuid);
4757
4758 mediumLock.release();
4759 multiLock.release();
4760 addMediumToRegistry(pMedium);
4761 multiLock.acquire();
4762 mediumLock.acquire();
4763 }
4764
4765 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4766 pAttach->updateMedium(pMedium);
4767 }
4768
4769 setModified(IsModified_Storage);
4770
4771 mediumLock.release();
4772 multiLock.release();
4773 rc = onMediumChange(pAttach, aForce);
4774 multiLock.acquire();
4775 mediumLock.acquire();
4776
4777 /* On error roll back this change only. */
4778 if (FAILED(rc))
4779 {
4780 if (!pMedium.isNull())
4781 pMedium->removeBackReference(mData->mUuid);
4782 pAttach = findAttachment(mMediaData->mAttachments,
4783 aControllerName,
4784 aControllerPort,
4785 aDevice);
4786 /* If the attachment is gone in the meantime, bail out. */
4787 if (pAttach.isNull())
4788 return rc;
4789 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4790 if (!oldmedium.isNull())
4791 oldmedium->addBackReference(mData->mUuid);
4792 pAttach->updateMedium(oldmedium);
4793 }
4794
4795 mediumLock.release();
4796 multiLock.release();
4797
4798 mParent->saveModifiedRegistries();
4799
4800 return rc;
4801}
4802
4803STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
4804 LONG aControllerPort,
4805 LONG aDevice,
4806 IMedium **aMedium)
4807{
4808 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4809 aControllerName, aControllerPort, aDevice));
4810
4811 CheckComArgStrNotEmptyOrNull(aControllerName);
4812 CheckComArgOutPointerValid(aMedium);
4813
4814 AutoCaller autoCaller(this);
4815 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4816
4817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4818
4819 *aMedium = NULL;
4820
4821 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4822 aControllerName,
4823 aControllerPort,
4824 aDevice);
4825 if (pAttach.isNull())
4826 return setError(VBOX_E_OBJECT_NOT_FOUND,
4827 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4828 aDevice, aControllerPort, aControllerName);
4829
4830 pAttach->getMedium().queryInterfaceTo(aMedium);
4831
4832 return S_OK;
4833}
4834
4835STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
4836{
4837 CheckComArgOutPointerValid(port);
4838 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
4839
4840 AutoCaller autoCaller(this);
4841 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4842
4843 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4844
4845 mSerialPorts[slot].queryInterfaceTo(port);
4846
4847 return S_OK;
4848}
4849
4850STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
4851{
4852 CheckComArgOutPointerValid(port);
4853 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
4854
4855 AutoCaller autoCaller(this);
4856 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4857
4858 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4859
4860 mParallelPorts[slot].queryInterfaceTo(port);
4861
4862 return S_OK;
4863}
4864
4865STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
4866{
4867 CheckComArgOutPointerValid(adapter);
4868 /* Do not assert if slot is out of range, just return the advertised
4869 status. testdriver/vbox.py triggers this in logVmInfo. */
4870 if (slot >= mNetworkAdapters.size())
4871 return setError(E_INVALIDARG,
4872 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4873 slot, mNetworkAdapters.size());
4874
4875 AutoCaller autoCaller(this);
4876 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4877
4878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4879
4880 mNetworkAdapters[slot].queryInterfaceTo(adapter);
4881
4882 return S_OK;
4883}
4884
4885STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
4886{
4887 CheckComArgOutSafeArrayPointerValid(aKeys);
4888
4889 AutoCaller autoCaller(this);
4890 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4891
4892 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4893
4894 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4895 int i = 0;
4896 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4897 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4898 ++it, ++i)
4899 {
4900 const Utf8Str &strKey = it->first;
4901 strKey.cloneTo(&saKeys[i]);
4902 }
4903 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
4904
4905 return S_OK;
4906 }
4907
4908 /**
4909 * @note Locks this object for reading.
4910 */
4911STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
4912 BSTR *aValue)
4913{
4914 CheckComArgStrNotEmptyOrNull(aKey);
4915 CheckComArgOutPointerValid(aValue);
4916
4917 AutoCaller autoCaller(this);
4918 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4919
4920 /* start with nothing found */
4921 Bstr bstrResult("");
4922
4923 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4924
4925 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
4926 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4927 // found:
4928 bstrResult = it->second; // source is a Utf8Str
4929
4930 /* return the result to caller (may be empty) */
4931 bstrResult.cloneTo(aValue);
4932
4933 return S_OK;
4934}
4935
4936 /**
4937 * @note Locks mParent for writing + this object for writing.
4938 */
4939STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
4940{
4941 CheckComArgStrNotEmptyOrNull(aKey);
4942
4943 AutoCaller autoCaller(this);
4944 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4945
4946 Utf8Str strKey(aKey);
4947 Utf8Str strValue(aValue);
4948 Utf8Str strOldValue; // empty
4949
4950 // locking note: we only hold the read lock briefly to look up the old value,
4951 // then release it and call the onExtraCanChange callbacks. There is a small
4952 // chance of a race insofar as the callback might be called twice if two callers
4953 // change the same key at the same time, but that's a much better solution
4954 // than the deadlock we had here before. The actual changing of the extradata
4955 // is then performed under the write lock and race-free.
4956
4957 // look up the old value first; if nothing has changed then we need not do anything
4958 {
4959 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4960 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
4961 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4962 strOldValue = it->second;
4963 }
4964
4965 bool fChanged;
4966 if ((fChanged = (strOldValue != strValue)))
4967 {
4968 // ask for permission from all listeners outside the locks;
4969 // onExtraDataCanChange() only briefly requests the VirtualBox
4970 // lock to copy the list of callbacks to invoke
4971 Bstr error;
4972 Bstr bstrValue(aValue);
4973
4974 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
4975 {
4976 const char *sep = error.isEmpty() ? "" : ": ";
4977 CBSTR err = error.raw();
4978 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4979 sep, err));
4980 return setError(E_ACCESSDENIED,
4981 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
4982 aKey,
4983 bstrValue.raw(),
4984 sep,
4985 err);
4986 }
4987
4988 // data is changing and change not vetoed: then write it out under the lock
4989 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4990
4991 if (isSnapshotMachine())
4992 {
4993 HRESULT rc = checkStateDependency(MutableStateDep);
4994 if (FAILED(rc)) return rc;
4995 }
4996
4997 if (strValue.isEmpty())
4998 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
4999 else
5000 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
5001 // creates a new key if needed
5002
5003 bool fNeedsGlobalSaveSettings = false;
5004 saveSettings(&fNeedsGlobalSaveSettings);
5005
5006 if (fNeedsGlobalSaveSettings)
5007 {
5008 // save the global settings; for that we should hold only the VirtualBox lock
5009 alock.release();
5010 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5011 mParent->saveSettings();
5012 }
5013 }
5014
5015 // fire notification outside the lock
5016 if (fChanged)
5017 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
5018
5019 return S_OK;
5020}
5021
5022STDMETHODIMP Machine::SaveSettings()
5023{
5024 AutoCaller autoCaller(this);
5025 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5026
5027 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5028
5029 /* when there was auto-conversion, we want to save the file even if
5030 * the VM is saved */
5031 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
5032 if (FAILED(rc)) return rc;
5033
5034 /* the settings file path may never be null */
5035 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5036
5037 /* save all VM data excluding snapshots */
5038 bool fNeedsGlobalSaveSettings = false;
5039 rc = saveSettings(&fNeedsGlobalSaveSettings);
5040 mlock.release();
5041
5042 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5043 {
5044 // save the global settings; for that we should hold only the VirtualBox lock
5045 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5046 rc = mParent->saveSettings();
5047 }
5048
5049 return rc;
5050}
5051
5052STDMETHODIMP Machine::DiscardSettings()
5053{
5054 AutoCaller autoCaller(this);
5055 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5056
5057 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5058
5059 HRESULT rc = checkStateDependency(MutableStateDep);
5060 if (FAILED(rc)) return rc;
5061
5062 /*
5063 * during this rollback, the session will be notified if data has
5064 * been actually changed
5065 */
5066 rollback(true /* aNotify */);
5067
5068 return S_OK;
5069}
5070
5071/** @note Locks objects! */
5072STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
5073 ComSafeArrayOut(IMedium*, aMedia))
5074{
5075 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5076 AutoLimitedCaller autoCaller(this);
5077 AssertComRCReturnRC(autoCaller.rc());
5078
5079 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5080
5081 Guid id(getId());
5082
5083 if (mData->mSession.mState != SessionState_Unlocked)
5084 return setError(VBOX_E_INVALID_OBJECT_STATE,
5085 tr("Cannot unregister the machine '%s' while it is locked"),
5086 mUserData->s.strName.c_str());
5087
5088 // wait for state dependents to drop to zero
5089 ensureNoStateDependencies();
5090
5091 if (!mData->mAccessible)
5092 {
5093 // inaccessible maschines can only be unregistered; uninitialize ourselves
5094 // here because currently there may be no unregistered that are inaccessible
5095 // (this state combination is not supported). Note releasing the caller and
5096 // leaving the lock before calling uninit()
5097 alock.release();
5098 autoCaller.release();
5099
5100 uninit();
5101
5102 mParent->unregisterMachine(this, id);
5103 // calls VirtualBox::saveSettings()
5104
5105 return S_OK;
5106 }
5107
5108 HRESULT rc = S_OK;
5109
5110 // discard saved state
5111 if (mData->mMachineState == MachineState_Saved)
5112 {
5113 // add the saved state file to the list of files the caller should delete
5114 Assert(!mSSData->strStateFilePath.isEmpty());
5115 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5116
5117 mSSData->strStateFilePath.setNull();
5118
5119 // unconditionally set the machine state to powered off, we now
5120 // know no session has locked the machine
5121 mData->mMachineState = MachineState_PoweredOff;
5122 }
5123
5124 size_t cSnapshots = 0;
5125 if (mData->mFirstSnapshot)
5126 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5127 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
5128 // fail now before we start detaching media
5129 return setError(VBOX_E_INVALID_OBJECT_STATE,
5130 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5131 mUserData->s.strName.c_str(), cSnapshots);
5132
5133 // This list collects the medium objects from all medium attachments
5134 // which we will detach from the machine and its snapshots, in a specific
5135 // order which allows for closing all media without getting "media in use"
5136 // errors, simply by going through the list from the front to the back:
5137 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5138 // and must be closed before the parent media from the snapshots, or closing the parents
5139 // will fail because they still have children);
5140 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5141 // the root ("first") snapshot of the machine.
5142 MediaList llMedia;
5143
5144 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5145 && mMediaData->mAttachments.size()
5146 )
5147 {
5148 // we have media attachments: detach them all and add the Medium objects to our list
5149 if (cleanupMode != CleanupMode_UnregisterOnly)
5150 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5151 else
5152 return setError(VBOX_E_INVALID_OBJECT_STATE,
5153 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5154 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5155 }
5156
5157 if (cSnapshots)
5158 {
5159 // autoCleanup must be true here, or we would have failed above
5160
5161 // add the media from the medium attachments of the snapshots to llMedia
5162 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5163 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5164 // into the children first
5165
5166 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5167 MachineState_T oldState = mData->mMachineState;
5168 mData->mMachineState = MachineState_DeletingSnapshot;
5169
5170 // make a copy of the first snapshot so the refcount does not drop to 0
5171 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5172 // because of the AutoCaller voodoo)
5173 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5174
5175 // GO!
5176 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5177
5178 mData->mMachineState = oldState;
5179 }
5180
5181 if (FAILED(rc))
5182 {
5183 rollbackMedia();
5184 return rc;
5185 }
5186
5187 // commit all the media changes made above
5188 commitMedia();
5189
5190 mData->mRegistered = false;
5191
5192 // machine lock no longer needed
5193 alock.release();
5194
5195 // return media to caller
5196 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5197 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5198
5199 mParent->unregisterMachine(this, id);
5200 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5201
5202 return S_OK;
5203}
5204
5205struct Machine::DeleteTask
5206{
5207 ComObjPtr<Machine> pMachine;
5208 RTCList<ComPtr<IMedium> > llMediums;
5209 StringsList llFilesToDelete;
5210 ComObjPtr<Progress> pProgress;
5211};
5212
5213STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5214{
5215 LogFlowFuncEnter();
5216
5217 AutoCaller autoCaller(this);
5218 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5219
5220 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5221
5222 HRESULT rc = checkStateDependency(MutableStateDep);
5223 if (FAILED(rc)) return rc;
5224
5225 if (mData->mRegistered)
5226 return setError(VBOX_E_INVALID_VM_STATE,
5227 tr("Cannot delete settings of a registered machine"));
5228
5229 DeleteTask *pTask = new DeleteTask;
5230 pTask->pMachine = this;
5231 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5232
5233 // collect files to delete
5234 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5235
5236 for (size_t i = 0; i < sfaMedia.size(); ++i)
5237 {
5238 IMedium *pIMedium(sfaMedia[i]);
5239 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5240 if (pMedium.isNull())
5241 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5242 SafeArray<BSTR> ids;
5243 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5244 if (FAILED(rc)) return rc;
5245 /* At this point the medium should not have any back references
5246 * anymore. If it has it is attached to another VM and *must* not
5247 * deleted. */
5248 if (ids.size() < 1)
5249 pTask->llMediums.append(pMedium);
5250 }
5251 if (mData->pMachineConfigFile->fileExists())
5252 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5253
5254 pTask->pProgress.createObject();
5255 pTask->pProgress->init(getVirtualBox(),
5256 static_cast<IMachine*>(this) /* aInitiator */,
5257 Bstr(tr("Deleting files")).raw(),
5258 true /* fCancellable */,
5259 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5260 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5261
5262 int vrc = RTThreadCreate(NULL,
5263 Machine::deleteThread,
5264 (void*)pTask,
5265 0,
5266 RTTHREADTYPE_MAIN_WORKER,
5267 0,
5268 "MachineDelete");
5269
5270 pTask->pProgress.queryInterfaceTo(aProgress);
5271
5272 if (RT_FAILURE(vrc))
5273 {
5274 delete pTask;
5275 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5276 }
5277
5278 LogFlowFuncLeave();
5279
5280 return S_OK;
5281}
5282
5283/**
5284 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5285 * calls Machine::deleteTaskWorker() on the actual machine object.
5286 * @param Thread
5287 * @param pvUser
5288 * @return
5289 */
5290/*static*/
5291DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5292{
5293 LogFlowFuncEnter();
5294
5295 DeleteTask *pTask = (DeleteTask*)pvUser;
5296 Assert(pTask);
5297 Assert(pTask->pMachine);
5298 Assert(pTask->pProgress);
5299
5300 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5301 pTask->pProgress->notifyComplete(rc);
5302
5303 delete pTask;
5304
5305 LogFlowFuncLeave();
5306
5307 NOREF(Thread);
5308
5309 return VINF_SUCCESS;
5310}
5311
5312/**
5313 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5314 * @param task
5315 * @return
5316 */
5317HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5318{
5319 AutoCaller autoCaller(this);
5320 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5321
5322 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5323
5324 HRESULT rc = S_OK;
5325
5326 try
5327 {
5328 ULONG uLogHistoryCount = 3;
5329 ComPtr<ISystemProperties> systemProperties;
5330 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5331 if (FAILED(rc)) throw rc;
5332
5333 if (!systemProperties.isNull())
5334 {
5335 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5336 if (FAILED(rc)) throw rc;
5337 }
5338
5339 MachineState_T oldState = mData->mMachineState;
5340 setMachineState(MachineState_SettingUp);
5341 alock.release();
5342 for (size_t i = 0; i < task.llMediums.size(); ++i)
5343 {
5344 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5345 {
5346 AutoCaller mac(pMedium);
5347 if (FAILED(mac.rc())) throw mac.rc();
5348 Utf8Str strLocation = pMedium->getLocationFull();
5349 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5350 if (FAILED(rc)) throw rc;
5351 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5352 }
5353 ComPtr<IProgress> pProgress2;
5354 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5355 if (FAILED(rc)) throw rc;
5356 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5357 if (FAILED(rc)) throw rc;
5358 /* Check the result of the asynchrony process. */
5359 LONG iRc;
5360 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5361 if (FAILED(rc)) throw rc;
5362 /* If the thread of the progress object has an error, then
5363 * retrieve the error info from there, or it'll be lost. */
5364 if (FAILED(iRc))
5365 throw setError(ProgressErrorInfo(pProgress2));
5366 }
5367 setMachineState(oldState);
5368 alock.acquire();
5369
5370 // delete the files pushed on the task list by Machine::Delete()
5371 // (this includes saved states of the machine and snapshots and
5372 // medium storage files from the IMedium list passed in, and the
5373 // machine XML file)
5374 StringsList::const_iterator it = task.llFilesToDelete.begin();
5375 while (it != task.llFilesToDelete.end())
5376 {
5377 const Utf8Str &strFile = *it;
5378 LogFunc(("Deleting file %s\n", strFile.c_str()));
5379 int vrc = RTFileDelete(strFile.c_str());
5380 if (RT_FAILURE(vrc))
5381 throw setError(VBOX_E_IPRT_ERROR,
5382 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5383
5384 ++it;
5385 if (it == task.llFilesToDelete.end())
5386 {
5387 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5388 if (FAILED(rc)) throw rc;
5389 break;
5390 }
5391
5392 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5393 if (FAILED(rc)) throw rc;
5394 }
5395
5396 /* delete the settings only when the file actually exists */
5397 if (mData->pMachineConfigFile->fileExists())
5398 {
5399 /* Delete any backup or uncommitted XML files. Ignore failures.
5400 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5401 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5402 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5403 RTFileDelete(otherXml.c_str());
5404 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5405 RTFileDelete(otherXml.c_str());
5406
5407 /* delete the Logs folder, nothing important should be left
5408 * there (we don't check for errors because the user might have
5409 * some private files there that we don't want to delete) */
5410 Utf8Str logFolder;
5411 getLogFolder(logFolder);
5412 Assert(logFolder.length());
5413 if (RTDirExists(logFolder.c_str()))
5414 {
5415 /* Delete all VBox.log[.N] files from the Logs folder
5416 * (this must be in sync with the rotation logic in
5417 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5418 * files that may have been created by the GUI. */
5419 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5420 logFolder.c_str(), RTPATH_DELIMITER);
5421 RTFileDelete(log.c_str());
5422 log = Utf8StrFmt("%s%cVBox.png",
5423 logFolder.c_str(), RTPATH_DELIMITER);
5424 RTFileDelete(log.c_str());
5425 for (int i = uLogHistoryCount; i > 0; i--)
5426 {
5427 log = Utf8StrFmt("%s%cVBox.log.%d",
5428 logFolder.c_str(), RTPATH_DELIMITER, i);
5429 RTFileDelete(log.c_str());
5430 log = Utf8StrFmt("%s%cVBox.png.%d",
5431 logFolder.c_str(), RTPATH_DELIMITER, i);
5432 RTFileDelete(log.c_str());
5433 }
5434
5435 RTDirRemove(logFolder.c_str());
5436 }
5437
5438 /* delete the Snapshots folder, nothing important should be left
5439 * there (we don't check for errors because the user might have
5440 * some private files there that we don't want to delete) */
5441 Utf8Str strFullSnapshotFolder;
5442 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5443 Assert(!strFullSnapshotFolder.isEmpty());
5444 if (RTDirExists(strFullSnapshotFolder.c_str()))
5445 RTDirRemove(strFullSnapshotFolder.c_str());
5446
5447 // delete the directory that contains the settings file, but only
5448 // if it matches the VM name
5449 Utf8Str settingsDir;
5450 if (isInOwnDir(&settingsDir))
5451 RTDirRemove(settingsDir.c_str());
5452 }
5453
5454 alock.release();
5455
5456 mParent->saveModifiedRegistries();
5457 }
5458 catch (HRESULT aRC) { rc = aRC; }
5459
5460 return rc;
5461}
5462
5463STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5464{
5465 CheckComArgOutPointerValid(aSnapshot);
5466
5467 AutoCaller autoCaller(this);
5468 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5469
5470 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5471
5472 ComObjPtr<Snapshot> pSnapshot;
5473 HRESULT rc;
5474
5475 if (!aNameOrId || !*aNameOrId)
5476 // null case (caller wants root snapshot): findSnapshotById() handles this
5477 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5478 else
5479 {
5480 Guid uuid(aNameOrId);
5481 if (uuid.isValid())
5482 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5483 else
5484 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5485 }
5486 pSnapshot.queryInterfaceTo(aSnapshot);
5487
5488 return rc;
5489}
5490
5491STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5492{
5493 CheckComArgStrNotEmptyOrNull(aName);
5494 CheckComArgStrNotEmptyOrNull(aHostPath);
5495
5496 AutoCaller autoCaller(this);
5497 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5498
5499 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5500
5501 HRESULT rc = checkStateDependency(MutableStateDep);
5502 if (FAILED(rc)) return rc;
5503
5504 Utf8Str strName(aName);
5505
5506 ComObjPtr<SharedFolder> sharedFolder;
5507 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5508 if (SUCCEEDED(rc))
5509 return setError(VBOX_E_OBJECT_IN_USE,
5510 tr("Shared folder named '%s' already exists"),
5511 strName.c_str());
5512
5513 sharedFolder.createObject();
5514 rc = sharedFolder->init(getMachine(),
5515 strName,
5516 aHostPath,
5517 !!aWritable,
5518 !!aAutoMount,
5519 true /* fFailOnError */);
5520 if (FAILED(rc)) return rc;
5521
5522 setModified(IsModified_SharedFolders);
5523 mHWData.backup();
5524 mHWData->mSharedFolders.push_back(sharedFolder);
5525
5526 /* inform the direct session if any */
5527 alock.release();
5528 onSharedFolderChange();
5529
5530 return S_OK;
5531}
5532
5533STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5534{
5535 CheckComArgStrNotEmptyOrNull(aName);
5536
5537 AutoCaller autoCaller(this);
5538 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5539
5540 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5541
5542 HRESULT rc = checkStateDependency(MutableStateDep);
5543 if (FAILED(rc)) return rc;
5544
5545 ComObjPtr<SharedFolder> sharedFolder;
5546 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5547 if (FAILED(rc)) return rc;
5548
5549 setModified(IsModified_SharedFolders);
5550 mHWData.backup();
5551 mHWData->mSharedFolders.remove(sharedFolder);
5552
5553 /* inform the direct session if any */
5554 alock.release();
5555 onSharedFolderChange();
5556
5557 return S_OK;
5558}
5559
5560STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5561{
5562 CheckComArgOutPointerValid(aCanShow);
5563
5564 /* start with No */
5565 *aCanShow = FALSE;
5566
5567 AutoCaller autoCaller(this);
5568 AssertComRCReturnRC(autoCaller.rc());
5569
5570 ComPtr<IInternalSessionControl> directControl;
5571 {
5572 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5573
5574 if (mData->mSession.mState != SessionState_Locked)
5575 return setError(VBOX_E_INVALID_VM_STATE,
5576 tr("Machine is not locked for session (session state: %s)"),
5577 Global::stringifySessionState(mData->mSession.mState));
5578
5579 directControl = mData->mSession.mDirectControl;
5580 }
5581
5582 /* ignore calls made after #OnSessionEnd() is called */
5583 if (!directControl)
5584 return S_OK;
5585
5586 LONG64 dummy;
5587 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5588}
5589
5590STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5591{
5592 CheckComArgOutPointerValid(aWinId);
5593
5594 AutoCaller autoCaller(this);
5595 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5596
5597 ComPtr<IInternalSessionControl> directControl;
5598 {
5599 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5600
5601 if (mData->mSession.mState != SessionState_Locked)
5602 return setError(E_FAIL,
5603 tr("Machine is not locked for session (session state: %s)"),
5604 Global::stringifySessionState(mData->mSession.mState));
5605
5606 directControl = mData->mSession.mDirectControl;
5607 }
5608
5609 /* ignore calls made after #OnSessionEnd() is called */
5610 if (!directControl)
5611 return S_OK;
5612
5613 BOOL dummy;
5614 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5615}
5616
5617#ifdef VBOX_WITH_GUEST_PROPS
5618/**
5619 * Look up a guest property in VBoxSVC's internal structures.
5620 */
5621HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5622 BSTR *aValue,
5623 LONG64 *aTimestamp,
5624 BSTR *aFlags) const
5625{
5626 using namespace guestProp;
5627
5628 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5629 Utf8Str strName(aName);
5630 HWData::GuestPropertyMap::const_iterator it =
5631 mHWData->mGuestProperties.find(strName);
5632
5633 if (it != mHWData->mGuestProperties.end())
5634 {
5635 char szFlags[MAX_FLAGS_LEN + 1];
5636 it->second.strValue.cloneTo(aValue);
5637 *aTimestamp = it->second.mTimestamp;
5638 writeFlags(it->second.mFlags, szFlags);
5639 Bstr(szFlags).cloneTo(aFlags);
5640 }
5641
5642 return S_OK;
5643}
5644
5645/**
5646 * Query the VM that a guest property belongs to for the property.
5647 * @returns E_ACCESSDENIED if the VM process is not available or not
5648 * currently handling queries and the lookup should then be done in
5649 * VBoxSVC.
5650 */
5651HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5652 BSTR *aValue,
5653 LONG64 *aTimestamp,
5654 BSTR *aFlags) const
5655{
5656 HRESULT rc;
5657 ComPtr<IInternalSessionControl> directControl;
5658 directControl = mData->mSession.mDirectControl;
5659
5660 /* fail if we were called after #OnSessionEnd() is called. This is a
5661 * silly race condition. */
5662
5663 if (!directControl)
5664 rc = E_ACCESSDENIED;
5665 else
5666 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5667 false /* isSetter */,
5668 aValue, aTimestamp, aFlags);
5669 return rc;
5670}
5671#endif // VBOX_WITH_GUEST_PROPS
5672
5673STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5674 BSTR *aValue,
5675 LONG64 *aTimestamp,
5676 BSTR *aFlags)
5677{
5678#ifndef VBOX_WITH_GUEST_PROPS
5679 ReturnComNotImplemented();
5680#else // VBOX_WITH_GUEST_PROPS
5681 CheckComArgStrNotEmptyOrNull(aName);
5682 CheckComArgOutPointerValid(aValue);
5683 CheckComArgOutPointerValid(aTimestamp);
5684 CheckComArgOutPointerValid(aFlags);
5685
5686 AutoCaller autoCaller(this);
5687 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5688
5689 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5690 if (rc == E_ACCESSDENIED)
5691 /* The VM is not running or the service is not (yet) accessible */
5692 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5693 return rc;
5694#endif // VBOX_WITH_GUEST_PROPS
5695}
5696
5697STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5698{
5699 LONG64 dummyTimestamp;
5700 Bstr dummyFlags;
5701 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5702}
5703
5704STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5705{
5706 Bstr dummyValue;
5707 Bstr dummyFlags;
5708 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5709}
5710
5711#ifdef VBOX_WITH_GUEST_PROPS
5712/**
5713 * Set a guest property in VBoxSVC's internal structures.
5714 */
5715HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5716 IN_BSTR aFlags)
5717{
5718 using namespace guestProp;
5719
5720 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5721 HRESULT rc = S_OK;
5722 HWData::GuestProperty property;
5723 property.mFlags = NILFLAG;
5724
5725 rc = checkStateDependency(MutableStateDep);
5726 if (FAILED(rc)) return rc;
5727
5728 try
5729 {
5730 Utf8Str utf8Name(aName);
5731 Utf8Str utf8Flags(aFlags);
5732 uint32_t fFlags = NILFLAG;
5733 if ( (aFlags != NULL)
5734 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags))
5735 )
5736 return setError(E_INVALIDARG,
5737 tr("Invalid guest property flag values: '%ls'"),
5738 aFlags);
5739
5740 HWData::GuestPropertyMap::iterator it =
5741 mHWData->mGuestProperties.find(utf8Name);
5742
5743 if (it == mHWData->mGuestProperties.end())
5744 {
5745 setModified(IsModified_MachineData);
5746 mHWData.backupEx();
5747
5748 RTTIMESPEC time;
5749 HWData::GuestProperty prop;
5750 prop.strValue = aValue;
5751 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5752 prop.mFlags = fFlags;
5753
5754 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
5755 }
5756 else
5757 {
5758 if (it->second.mFlags & (RDONLYHOST))
5759 {
5760 rc = setError(E_ACCESSDENIED,
5761 tr("The property '%ls' cannot be changed by the host"),
5762 aName);
5763 }
5764 else
5765 {
5766 setModified(IsModified_MachineData);
5767 mHWData.backupEx();
5768
5769 /* The backupEx() operation invalidates our iterator,
5770 * so get a new one. */
5771 it = mHWData->mGuestProperties.find(utf8Name);
5772 Assert(it != mHWData->mGuestProperties.end());
5773
5774 if (RT_VALID_PTR(aValue) && *(aValue) != '\0')
5775 {
5776 RTTIMESPEC time;
5777 it->second.strValue = aValue;
5778 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5779 if (aFlags != NULL)
5780 it->second.mFlags = fFlags;
5781 }
5782 else
5783 {
5784 mHWData->mGuestProperties.erase(it);
5785 }
5786 }
5787 }
5788
5789 if ( SUCCEEDED(rc)
5790 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5791 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5792 RTSTR_MAX,
5793 utf8Name.c_str(),
5794 RTSTR_MAX,
5795 NULL)
5796 )
5797 )
5798 {
5799 alock.release();
5800
5801 mParent->onGuestPropertyChange(mData->mUuid, aName,
5802 aValue ? aValue : Bstr("").raw(),
5803 aFlags ? aFlags : Bstr("").raw());
5804 }
5805 }
5806 catch (std::bad_alloc &)
5807 {
5808 rc = E_OUTOFMEMORY;
5809 }
5810
5811 return rc;
5812}
5813
5814/**
5815 * Set a property on the VM that that property belongs to.
5816 * @returns E_ACCESSDENIED if the VM process is not available or not
5817 * currently handling queries and the setting should then be done in
5818 * VBoxSVC.
5819 */
5820HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
5821 IN_BSTR aFlags)
5822{
5823 HRESULT rc;
5824
5825 try
5826 {
5827 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5828
5829 BSTR dummy = NULL; /* will not be changed (setter) */
5830 LONG64 dummy64;
5831 if (!directControl)
5832 rc = E_ACCESSDENIED;
5833 else
5834 /** @todo Fix when adding DeleteGuestProperty(),
5835 see defect. */
5836 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
5837 true /* isSetter */,
5838 &dummy, &dummy64, &dummy);
5839 }
5840 catch (std::bad_alloc &)
5841 {
5842 rc = E_OUTOFMEMORY;
5843 }
5844
5845 return rc;
5846}
5847#endif // VBOX_WITH_GUEST_PROPS
5848
5849STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
5850 IN_BSTR aFlags)
5851{
5852#ifndef VBOX_WITH_GUEST_PROPS
5853 ReturnComNotImplemented();
5854#else // VBOX_WITH_GUEST_PROPS
5855 CheckComArgStrNotEmptyOrNull(aName);
5856 CheckComArgMaybeNull(aFlags);
5857 CheckComArgMaybeNull(aValue);
5858
5859 AutoCaller autoCaller(this);
5860 if (FAILED(autoCaller.rc()))
5861 return autoCaller.rc();
5862
5863 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
5864 if (rc == E_ACCESSDENIED)
5865 /* The VM is not running or the service is not (yet) accessible */
5866 rc = setGuestPropertyToService(aName, aValue, aFlags);
5867 return rc;
5868#endif // VBOX_WITH_GUEST_PROPS
5869}
5870
5871STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
5872{
5873 return SetGuestProperty(aName, aValue, NULL);
5874}
5875
5876STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
5877{
5878 return SetGuestProperty(aName, NULL, NULL);
5879}
5880
5881#ifdef VBOX_WITH_GUEST_PROPS
5882/**
5883 * Enumerate the guest properties in VBoxSVC's internal structures.
5884 */
5885HRESULT Machine::enumerateGuestPropertiesInService
5886 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5887 ComSafeArrayOut(BSTR, aValues),
5888 ComSafeArrayOut(LONG64, aTimestamps),
5889 ComSafeArrayOut(BSTR, aFlags))
5890{
5891 using namespace guestProp;
5892
5893 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5894 Utf8Str strPatterns(aPatterns);
5895
5896 HWData::GuestPropertyMap propMap;
5897
5898 /*
5899 * Look for matching patterns and build up a list.
5900 */
5901 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5902 while (it != mHWData->mGuestProperties.end())
5903 {
5904 if ( strPatterns.isEmpty()
5905 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5906 RTSTR_MAX,
5907 it->first.c_str(),
5908 RTSTR_MAX,
5909 NULL)
5910 )
5911 {
5912 propMap.insert(*it);
5913 }
5914
5915 it++;
5916 }
5917
5918 alock.release();
5919
5920 /*
5921 * And build up the arrays for returning the property information.
5922 */
5923 size_t cEntries = propMap.size();
5924 SafeArray<BSTR> names(cEntries);
5925 SafeArray<BSTR> values(cEntries);
5926 SafeArray<LONG64> timestamps(cEntries);
5927 SafeArray<BSTR> flags(cEntries);
5928 size_t iProp = 0;
5929
5930 it = propMap.begin();
5931 while (it != propMap.end())
5932 {
5933 char szFlags[MAX_FLAGS_LEN + 1];
5934 it->first.cloneTo(&names[iProp]);
5935 it->second.strValue.cloneTo(&values[iProp]);
5936 timestamps[iProp] = it->second.mTimestamp;
5937 writeFlags(it->second.mFlags, szFlags);
5938 Bstr(szFlags).cloneTo(&flags[iProp++]);
5939 it++;
5940 }
5941 names.detachTo(ComSafeArrayOutArg(aNames));
5942 values.detachTo(ComSafeArrayOutArg(aValues));
5943 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
5944 flags.detachTo(ComSafeArrayOutArg(aFlags));
5945 return S_OK;
5946}
5947
5948/**
5949 * Enumerate the properties managed by a VM.
5950 * @returns E_ACCESSDENIED if the VM process is not available or not
5951 * currently handling queries and the setting should then be done in
5952 * VBoxSVC.
5953 */
5954HRESULT Machine::enumerateGuestPropertiesOnVM
5955 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5956 ComSafeArrayOut(BSTR, aValues),
5957 ComSafeArrayOut(LONG64, aTimestamps),
5958 ComSafeArrayOut(BSTR, aFlags))
5959{
5960 HRESULT rc;
5961 ComPtr<IInternalSessionControl> directControl;
5962 directControl = mData->mSession.mDirectControl;
5963
5964 if (!directControl)
5965 rc = E_ACCESSDENIED;
5966 else
5967 rc = directControl->EnumerateGuestProperties
5968 (aPatterns, ComSafeArrayOutArg(aNames),
5969 ComSafeArrayOutArg(aValues),
5970 ComSafeArrayOutArg(aTimestamps),
5971 ComSafeArrayOutArg(aFlags));
5972 return rc;
5973}
5974#endif // VBOX_WITH_GUEST_PROPS
5975
5976STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
5977 ComSafeArrayOut(BSTR, aNames),
5978 ComSafeArrayOut(BSTR, aValues),
5979 ComSafeArrayOut(LONG64, aTimestamps),
5980 ComSafeArrayOut(BSTR, aFlags))
5981{
5982#ifndef VBOX_WITH_GUEST_PROPS
5983 ReturnComNotImplemented();
5984#else // VBOX_WITH_GUEST_PROPS
5985 CheckComArgMaybeNull(aPatterns);
5986 CheckComArgOutSafeArrayPointerValid(aNames);
5987 CheckComArgOutSafeArrayPointerValid(aValues);
5988 CheckComArgOutSafeArrayPointerValid(aTimestamps);
5989 CheckComArgOutSafeArrayPointerValid(aFlags);
5990
5991 AutoCaller autoCaller(this);
5992 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5993
5994 HRESULT rc = enumerateGuestPropertiesOnVM
5995 (aPatterns, ComSafeArrayOutArg(aNames),
5996 ComSafeArrayOutArg(aValues),
5997 ComSafeArrayOutArg(aTimestamps),
5998 ComSafeArrayOutArg(aFlags));
5999 if (rc == E_ACCESSDENIED)
6000 /* The VM is not running or the service is not (yet) accessible */
6001 rc = enumerateGuestPropertiesInService
6002 (aPatterns, ComSafeArrayOutArg(aNames),
6003 ComSafeArrayOutArg(aValues),
6004 ComSafeArrayOutArg(aTimestamps),
6005 ComSafeArrayOutArg(aFlags));
6006 return rc;
6007#endif // VBOX_WITH_GUEST_PROPS
6008}
6009
6010STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
6011 ComSafeArrayOut(IMediumAttachment*, aAttachments))
6012{
6013 MediaData::AttachmentList atts;
6014
6015 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
6016 if (FAILED(rc)) return rc;
6017
6018 SafeIfaceArray<IMediumAttachment> attachments(atts);
6019 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
6020
6021 return S_OK;
6022}
6023
6024STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
6025 LONG aControllerPort,
6026 LONG aDevice,
6027 IMediumAttachment **aAttachment)
6028{
6029 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
6030 aControllerName, aControllerPort, aDevice));
6031
6032 CheckComArgStrNotEmptyOrNull(aControllerName);
6033 CheckComArgOutPointerValid(aAttachment);
6034
6035 AutoCaller autoCaller(this);
6036 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6037
6038 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6039
6040 *aAttachment = NULL;
6041
6042 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
6043 aControllerName,
6044 aControllerPort,
6045 aDevice);
6046 if (pAttach.isNull())
6047 return setError(VBOX_E_OBJECT_NOT_FOUND,
6048 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
6049 aDevice, aControllerPort, aControllerName);
6050
6051 pAttach.queryInterfaceTo(aAttachment);
6052
6053 return S_OK;
6054}
6055
6056STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
6057 StorageBus_T aConnectionType,
6058 IStorageController **controller)
6059{
6060 CheckComArgStrNotEmptyOrNull(aName);
6061
6062 if ( (aConnectionType <= StorageBus_Null)
6063 || (aConnectionType > StorageBus_SAS))
6064 return setError(E_INVALIDARG,
6065 tr("Invalid connection type: %d"),
6066 aConnectionType);
6067
6068 AutoCaller autoCaller(this);
6069 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6070
6071 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6072
6073 HRESULT rc = checkStateDependency(MutableStateDep);
6074 if (FAILED(rc)) return rc;
6075
6076 /* try to find one with the name first. */
6077 ComObjPtr<StorageController> ctrl;
6078
6079 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
6080 if (SUCCEEDED(rc))
6081 return setError(VBOX_E_OBJECT_IN_USE,
6082 tr("Storage controller named '%ls' already exists"),
6083 aName);
6084
6085 ctrl.createObject();
6086
6087 /* get a new instance number for the storage controller */
6088 ULONG ulInstance = 0;
6089 bool fBootable = true;
6090 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6091 it != mStorageControllers->end();
6092 ++it)
6093 {
6094 if ((*it)->getStorageBus() == aConnectionType)
6095 {
6096 ULONG ulCurInst = (*it)->getInstance();
6097
6098 if (ulCurInst >= ulInstance)
6099 ulInstance = ulCurInst + 1;
6100
6101 /* Only one controller of each type can be marked as bootable. */
6102 if ((*it)->getBootable())
6103 fBootable = false;
6104 }
6105 }
6106
6107 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6108 if (FAILED(rc)) return rc;
6109
6110 setModified(IsModified_Storage);
6111 mStorageControllers.backup();
6112 mStorageControllers->push_back(ctrl);
6113
6114 ctrl.queryInterfaceTo(controller);
6115
6116 /* inform the direct session if any */
6117 alock.release();
6118 onStorageControllerChange();
6119
6120 return S_OK;
6121}
6122
6123STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6124 IStorageController **aStorageController)
6125{
6126 CheckComArgStrNotEmptyOrNull(aName);
6127
6128 AutoCaller autoCaller(this);
6129 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6130
6131 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6132
6133 ComObjPtr<StorageController> ctrl;
6134
6135 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6136 if (SUCCEEDED(rc))
6137 ctrl.queryInterfaceTo(aStorageController);
6138
6139 return rc;
6140}
6141
6142STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6143 IStorageController **aStorageController)
6144{
6145 AutoCaller autoCaller(this);
6146 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6147
6148 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6149
6150 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6151 it != mStorageControllers->end();
6152 ++it)
6153 {
6154 if ((*it)->getInstance() == aInstance)
6155 {
6156 (*it).queryInterfaceTo(aStorageController);
6157 return S_OK;
6158 }
6159 }
6160
6161 return setError(VBOX_E_OBJECT_NOT_FOUND,
6162 tr("Could not find a storage controller with instance number '%lu'"),
6163 aInstance);
6164}
6165
6166STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6167{
6168 AutoCaller autoCaller(this);
6169 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6170
6171 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6172
6173 HRESULT rc = checkStateDependency(MutableStateDep);
6174 if (FAILED(rc)) return rc;
6175
6176 ComObjPtr<StorageController> ctrl;
6177
6178 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6179 if (SUCCEEDED(rc))
6180 {
6181 /* Ensure that only one controller of each type is marked as bootable. */
6182 if (fBootable == TRUE)
6183 {
6184 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6185 it != mStorageControllers->end();
6186 ++it)
6187 {
6188 ComObjPtr<StorageController> aCtrl = (*it);
6189
6190 if ( (aCtrl->getName() != Utf8Str(aName))
6191 && aCtrl->getBootable() == TRUE
6192 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6193 && aCtrl->getControllerType() == ctrl->getControllerType())
6194 {
6195 aCtrl->setBootable(FALSE);
6196 break;
6197 }
6198 }
6199 }
6200
6201 if (SUCCEEDED(rc))
6202 {
6203 ctrl->setBootable(fBootable);
6204 setModified(IsModified_Storage);
6205 }
6206 }
6207
6208 if (SUCCEEDED(rc))
6209 {
6210 /* inform the direct session if any */
6211 alock.release();
6212 onStorageControllerChange();
6213 }
6214
6215 return rc;
6216}
6217
6218STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6219{
6220 CheckComArgStrNotEmptyOrNull(aName);
6221
6222 AutoCaller autoCaller(this);
6223 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6224
6225 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6226
6227 HRESULT rc = checkStateDependency(MutableStateDep);
6228 if (FAILED(rc)) return rc;
6229
6230 ComObjPtr<StorageController> ctrl;
6231 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6232 if (FAILED(rc)) return rc;
6233
6234 {
6235 /* find all attached devices to the appropriate storage controller and detach them all */
6236 // make a temporary list because detachDevice invalidates iterators into
6237 // mMediaData->mAttachments
6238 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6239
6240 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6241 it != llAttachments2.end();
6242 ++it)
6243 {
6244 MediumAttachment *pAttachTemp = *it;
6245
6246 AutoCaller localAutoCaller(pAttachTemp);
6247 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6248
6249 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6250
6251 if (pAttachTemp->getControllerName() == aName)
6252 {
6253 rc = detachDevice(pAttachTemp, alock, NULL);
6254 if (FAILED(rc)) return rc;
6255 }
6256 }
6257 }
6258
6259 /* We can remove it now. */
6260 setModified(IsModified_Storage);
6261 mStorageControllers.backup();
6262
6263 ctrl->unshare();
6264
6265 mStorageControllers->remove(ctrl);
6266
6267 /* inform the direct session if any */
6268 alock.release();
6269 onStorageControllerChange();
6270
6271 return S_OK;
6272}
6273
6274STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6275 ULONG *puOriginX,
6276 ULONG *puOriginY,
6277 ULONG *puWidth,
6278 ULONG *puHeight,
6279 BOOL *pfEnabled)
6280{
6281 LogFlowThisFunc(("\n"));
6282
6283 CheckComArgNotNull(puOriginX);
6284 CheckComArgNotNull(puOriginY);
6285 CheckComArgNotNull(puWidth);
6286 CheckComArgNotNull(puHeight);
6287 CheckComArgNotNull(pfEnabled);
6288
6289 uint32_t u32OriginX= 0;
6290 uint32_t u32OriginY= 0;
6291 uint32_t u32Width = 0;
6292 uint32_t u32Height = 0;
6293 uint16_t u16Flags = 0;
6294
6295 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6296 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6297 if (RT_FAILURE(vrc))
6298 {
6299#ifdef RT_OS_WINDOWS
6300 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6301 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6302 * So just assign fEnable to TRUE again.
6303 * The right fix would be to change GUI API wrappers to make sure that parameters
6304 * are changed only if API succeeds.
6305 */
6306 *pfEnabled = TRUE;
6307#endif
6308 return setError(VBOX_E_IPRT_ERROR,
6309 tr("Saved guest size is not available (%Rrc)"),
6310 vrc);
6311 }
6312
6313 *puOriginX = u32OriginX;
6314 *puOriginY = u32OriginY;
6315 *puWidth = u32Width;
6316 *puHeight = u32Height;
6317 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6318
6319 return S_OK;
6320}
6321
6322STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6323{
6324 LogFlowThisFunc(("\n"));
6325
6326 CheckComArgNotNull(aSize);
6327 CheckComArgNotNull(aWidth);
6328 CheckComArgNotNull(aHeight);
6329
6330 if (aScreenId != 0)
6331 return E_NOTIMPL;
6332
6333 AutoCaller autoCaller(this);
6334 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6335
6336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6337
6338 uint8_t *pu8Data = NULL;
6339 uint32_t cbData = 0;
6340 uint32_t u32Width = 0;
6341 uint32_t u32Height = 0;
6342
6343 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6344
6345 if (RT_FAILURE(vrc))
6346 return setError(VBOX_E_IPRT_ERROR,
6347 tr("Saved screenshot data is not available (%Rrc)"),
6348 vrc);
6349
6350 *aSize = cbData;
6351 *aWidth = u32Width;
6352 *aHeight = u32Height;
6353
6354 freeSavedDisplayScreenshot(pu8Data);
6355
6356 return S_OK;
6357}
6358
6359STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6360{
6361 LogFlowThisFunc(("\n"));
6362
6363 CheckComArgNotNull(aWidth);
6364 CheckComArgNotNull(aHeight);
6365 CheckComArgOutSafeArrayPointerValid(aData);
6366
6367 if (aScreenId != 0)
6368 return E_NOTIMPL;
6369
6370 AutoCaller autoCaller(this);
6371 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6372
6373 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6374
6375 uint8_t *pu8Data = NULL;
6376 uint32_t cbData = 0;
6377 uint32_t u32Width = 0;
6378 uint32_t u32Height = 0;
6379
6380 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6381
6382 if (RT_FAILURE(vrc))
6383 return setError(VBOX_E_IPRT_ERROR,
6384 tr("Saved screenshot data is not available (%Rrc)"),
6385 vrc);
6386
6387 *aWidth = u32Width;
6388 *aHeight = u32Height;
6389
6390 com::SafeArray<BYTE> bitmap(cbData);
6391 /* Convert pixels to format expected by the API caller. */
6392 if (aBGR)
6393 {
6394 /* [0] B, [1] G, [2] R, [3] A. */
6395 for (unsigned i = 0; i < cbData; i += 4)
6396 {
6397 bitmap[i] = pu8Data[i];
6398 bitmap[i + 1] = pu8Data[i + 1];
6399 bitmap[i + 2] = pu8Data[i + 2];
6400 bitmap[i + 3] = 0xff;
6401 }
6402 }
6403 else
6404 {
6405 /* [0] R, [1] G, [2] B, [3] A. */
6406 for (unsigned i = 0; i < cbData; i += 4)
6407 {
6408 bitmap[i] = pu8Data[i + 2];
6409 bitmap[i + 1] = pu8Data[i + 1];
6410 bitmap[i + 2] = pu8Data[i];
6411 bitmap[i + 3] = 0xff;
6412 }
6413 }
6414 bitmap.detachTo(ComSafeArrayOutArg(aData));
6415
6416 freeSavedDisplayScreenshot(pu8Data);
6417
6418 return S_OK;
6419}
6420
6421
6422STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6423{
6424 LogFlowThisFunc(("\n"));
6425
6426 CheckComArgNotNull(aWidth);
6427 CheckComArgNotNull(aHeight);
6428 CheckComArgOutSafeArrayPointerValid(aData);
6429
6430 if (aScreenId != 0)
6431 return E_NOTIMPL;
6432
6433 AutoCaller autoCaller(this);
6434 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6435
6436 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6437
6438 uint8_t *pu8Data = NULL;
6439 uint32_t cbData = 0;
6440 uint32_t u32Width = 0;
6441 uint32_t u32Height = 0;
6442
6443 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6444
6445 if (RT_FAILURE(vrc))
6446 return setError(VBOX_E_IPRT_ERROR,
6447 tr("Saved screenshot data is not available (%Rrc)"),
6448 vrc);
6449
6450 *aWidth = u32Width;
6451 *aHeight = u32Height;
6452
6453 HRESULT rc = S_OK;
6454 uint8_t *pu8PNG = NULL;
6455 uint32_t cbPNG = 0;
6456 uint32_t cxPNG = 0;
6457 uint32_t cyPNG = 0;
6458
6459 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6460
6461 if (RT_SUCCESS(vrc))
6462 {
6463 com::SafeArray<BYTE> screenData(cbPNG);
6464 screenData.initFrom(pu8PNG, cbPNG);
6465 if (pu8PNG)
6466 RTMemFree(pu8PNG);
6467 screenData.detachTo(ComSafeArrayOutArg(aData));
6468 }
6469 else
6470 {
6471 if (pu8PNG)
6472 RTMemFree(pu8PNG);
6473 return setError(VBOX_E_IPRT_ERROR,
6474 tr("Could not convert screenshot to PNG (%Rrc)"),
6475 vrc);
6476 }
6477
6478 freeSavedDisplayScreenshot(pu8Data);
6479
6480 return rc;
6481}
6482
6483STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6484{
6485 LogFlowThisFunc(("\n"));
6486
6487 CheckComArgNotNull(aSize);
6488 CheckComArgNotNull(aWidth);
6489 CheckComArgNotNull(aHeight);
6490
6491 if (aScreenId != 0)
6492 return E_NOTIMPL;
6493
6494 AutoCaller autoCaller(this);
6495 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6496
6497 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6498
6499 uint8_t *pu8Data = NULL;
6500 uint32_t cbData = 0;
6501 uint32_t u32Width = 0;
6502 uint32_t u32Height = 0;
6503
6504 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6505
6506 if (RT_FAILURE(vrc))
6507 return setError(VBOX_E_IPRT_ERROR,
6508 tr("Saved screenshot data is not available (%Rrc)"),
6509 vrc);
6510
6511 *aSize = cbData;
6512 *aWidth = u32Width;
6513 *aHeight = u32Height;
6514
6515 freeSavedDisplayScreenshot(pu8Data);
6516
6517 return S_OK;
6518}
6519
6520STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6521{
6522 LogFlowThisFunc(("\n"));
6523
6524 CheckComArgNotNull(aWidth);
6525 CheckComArgNotNull(aHeight);
6526 CheckComArgOutSafeArrayPointerValid(aData);
6527
6528 if (aScreenId != 0)
6529 return E_NOTIMPL;
6530
6531 AutoCaller autoCaller(this);
6532 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6533
6534 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6535
6536 uint8_t *pu8Data = NULL;
6537 uint32_t cbData = 0;
6538 uint32_t u32Width = 0;
6539 uint32_t u32Height = 0;
6540
6541 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6542
6543 if (RT_FAILURE(vrc))
6544 return setError(VBOX_E_IPRT_ERROR,
6545 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6546 vrc);
6547
6548 *aWidth = u32Width;
6549 *aHeight = u32Height;
6550
6551 com::SafeArray<BYTE> png(cbData);
6552 png.initFrom(pu8Data, cbData);
6553 png.detachTo(ComSafeArrayOutArg(aData));
6554
6555 freeSavedDisplayScreenshot(pu8Data);
6556
6557 return S_OK;
6558}
6559
6560STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6561{
6562 HRESULT rc = S_OK;
6563 LogFlowThisFunc(("\n"));
6564
6565 AutoCaller autoCaller(this);
6566 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6567
6568 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6569
6570 if (!mHWData->mCPUHotPlugEnabled)
6571 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6572
6573 if (aCpu >= mHWData->mCPUCount)
6574 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6575
6576 if (mHWData->mCPUAttached[aCpu])
6577 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6578
6579 alock.release();
6580 rc = onCPUChange(aCpu, false);
6581 alock.acquire();
6582 if (FAILED(rc)) return rc;
6583
6584 setModified(IsModified_MachineData);
6585 mHWData.backup();
6586 mHWData->mCPUAttached[aCpu] = true;
6587
6588 /* Save settings if online */
6589 if (Global::IsOnline(mData->mMachineState))
6590 saveSettings(NULL);
6591
6592 return S_OK;
6593}
6594
6595STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6596{
6597 HRESULT rc = S_OK;
6598 LogFlowThisFunc(("\n"));
6599
6600 AutoCaller autoCaller(this);
6601 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6602
6603 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6604
6605 if (!mHWData->mCPUHotPlugEnabled)
6606 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6607
6608 if (aCpu >= SchemaDefs::MaxCPUCount)
6609 return setError(E_INVALIDARG,
6610 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6611 SchemaDefs::MaxCPUCount);
6612
6613 if (!mHWData->mCPUAttached[aCpu])
6614 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6615
6616 /* CPU 0 can't be detached */
6617 if (aCpu == 0)
6618 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6619
6620 alock.release();
6621 rc = onCPUChange(aCpu, true);
6622 alock.acquire();
6623 if (FAILED(rc)) return rc;
6624
6625 setModified(IsModified_MachineData);
6626 mHWData.backup();
6627 mHWData->mCPUAttached[aCpu] = false;
6628
6629 /* Save settings if online */
6630 if (Global::IsOnline(mData->mMachineState))
6631 saveSettings(NULL);
6632
6633 return S_OK;
6634}
6635
6636STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6637{
6638 LogFlowThisFunc(("\n"));
6639
6640 CheckComArgNotNull(aCpuAttached);
6641
6642 *aCpuAttached = false;
6643
6644 AutoCaller autoCaller(this);
6645 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6646
6647 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6648
6649 /* If hotplug is enabled the CPU is always enabled. */
6650 if (!mHWData->mCPUHotPlugEnabled)
6651 {
6652 if (aCpu < mHWData->mCPUCount)
6653 *aCpuAttached = true;
6654 }
6655 else
6656 {
6657 if (aCpu < SchemaDefs::MaxCPUCount)
6658 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6659 }
6660
6661 return S_OK;
6662}
6663
6664STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6665{
6666 CheckComArgOutPointerValid(aName);
6667
6668 AutoCaller autoCaller(this);
6669 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6670
6671 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6672
6673 Utf8Str log = queryLogFilename(aIdx);
6674 if (!RTFileExists(log.c_str()))
6675 log.setNull();
6676 log.cloneTo(aName);
6677
6678 return S_OK;
6679}
6680
6681STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6682{
6683 LogFlowThisFunc(("\n"));
6684 CheckComArgOutSafeArrayPointerValid(aData);
6685 if (aSize < 0)
6686 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6687
6688 AutoCaller autoCaller(this);
6689 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6690
6691 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6692
6693 HRESULT rc = S_OK;
6694 Utf8Str log = queryLogFilename(aIdx);
6695
6696 /* do not unnecessarily hold the lock while doing something which does
6697 * not need the lock and potentially takes a long time. */
6698 alock.release();
6699
6700 /* Limit the chunk size to 32K for now, as that gives better performance
6701 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6702 * One byte expands to approx. 25 bytes of breathtaking XML. */
6703 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6704 com::SafeArray<BYTE> logData(cbData);
6705
6706 RTFILE LogFile;
6707 int vrc = RTFileOpen(&LogFile, log.c_str(),
6708 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6709 if (RT_SUCCESS(vrc))
6710 {
6711 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6712 if (RT_SUCCESS(vrc))
6713 logData.resize(cbData);
6714 else
6715 rc = setError(VBOX_E_IPRT_ERROR,
6716 tr("Could not read log file '%s' (%Rrc)"),
6717 log.c_str(), vrc);
6718 RTFileClose(LogFile);
6719 }
6720 else
6721 rc = setError(VBOX_E_IPRT_ERROR,
6722 tr("Could not open log file '%s' (%Rrc)"),
6723 log.c_str(), vrc);
6724
6725 if (FAILED(rc))
6726 logData.resize(0);
6727 logData.detachTo(ComSafeArrayOutArg(aData));
6728
6729 return rc;
6730}
6731
6732
6733/**
6734 * Currently this method doesn't attach device to the running VM,
6735 * just makes sure it's plugged on next VM start.
6736 */
6737STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6738{
6739 AutoCaller autoCaller(this);
6740 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6741
6742 // lock scope
6743 {
6744 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6745
6746 HRESULT rc = checkStateDependency(MutableStateDep);
6747 if (FAILED(rc)) return rc;
6748
6749 ChipsetType_T aChipset = ChipsetType_PIIX3;
6750 COMGETTER(ChipsetType)(&aChipset);
6751
6752 if (aChipset != ChipsetType_ICH9)
6753 {
6754 return setError(E_INVALIDARG,
6755 tr("Host PCI attachment only supported with ICH9 chipset"));
6756 }
6757
6758 // check if device with this host PCI address already attached
6759 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6760 it != mHWData->mPCIDeviceAssignments.end();
6761 ++it)
6762 {
6763 LONG iHostAddress = -1;
6764 ComPtr<PCIDeviceAttachment> pAttach;
6765 pAttach = *it;
6766 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6767 if (iHostAddress == hostAddress)
6768 return setError(E_INVALIDARG,
6769 tr("Device with host PCI address already attached to this VM"));
6770 }
6771
6772 ComObjPtr<PCIDeviceAttachment> pda;
6773 char name[32];
6774
6775 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6776 Bstr bname(name);
6777 pda.createObject();
6778 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
6779 setModified(IsModified_MachineData);
6780 mHWData.backup();
6781 mHWData->mPCIDeviceAssignments.push_back(pda);
6782 }
6783
6784 return S_OK;
6785}
6786
6787/**
6788 * Currently this method doesn't detach device from the running VM,
6789 * just makes sure it's not plugged on next VM start.
6790 */
6791STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
6792{
6793 AutoCaller autoCaller(this);
6794 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6795
6796 ComObjPtr<PCIDeviceAttachment> pAttach;
6797 bool fRemoved = false;
6798 HRESULT rc;
6799
6800 // lock scope
6801 {
6802 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6803
6804 rc = checkStateDependency(MutableStateDep);
6805 if (FAILED(rc)) return rc;
6806
6807 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6808 it != mHWData->mPCIDeviceAssignments.end();
6809 ++it)
6810 {
6811 LONG iHostAddress = -1;
6812 pAttach = *it;
6813 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6814 if (iHostAddress != -1 && iHostAddress == hostAddress)
6815 {
6816 setModified(IsModified_MachineData);
6817 mHWData.backup();
6818 mHWData->mPCIDeviceAssignments.remove(pAttach);
6819 fRemoved = true;
6820 break;
6821 }
6822 }
6823 }
6824
6825
6826 /* Fire event outside of the lock */
6827 if (fRemoved)
6828 {
6829 Assert(!pAttach.isNull());
6830 ComPtr<IEventSource> es;
6831 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6832 Assert(SUCCEEDED(rc));
6833 Bstr mid;
6834 rc = this->COMGETTER(Id)(mid.asOutParam());
6835 Assert(SUCCEEDED(rc));
6836 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6837 }
6838
6839 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6840 tr("No host PCI device %08x attached"),
6841 hostAddress
6842 );
6843}
6844
6845STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
6846{
6847 CheckComArgOutSafeArrayPointerValid(aAssignments);
6848
6849 AutoCaller autoCaller(this);
6850 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6851
6852 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6853
6854 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
6855 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
6856
6857 return S_OK;
6858}
6859
6860STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
6861{
6862 CheckComArgOutPointerValid(aBandwidthControl);
6863
6864 AutoCaller autoCaller(this);
6865 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6866
6867 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
6868
6869 return S_OK;
6870}
6871
6872STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
6873{
6874 CheckComArgOutPointerValid(pfEnabled);
6875 AutoCaller autoCaller(this);
6876 HRESULT hrc = autoCaller.rc();
6877 if (SUCCEEDED(hrc))
6878 {
6879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6880 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
6881 }
6882 return hrc;
6883}
6884
6885STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
6886{
6887 AutoCaller autoCaller(this);
6888 HRESULT hrc = autoCaller.rc();
6889 if (SUCCEEDED(hrc))
6890 {
6891 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6892 hrc = checkStateDependency(MutableStateDep);
6893 if (SUCCEEDED(hrc))
6894 {
6895 hrc = mHWData.backupEx();
6896 if (SUCCEEDED(hrc))
6897 {
6898 setModified(IsModified_MachineData);
6899 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
6900 }
6901 }
6902 }
6903 return hrc;
6904}
6905
6906STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
6907{
6908 CheckComArgOutPointerValid(pbstrConfig);
6909 AutoCaller autoCaller(this);
6910 HRESULT hrc = autoCaller.rc();
6911 if (SUCCEEDED(hrc))
6912 {
6913 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6914 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
6915 }
6916 return hrc;
6917}
6918
6919STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
6920{
6921 CheckComArgStr(bstrConfig);
6922 AutoCaller autoCaller(this);
6923 HRESULT hrc = autoCaller.rc();
6924 if (SUCCEEDED(hrc))
6925 {
6926 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6927 hrc = checkStateDependency(MutableStateDep);
6928 if (SUCCEEDED(hrc))
6929 {
6930 hrc = mHWData.backupEx();
6931 if (SUCCEEDED(hrc))
6932 {
6933 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
6934 if (SUCCEEDED(hrc))
6935 setModified(IsModified_MachineData);
6936 }
6937 }
6938 }
6939 return hrc;
6940
6941}
6942
6943STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
6944{
6945 CheckComArgOutPointerValid(pfAllow);
6946 AutoCaller autoCaller(this);
6947 HRESULT hrc = autoCaller.rc();
6948 if (SUCCEEDED(hrc))
6949 {
6950 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6951 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
6952 }
6953 return hrc;
6954}
6955
6956STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
6957{
6958 AutoCaller autoCaller(this);
6959 HRESULT hrc = autoCaller.rc();
6960 if (SUCCEEDED(hrc))
6961 {
6962 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6963 hrc = checkStateDependency(MutableStateDep);
6964 if (SUCCEEDED(hrc))
6965 {
6966 hrc = mHWData.backupEx();
6967 if (SUCCEEDED(hrc))
6968 {
6969 setModified(IsModified_MachineData);
6970 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
6971 }
6972 }
6973 }
6974 return hrc;
6975}
6976
6977STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
6978{
6979 CheckComArgOutPointerValid(pfEnabled);
6980 AutoCaller autoCaller(this);
6981 HRESULT hrc = autoCaller.rc();
6982 if (SUCCEEDED(hrc))
6983 {
6984 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6985 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
6986 }
6987 return hrc;
6988}
6989
6990STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
6991{
6992 AutoCaller autoCaller(this);
6993 HRESULT hrc = autoCaller.rc();
6994 if (SUCCEEDED(hrc))
6995 {
6996 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6997 hrc = checkStateDependency(MutableStateDep);
6998 if ( SUCCEEDED(hrc)
6999 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
7000 {
7001 AutostartDb *autostartDb = mParent->getAutostartDb();
7002 int vrc;
7003
7004 if (fEnabled)
7005 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7006 else
7007 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7008
7009 if (RT_SUCCESS(vrc))
7010 {
7011 hrc = mHWData.backupEx();
7012 if (SUCCEEDED(hrc))
7013 {
7014 setModified(IsModified_MachineData);
7015 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
7016 }
7017 }
7018 else if (vrc == VERR_NOT_SUPPORTED)
7019 hrc = setError(VBOX_E_NOT_SUPPORTED,
7020 tr("The VM autostart feature is not supported on this platform"));
7021 else if (vrc == VERR_PATH_NOT_FOUND)
7022 hrc = setError(E_FAIL,
7023 tr("The path to the autostart database is not set"));
7024 else
7025 hrc = setError(E_UNEXPECTED,
7026 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7027 fEnabled ? "Adding" : "Removing",
7028 mUserData->s.strName.c_str(), vrc);
7029 }
7030 }
7031 return hrc;
7032}
7033
7034STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
7035{
7036 CheckComArgOutPointerValid(puDelay);
7037 AutoCaller autoCaller(this);
7038 HRESULT hrc = autoCaller.rc();
7039 if (SUCCEEDED(hrc))
7040 {
7041 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7042 *puDelay = mHWData->mAutostart.uAutostartDelay;
7043 }
7044 return hrc;
7045}
7046
7047STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
7048{
7049 AutoCaller autoCaller(this);
7050 HRESULT hrc = autoCaller.rc();
7051 if (SUCCEEDED(hrc))
7052 {
7053 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7054 hrc = checkStateDependency(MutableStateDep);
7055 if (SUCCEEDED(hrc))
7056 {
7057 hrc = mHWData.backupEx();
7058 if (SUCCEEDED(hrc))
7059 {
7060 setModified(IsModified_MachineData);
7061 mHWData->mAutostart.uAutostartDelay = uDelay;
7062 }
7063 }
7064 }
7065 return hrc;
7066}
7067
7068STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
7069{
7070 CheckComArgOutPointerValid(penmAutostopType);
7071 AutoCaller autoCaller(this);
7072 HRESULT hrc = autoCaller.rc();
7073 if (SUCCEEDED(hrc))
7074 {
7075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7076 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
7077 }
7078 return hrc;
7079}
7080
7081STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
7082{
7083 AutoCaller autoCaller(this);
7084 HRESULT hrc = autoCaller.rc();
7085 if (SUCCEEDED(hrc))
7086 {
7087 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7088 hrc = checkStateDependency(MutableStateDep);
7089 if ( SUCCEEDED(hrc)
7090 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
7091 {
7092 AutostartDb *autostartDb = mParent->getAutostartDb();
7093 int vrc;
7094
7095 if (enmAutostopType != AutostopType_Disabled)
7096 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7097 else
7098 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7099
7100 if (RT_SUCCESS(vrc))
7101 {
7102 hrc = mHWData.backupEx();
7103 if (SUCCEEDED(hrc))
7104 {
7105 setModified(IsModified_MachineData);
7106 mHWData->mAutostart.enmAutostopType = enmAutostopType;
7107 }
7108 }
7109 else if (vrc == VERR_NOT_SUPPORTED)
7110 hrc = setError(VBOX_E_NOT_SUPPORTED,
7111 tr("The VM autostop feature is not supported on this platform"));
7112 else if (vrc == VERR_PATH_NOT_FOUND)
7113 hrc = setError(E_FAIL,
7114 tr("The path to the autostart database is not set"));
7115 else
7116 hrc = setError(E_UNEXPECTED,
7117 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7118 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7119 mUserData->s.strName.c_str(), vrc);
7120 }
7121 }
7122 return hrc;
7123}
7124
7125STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7126{
7127 CheckComArgOutPointerValid(aDefaultFrontend);
7128 AutoCaller autoCaller(this);
7129 HRESULT hrc = autoCaller.rc();
7130 if (SUCCEEDED(hrc))
7131 {
7132 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7133 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7134 }
7135 return hrc;
7136}
7137
7138STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7139{
7140 CheckComArgStr(aDefaultFrontend);
7141 AutoCaller autoCaller(this);
7142 HRESULT hrc = autoCaller.rc();
7143 if (SUCCEEDED(hrc))
7144 {
7145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7146 hrc = checkStateDependency(MutableOrSavedStateDep);
7147 if (SUCCEEDED(hrc))
7148 {
7149 hrc = mHWData.backupEx();
7150 if (SUCCEEDED(hrc))
7151 {
7152 setModified(IsModified_MachineData);
7153 mHWData->mDefaultFrontend = aDefaultFrontend;
7154 }
7155 }
7156 }
7157 return hrc;
7158}
7159
7160
7161STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7162{
7163 LogFlowFuncEnter();
7164
7165 CheckComArgNotNull(pTarget);
7166 CheckComArgOutPointerValid(pProgress);
7167
7168 /* Convert the options. */
7169 RTCList<CloneOptions_T> optList;
7170 if (options != NULL)
7171 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7172
7173 if (optList.contains(CloneOptions_Link))
7174 {
7175 if (!isSnapshotMachine())
7176 return setError(E_INVALIDARG,
7177 tr("Linked clone can only be created from a snapshot"));
7178 if (mode != CloneMode_MachineState)
7179 return setError(E_INVALIDARG,
7180 tr("Linked clone can only be created for a single machine state"));
7181 }
7182 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7183
7184 AutoCaller autoCaller(this);
7185 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7186
7187
7188 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7189
7190 HRESULT rc = pWorker->start(pProgress);
7191
7192 LogFlowFuncLeave();
7193
7194 return rc;
7195}
7196
7197// public methods for internal purposes
7198/////////////////////////////////////////////////////////////////////////////
7199
7200/**
7201 * Adds the given IsModified_* flag to the dirty flags of the machine.
7202 * This must be called either during loadSettings or under the machine write lock.
7203 * @param fl
7204 */
7205void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7206{
7207 mData->flModifications |= fl;
7208 if (fAllowStateModification && isStateModificationAllowed())
7209 mData->mCurrentStateModified = true;
7210}
7211
7212/**
7213 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7214 * care of the write locking.
7215 *
7216 * @param fModifications The flag to add.
7217 */
7218void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7219{
7220 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7221 setModified(fModification, fAllowStateModification);
7222}
7223
7224/**
7225 * Saves the registry entry of this machine to the given configuration node.
7226 *
7227 * @param aEntryNode Node to save the registry entry to.
7228 *
7229 * @note locks this object for reading.
7230 */
7231HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7232{
7233 AutoLimitedCaller autoCaller(this);
7234 AssertComRCReturnRC(autoCaller.rc());
7235
7236 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7237
7238 data.uuid = mData->mUuid;
7239 data.strSettingsFile = mData->m_strConfigFile;
7240
7241 return S_OK;
7242}
7243
7244/**
7245 * Calculates the absolute path of the given path taking the directory of the
7246 * machine settings file as the current directory.
7247 *
7248 * @param aPath Path to calculate the absolute path for.
7249 * @param aResult Where to put the result (used only on success, can be the
7250 * same Utf8Str instance as passed in @a aPath).
7251 * @return IPRT result.
7252 *
7253 * @note Locks this object for reading.
7254 */
7255int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7256{
7257 AutoCaller autoCaller(this);
7258 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7259
7260 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7261
7262 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7263
7264 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7265
7266 strSettingsDir.stripFilename();
7267 char folder[RTPATH_MAX];
7268 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7269 if (RT_SUCCESS(vrc))
7270 aResult = folder;
7271
7272 return vrc;
7273}
7274
7275/**
7276 * Copies strSource to strTarget, making it relative to the machine folder
7277 * if it is a subdirectory thereof, or simply copying it otherwise.
7278 *
7279 * @param strSource Path to evaluate and copy.
7280 * @param strTarget Buffer to receive target path.
7281 *
7282 * @note Locks this object for reading.
7283 */
7284void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7285 Utf8Str &strTarget)
7286{
7287 AutoCaller autoCaller(this);
7288 AssertComRCReturn(autoCaller.rc(), (void)0);
7289
7290 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7291
7292 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7293 // use strTarget as a temporary buffer to hold the machine settings dir
7294 strTarget = mData->m_strConfigFileFull;
7295 strTarget.stripFilename();
7296 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7297 {
7298 // is relative: then append what's left
7299 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7300 // for empty paths (only possible for subdirs) use "." to avoid
7301 // triggering default settings for not present config attributes.
7302 if (strTarget.isEmpty())
7303 strTarget = ".";
7304 }
7305 else
7306 // is not relative: then overwrite
7307 strTarget = strSource;
7308}
7309
7310/**
7311 * Returns the full path to the machine's log folder in the
7312 * \a aLogFolder argument.
7313 */
7314void Machine::getLogFolder(Utf8Str &aLogFolder)
7315{
7316 AutoCaller autoCaller(this);
7317 AssertComRCReturnVoid(autoCaller.rc());
7318
7319 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7320
7321 char szTmp[RTPATH_MAX];
7322 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7323 if (RT_SUCCESS(vrc))
7324 {
7325 if (szTmp[0] && !mUserData.isNull())
7326 {
7327 char szTmp2[RTPATH_MAX];
7328 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7329 if (RT_SUCCESS(vrc))
7330 aLogFolder = BstrFmt("%s%c%s",
7331 szTmp2,
7332 RTPATH_DELIMITER,
7333 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7334 }
7335 else
7336 vrc = VERR_PATH_IS_RELATIVE;
7337 }
7338
7339 if (RT_FAILURE(vrc))
7340 {
7341 // fallback if VBOX_USER_LOGHOME is not set or invalid
7342 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7343 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7344 aLogFolder.append(RTPATH_DELIMITER);
7345 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7346 }
7347}
7348
7349/**
7350 * Returns the full path to the machine's log file for an given index.
7351 */
7352Utf8Str Machine::queryLogFilename(ULONG idx)
7353{
7354 Utf8Str logFolder;
7355 getLogFolder(logFolder);
7356 Assert(logFolder.length());
7357 Utf8Str log;
7358 if (idx == 0)
7359 log = Utf8StrFmt("%s%cVBox.log",
7360 logFolder.c_str(), RTPATH_DELIMITER);
7361 else
7362 log = Utf8StrFmt("%s%cVBox.log.%d",
7363 logFolder.c_str(), RTPATH_DELIMITER, idx);
7364 return log;
7365}
7366
7367/**
7368 * Composes a unique saved state filename based on the current system time. The filename is
7369 * granular to the second so this will work so long as no more than one snapshot is taken on
7370 * a machine per second.
7371 *
7372 * Before version 4.1, we used this formula for saved state files:
7373 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7374 * which no longer works because saved state files can now be shared between the saved state of the
7375 * "saved" machine and an online snapshot, and the following would cause problems:
7376 * 1) save machine
7377 * 2) create online snapshot from that machine state --> reusing saved state file
7378 * 3) save machine again --> filename would be reused, breaking the online snapshot
7379 *
7380 * So instead we now use a timestamp.
7381 *
7382 * @param str
7383 */
7384void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7385{
7386 AutoCaller autoCaller(this);
7387 AssertComRCReturnVoid(autoCaller.rc());
7388
7389 {
7390 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7391 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7392 }
7393
7394 RTTIMESPEC ts;
7395 RTTimeNow(&ts);
7396 RTTIME time;
7397 RTTimeExplode(&time, &ts);
7398
7399 strStateFilePath += RTPATH_DELIMITER;
7400 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7401 time.i32Year, time.u8Month, time.u8MonthDay,
7402 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7403}
7404
7405/**
7406 * @note Locks this object for writing, calls the client process
7407 * (inside the lock).
7408 */
7409HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7410 const Utf8Str &strFrontend,
7411 const Utf8Str &strEnvironment,
7412 ProgressProxy *aProgress)
7413{
7414 LogFlowThisFuncEnter();
7415
7416 AssertReturn(aControl, E_FAIL);
7417 AssertReturn(aProgress, E_FAIL);
7418
7419 AutoCaller autoCaller(this);
7420 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7421
7422 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7423
7424 if (!mData->mRegistered)
7425 return setError(E_UNEXPECTED,
7426 tr("The machine '%s' is not registered"),
7427 mUserData->s.strName.c_str());
7428
7429 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7430
7431 if ( mData->mSession.mState == SessionState_Locked
7432 || mData->mSession.mState == SessionState_Spawning
7433 || mData->mSession.mState == SessionState_Unlocking)
7434 return setError(VBOX_E_INVALID_OBJECT_STATE,
7435 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7436 mUserData->s.strName.c_str());
7437
7438 /* may not be busy */
7439 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7440
7441 /* get the path to the executable */
7442 char szPath[RTPATH_MAX];
7443 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7444 size_t sz = strlen(szPath);
7445 szPath[sz++] = RTPATH_DELIMITER;
7446 szPath[sz] = 0;
7447 char *cmd = szPath + sz;
7448 sz = RTPATH_MAX - sz;
7449
7450 int vrc = VINF_SUCCESS;
7451 RTPROCESS pid = NIL_RTPROCESS;
7452
7453 RTENV env = RTENV_DEFAULT;
7454
7455 if (!strEnvironment.isEmpty())
7456 {
7457 char *newEnvStr = NULL;
7458
7459 do
7460 {
7461 /* clone the current environment */
7462 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7463 AssertRCBreakStmt(vrc2, vrc = vrc2);
7464
7465 newEnvStr = RTStrDup(strEnvironment.c_str());
7466 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7467
7468 /* put new variables to the environment
7469 * (ignore empty variable names here since RTEnv API
7470 * intentionally doesn't do that) */
7471 char *var = newEnvStr;
7472 for (char *p = newEnvStr; *p; ++p)
7473 {
7474 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7475 {
7476 *p = '\0';
7477 if (*var)
7478 {
7479 char *val = strchr(var, '=');
7480 if (val)
7481 {
7482 *val++ = '\0';
7483 vrc2 = RTEnvSetEx(env, var, val);
7484 }
7485 else
7486 vrc2 = RTEnvUnsetEx(env, var);
7487 if (RT_FAILURE(vrc2))
7488 break;
7489 }
7490 var = p + 1;
7491 }
7492 }
7493 if (RT_SUCCESS(vrc2) && *var)
7494 vrc2 = RTEnvPutEx(env, var);
7495
7496 AssertRCBreakStmt(vrc2, vrc = vrc2);
7497 }
7498 while (0);
7499
7500 if (newEnvStr != NULL)
7501 RTStrFree(newEnvStr);
7502 }
7503
7504 /* Qt is default */
7505#ifdef VBOX_WITH_QTGUI
7506 if (strFrontend == "gui" || strFrontend == "GUI/Qt" || strFrontend == "")
7507 {
7508# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7509 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7510# else
7511 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7512# endif
7513 Assert(sz >= sizeof(VirtualBox_exe));
7514 strcpy(cmd, VirtualBox_exe);
7515
7516 Utf8Str idStr = mData->mUuid.toString();
7517 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7518 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7519 }
7520#else /* !VBOX_WITH_QTGUI */
7521 if (0)
7522 ;
7523#endif /* VBOX_WITH_QTGUI */
7524
7525 else
7526
7527#ifdef VBOX_WITH_VBOXSDL
7528 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7529 {
7530 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7531 Assert(sz >= sizeof(VBoxSDL_exe));
7532 strcpy(cmd, VBoxSDL_exe);
7533
7534 Utf8Str idStr = mData->mUuid.toString();
7535 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7536 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7537 }
7538#else /* !VBOX_WITH_VBOXSDL */
7539 if (0)
7540 ;
7541#endif /* !VBOX_WITH_VBOXSDL */
7542
7543 else
7544
7545#ifdef VBOX_WITH_HEADLESS
7546 if ( strFrontend == "headless"
7547 || strFrontend == "capture"
7548 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7549 )
7550 {
7551 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7552 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7553 * and a VM works even if the server has not been installed.
7554 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7555 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7556 * differently in 4.0 and 3.x.
7557 */
7558 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7559 Assert(sz >= sizeof(VBoxHeadless_exe));
7560 strcpy(cmd, VBoxHeadless_exe);
7561
7562 Utf8Str idStr = mData->mUuid.toString();
7563 /* Leave space for "--capture" arg. */
7564 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7565 "--startvm", idStr.c_str(),
7566 "--vrde", "config",
7567 0, /* For "--capture". */
7568 0 };
7569 if (strFrontend == "capture")
7570 {
7571 unsigned pos = RT_ELEMENTS(args) - 2;
7572 args[pos] = "--capture";
7573 }
7574 vrc = RTProcCreate(szPath, args, env,
7575#ifdef RT_OS_WINDOWS
7576 RTPROC_FLAGS_NO_WINDOW
7577#else
7578 0
7579#endif
7580 , &pid);
7581 }
7582#else /* !VBOX_WITH_HEADLESS */
7583 if (0)
7584 ;
7585#endif /* !VBOX_WITH_HEADLESS */
7586 else
7587 {
7588 RTEnvDestroy(env);
7589 return setError(E_INVALIDARG,
7590 tr("Invalid frontend name: '%s'"),
7591 strFrontend.c_str());
7592 }
7593
7594 RTEnvDestroy(env);
7595
7596 if (RT_FAILURE(vrc))
7597 return setError(VBOX_E_IPRT_ERROR,
7598 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7599 mUserData->s.strName.c_str(), vrc);
7600
7601 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7602
7603 /*
7604 * Note that we don't release the lock here before calling the client,
7605 * because it doesn't need to call us back if called with a NULL argument.
7606 * Releasing the lock here is dangerous because we didn't prepare the
7607 * launch data yet, but the client we've just started may happen to be
7608 * too fast and call openSession() that will fail (because of PID, etc.),
7609 * so that the Machine will never get out of the Spawning session state.
7610 */
7611
7612 /* inform the session that it will be a remote one */
7613 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7614 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
7615 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7616
7617 if (FAILED(rc))
7618 {
7619 /* restore the session state */
7620 mData->mSession.mState = SessionState_Unlocked;
7621 /* The failure may occur w/o any error info (from RPC), so provide one */
7622 return setError(VBOX_E_VM_ERROR,
7623 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7624 }
7625
7626 /* attach launch data to the machine */
7627 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7628 mData->mSession.mRemoteControls.push_back(aControl);
7629 mData->mSession.mProgress = aProgress;
7630 mData->mSession.mPID = pid;
7631 mData->mSession.mState = SessionState_Spawning;
7632 mData->mSession.mType = strFrontend;
7633
7634 LogFlowThisFuncLeave();
7635 return S_OK;
7636}
7637
7638/**
7639 * Returns @c true if the given machine has an open direct session and returns
7640 * the session machine instance and additional session data (on some platforms)
7641 * if so.
7642 *
7643 * Note that when the method returns @c false, the arguments remain unchanged.
7644 *
7645 * @param aMachine Session machine object.
7646 * @param aControl Direct session control object (optional).
7647 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7648 *
7649 * @note locks this object for reading.
7650 */
7651#if defined(RT_OS_WINDOWS)
7652bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7653 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7654 HANDLE *aIPCSem /*= NULL*/,
7655 bool aAllowClosing /*= false*/)
7656#elif defined(RT_OS_OS2)
7657bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7658 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7659 HMTX *aIPCSem /*= NULL*/,
7660 bool aAllowClosing /*= false*/)
7661#else
7662bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7663 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7664 bool aAllowClosing /*= false*/)
7665#endif
7666{
7667 AutoLimitedCaller autoCaller(this);
7668 AssertComRCReturn(autoCaller.rc(), false);
7669
7670 /* just return false for inaccessible machines */
7671 if (autoCaller.state() != Ready)
7672 return false;
7673
7674 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7675
7676 if ( mData->mSession.mState == SessionState_Locked
7677 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7678 )
7679 {
7680 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7681
7682 aMachine = mData->mSession.mMachine;
7683
7684 if (aControl != NULL)
7685 *aControl = mData->mSession.mDirectControl;
7686
7687#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7688 /* Additional session data */
7689 if (aIPCSem != NULL)
7690 *aIPCSem = aMachine->mIPCSem;
7691#endif
7692 return true;
7693 }
7694
7695 return false;
7696}
7697
7698/**
7699 * Returns @c true if the given machine has an spawning direct session and
7700 * returns and additional session data (on some platforms) if so.
7701 *
7702 * Note that when the method returns @c false, the arguments remain unchanged.
7703 *
7704 * @param aPID PID of the spawned direct session process.
7705 *
7706 * @note locks this object for reading.
7707 */
7708#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7709bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7710#else
7711bool Machine::isSessionSpawning()
7712#endif
7713{
7714 AutoLimitedCaller autoCaller(this);
7715 AssertComRCReturn(autoCaller.rc(), false);
7716
7717 /* just return false for inaccessible machines */
7718 if (autoCaller.state() != Ready)
7719 return false;
7720
7721 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7722
7723 if (mData->mSession.mState == SessionState_Spawning)
7724 {
7725#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7726 /* Additional session data */
7727 if (aPID != NULL)
7728 {
7729 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
7730 *aPID = mData->mSession.mPID;
7731 }
7732#endif
7733 return true;
7734 }
7735
7736 return false;
7737}
7738
7739/**
7740 * Called from the client watcher thread to check for unexpected client process
7741 * death during Session_Spawning state (e.g. before it successfully opened a
7742 * direct session).
7743 *
7744 * On Win32 and on OS/2, this method is called only when we've got the
7745 * direct client's process termination notification, so it always returns @c
7746 * true.
7747 *
7748 * On other platforms, this method returns @c true if the client process is
7749 * terminated and @c false if it's still alive.
7750 *
7751 * @note Locks this object for writing.
7752 */
7753bool Machine::checkForSpawnFailure()
7754{
7755 AutoCaller autoCaller(this);
7756 if (!autoCaller.isOk())
7757 {
7758 /* nothing to do */
7759 LogFlowThisFunc(("Already uninitialized!\n"));
7760 return true;
7761 }
7762
7763 /* VirtualBox::addProcessToReap() needs a write lock */
7764 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
7765
7766 if (mData->mSession.mState != SessionState_Spawning)
7767 {
7768 /* nothing to do */
7769 LogFlowThisFunc(("Not spawning any more!\n"));
7770 return true;
7771 }
7772
7773 HRESULT rc = S_OK;
7774
7775#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7776
7777 /* the process was already unexpectedly terminated, we just need to set an
7778 * error and finalize session spawning */
7779 rc = setError(E_FAIL,
7780 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
7781 getName().c_str());
7782#else
7783
7784 /* PID not yet initialized, skip check. */
7785 if (mData->mSession.mPID == NIL_RTPROCESS)
7786 return false;
7787
7788 RTPROCSTATUS status;
7789 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
7790 &status);
7791
7792 if (vrc != VERR_PROCESS_RUNNING)
7793 {
7794 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7795 rc = setError(E_FAIL,
7796 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7797 getName().c_str(), status.iStatus);
7798 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7799 rc = setError(E_FAIL,
7800 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7801 getName().c_str(), status.iStatus);
7802 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7803 rc = setError(E_FAIL,
7804 tr("The virtual machine '%s' has terminated abnormally"),
7805 getName().c_str(), status.iStatus);
7806 else
7807 rc = setError(E_FAIL,
7808 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7809 getName().c_str(), rc);
7810 }
7811
7812#endif
7813
7814 if (FAILED(rc))
7815 {
7816 /* Close the remote session, remove the remote control from the list
7817 * and reset session state to Closed (@note keep the code in sync with
7818 * the relevant part in checkForSpawnFailure()). */
7819
7820 Assert(mData->mSession.mRemoteControls.size() == 1);
7821 if (mData->mSession.mRemoteControls.size() == 1)
7822 {
7823 ErrorInfoKeeper eik;
7824 mData->mSession.mRemoteControls.front()->Uninitialize();
7825 }
7826
7827 mData->mSession.mRemoteControls.clear();
7828 mData->mSession.mState = SessionState_Unlocked;
7829
7830 /* finalize the progress after setting the state */
7831 if (!mData->mSession.mProgress.isNull())
7832 {
7833 mData->mSession.mProgress->notifyComplete(rc);
7834 mData->mSession.mProgress.setNull();
7835 }
7836
7837 mParent->addProcessToReap(mData->mSession.mPID);
7838 mData->mSession.mPID = NIL_RTPROCESS;
7839
7840 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7841 return true;
7842 }
7843
7844 return false;
7845}
7846
7847/**
7848 * Checks whether the machine can be registered. If so, commits and saves
7849 * all settings.
7850 *
7851 * @note Must be called from mParent's write lock. Locks this object and
7852 * children for writing.
7853 */
7854HRESULT Machine::prepareRegister()
7855{
7856 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7857
7858 AutoLimitedCaller autoCaller(this);
7859 AssertComRCReturnRC(autoCaller.rc());
7860
7861 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7862
7863 /* wait for state dependents to drop to zero */
7864 ensureNoStateDependencies();
7865
7866 if (!mData->mAccessible)
7867 return setError(VBOX_E_INVALID_OBJECT_STATE,
7868 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7869 mUserData->s.strName.c_str(),
7870 mData->mUuid.toString().c_str());
7871
7872 AssertReturn(autoCaller.state() == Ready, E_FAIL);
7873
7874 if (mData->mRegistered)
7875 return setError(VBOX_E_INVALID_OBJECT_STATE,
7876 tr("The machine '%s' with UUID {%s} is already registered"),
7877 mUserData->s.strName.c_str(),
7878 mData->mUuid.toString().c_str());
7879
7880 HRESULT rc = S_OK;
7881
7882 // Ensure the settings are saved. If we are going to be registered and
7883 // no config file exists yet, create it by calling saveSettings() too.
7884 if ( (mData->flModifications)
7885 || (!mData->pMachineConfigFile->fileExists())
7886 )
7887 {
7888 rc = saveSettings(NULL);
7889 // no need to check whether VirtualBox.xml needs saving too since
7890 // we can't have a machine XML file rename pending
7891 if (FAILED(rc)) return rc;
7892 }
7893
7894 /* more config checking goes here */
7895
7896 if (SUCCEEDED(rc))
7897 {
7898 /* we may have had implicit modifications we want to fix on success */
7899 commit();
7900
7901 mData->mRegistered = true;
7902 }
7903 else
7904 {
7905 /* we may have had implicit modifications we want to cancel on failure*/
7906 rollback(false /* aNotify */);
7907 }
7908
7909 return rc;
7910}
7911
7912/**
7913 * Increases the number of objects dependent on the machine state or on the
7914 * registered state. Guarantees that these two states will not change at least
7915 * until #releaseStateDependency() is called.
7916 *
7917 * Depending on the @a aDepType value, additional state checks may be made.
7918 * These checks will set extended error info on failure. See
7919 * #checkStateDependency() for more info.
7920 *
7921 * If this method returns a failure, the dependency is not added and the caller
7922 * is not allowed to rely on any particular machine state or registration state
7923 * value and may return the failed result code to the upper level.
7924 *
7925 * @param aDepType Dependency type to add.
7926 * @param aState Current machine state (NULL if not interested).
7927 * @param aRegistered Current registered state (NULL if not interested).
7928 *
7929 * @note Locks this object for writing.
7930 */
7931HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7932 MachineState_T *aState /* = NULL */,
7933 BOOL *aRegistered /* = NULL */)
7934{
7935 AutoCaller autoCaller(this);
7936 AssertComRCReturnRC(autoCaller.rc());
7937
7938 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7939
7940 HRESULT rc = checkStateDependency(aDepType);
7941 if (FAILED(rc)) return rc;
7942
7943 {
7944 if (mData->mMachineStateChangePending != 0)
7945 {
7946 /* ensureNoStateDependencies() is waiting for state dependencies to
7947 * drop to zero so don't add more. It may make sense to wait a bit
7948 * and retry before reporting an error (since the pending state
7949 * transition should be really quick) but let's just assert for
7950 * now to see if it ever happens on practice. */
7951
7952 AssertFailed();
7953
7954 return setError(E_ACCESSDENIED,
7955 tr("Machine state change is in progress. Please retry the operation later."));
7956 }
7957
7958 ++mData->mMachineStateDeps;
7959 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7960 }
7961
7962 if (aState)
7963 *aState = mData->mMachineState;
7964 if (aRegistered)
7965 *aRegistered = mData->mRegistered;
7966
7967 return S_OK;
7968}
7969
7970/**
7971 * Decreases the number of objects dependent on the machine state.
7972 * Must always complete the #addStateDependency() call after the state
7973 * dependency is no more necessary.
7974 */
7975void Machine::releaseStateDependency()
7976{
7977 AutoCaller autoCaller(this);
7978 AssertComRCReturnVoid(autoCaller.rc());
7979
7980 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7981
7982 /* releaseStateDependency() w/o addStateDependency()? */
7983 AssertReturnVoid(mData->mMachineStateDeps != 0);
7984 -- mData->mMachineStateDeps;
7985
7986 if (mData->mMachineStateDeps == 0)
7987 {
7988 /* inform ensureNoStateDependencies() that there are no more deps */
7989 if (mData->mMachineStateChangePending != 0)
7990 {
7991 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7992 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7993 }
7994 }
7995}
7996
7997Utf8Str Machine::getExtraData(const Utf8Str &strKey)
7998{
7999 /* start with nothing found */
8000 Utf8Str strResult("");
8001
8002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8003
8004 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8005 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8006 // found:
8007 strResult = it->second; // source is a Utf8Str
8008
8009 return strResult;
8010}
8011
8012// protected methods
8013/////////////////////////////////////////////////////////////////////////////
8014
8015/**
8016 * Performs machine state checks based on the @a aDepType value. If a check
8017 * fails, this method will set extended error info, otherwise it will return
8018 * S_OK. It is supposed, that on failure, the caller will immediately return
8019 * the return value of this method to the upper level.
8020 *
8021 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8022 *
8023 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8024 * current state of this machine object allows to change settings of the
8025 * machine (i.e. the machine is not registered, or registered but not running
8026 * and not saved). It is useful to call this method from Machine setters
8027 * before performing any change.
8028 *
8029 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8030 * as for MutableStateDep except that if the machine is saved, S_OK is also
8031 * returned. This is useful in setters which allow changing machine
8032 * properties when it is in the saved state.
8033 *
8034 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
8035 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
8036 * Aborted).
8037 *
8038 * @param aDepType Dependency type to check.
8039 *
8040 * @note Non Machine based classes should use #addStateDependency() and
8041 * #releaseStateDependency() methods or the smart AutoStateDependency
8042 * template.
8043 *
8044 * @note This method must be called from under this object's read or write
8045 * lock.
8046 */
8047HRESULT Machine::checkStateDependency(StateDependency aDepType)
8048{
8049 switch (aDepType)
8050 {
8051 case AnyStateDep:
8052 {
8053 break;
8054 }
8055 case MutableStateDep:
8056 {
8057 if ( mData->mRegistered
8058 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8059 || ( mData->mMachineState != MachineState_Paused
8060 && mData->mMachineState != MachineState_Running
8061 && mData->mMachineState != MachineState_Aborted
8062 && mData->mMachineState != MachineState_Teleported
8063 && mData->mMachineState != MachineState_PoweredOff
8064 )
8065 )
8066 )
8067 return setError(VBOX_E_INVALID_VM_STATE,
8068 tr("The machine is not mutable (state is %s)"),
8069 Global::stringifyMachineState(mData->mMachineState));
8070 break;
8071 }
8072 case MutableOrSavedStateDep:
8073 {
8074 if ( mData->mRegistered
8075 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8076 || ( mData->mMachineState != MachineState_Paused
8077 && mData->mMachineState != MachineState_Running
8078 && mData->mMachineState != MachineState_Aborted
8079 && mData->mMachineState != MachineState_Teleported
8080 && mData->mMachineState != MachineState_Saved
8081 && mData->mMachineState != MachineState_PoweredOff
8082 )
8083 )
8084 )
8085 return setError(VBOX_E_INVALID_VM_STATE,
8086 tr("The machine is not mutable (state is %s)"),
8087 Global::stringifyMachineState(mData->mMachineState));
8088 break;
8089 }
8090 case OfflineStateDep:
8091 {
8092 if ( mData->mRegistered
8093 && ( !isSessionMachine()
8094 || ( mData->mMachineState != MachineState_PoweredOff
8095 && mData->mMachineState != MachineState_Saved
8096 && mData->mMachineState != MachineState_Aborted
8097 && mData->mMachineState != MachineState_Teleported
8098 )
8099 )
8100 )
8101 return setError(VBOX_E_INVALID_VM_STATE,
8102 tr("The machine is not offline (state is %s)"),
8103 Global::stringifyMachineState(mData->mMachineState));
8104 break;
8105 }
8106 }
8107
8108 return S_OK;
8109}
8110
8111/**
8112 * Helper to initialize all associated child objects and allocate data
8113 * structures.
8114 *
8115 * This method must be called as a part of the object's initialization procedure
8116 * (usually done in the #init() method).
8117 *
8118 * @note Must be called only from #init() or from #registeredInit().
8119 */
8120HRESULT Machine::initDataAndChildObjects()
8121{
8122 AutoCaller autoCaller(this);
8123 AssertComRCReturnRC(autoCaller.rc());
8124 AssertComRCReturn(autoCaller.state() == InInit ||
8125 autoCaller.state() == Limited, E_FAIL);
8126
8127 AssertReturn(!mData->mAccessible, E_FAIL);
8128
8129 /* allocate data structures */
8130 mSSData.allocate();
8131 mUserData.allocate();
8132 mHWData.allocate();
8133 mMediaData.allocate();
8134 mStorageControllers.allocate();
8135
8136 /* initialize mOSTypeId */
8137 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
8138
8139 /* create associated BIOS settings object */
8140 unconst(mBIOSSettings).createObject();
8141 mBIOSSettings->init(this);
8142
8143 /* create an associated VRDE object (default is disabled) */
8144 unconst(mVRDEServer).createObject();
8145 mVRDEServer->init(this);
8146
8147 /* create associated serial port objects */
8148 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8149 {
8150 unconst(mSerialPorts[slot]).createObject();
8151 mSerialPorts[slot]->init(this, slot);
8152 }
8153
8154 /* create associated parallel port objects */
8155 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8156 {
8157 unconst(mParallelPorts[slot]).createObject();
8158 mParallelPorts[slot]->init(this, slot);
8159 }
8160
8161 /* create the audio adapter object (always present, default is disabled) */
8162 unconst(mAudioAdapter).createObject();
8163 mAudioAdapter->init(this);
8164
8165 /* create the USB controller object (always present, default is disabled) */
8166 unconst(mUSBController).createObject();
8167 mUSBController->init(this);
8168
8169 /* create associated network adapter objects */
8170 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8171 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8172 {
8173 unconst(mNetworkAdapters[slot]).createObject();
8174 mNetworkAdapters[slot]->init(this, slot);
8175 }
8176
8177 /* create the bandwidth control */
8178 unconst(mBandwidthControl).createObject();
8179 mBandwidthControl->init(this);
8180
8181 return S_OK;
8182}
8183
8184/**
8185 * Helper to uninitialize all associated child objects and to free all data
8186 * structures.
8187 *
8188 * This method must be called as a part of the object's uninitialization
8189 * procedure (usually done in the #uninit() method).
8190 *
8191 * @note Must be called only from #uninit() or from #registeredInit().
8192 */
8193void Machine::uninitDataAndChildObjects()
8194{
8195 AutoCaller autoCaller(this);
8196 AssertComRCReturnVoid(autoCaller.rc());
8197 AssertComRCReturnVoid( autoCaller.state() == InUninit
8198 || autoCaller.state() == Limited);
8199
8200 /* tell all our other child objects we've been uninitialized */
8201 if (mBandwidthControl)
8202 {
8203 mBandwidthControl->uninit();
8204 unconst(mBandwidthControl).setNull();
8205 }
8206
8207 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8208 {
8209 if (mNetworkAdapters[slot])
8210 {
8211 mNetworkAdapters[slot]->uninit();
8212 unconst(mNetworkAdapters[slot]).setNull();
8213 }
8214 }
8215
8216 if (mUSBController)
8217 {
8218 mUSBController->uninit();
8219 unconst(mUSBController).setNull();
8220 }
8221
8222 if (mAudioAdapter)
8223 {
8224 mAudioAdapter->uninit();
8225 unconst(mAudioAdapter).setNull();
8226 }
8227
8228 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8229 {
8230 if (mParallelPorts[slot])
8231 {
8232 mParallelPorts[slot]->uninit();
8233 unconst(mParallelPorts[slot]).setNull();
8234 }
8235 }
8236
8237 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8238 {
8239 if (mSerialPorts[slot])
8240 {
8241 mSerialPorts[slot]->uninit();
8242 unconst(mSerialPorts[slot]).setNull();
8243 }
8244 }
8245
8246 if (mVRDEServer)
8247 {
8248 mVRDEServer->uninit();
8249 unconst(mVRDEServer).setNull();
8250 }
8251
8252 if (mBIOSSettings)
8253 {
8254 mBIOSSettings->uninit();
8255 unconst(mBIOSSettings).setNull();
8256 }
8257
8258 /* Deassociate media (only when a real Machine or a SnapshotMachine
8259 * instance is uninitialized; SessionMachine instances refer to real
8260 * Machine media). This is necessary for a clean re-initialization of
8261 * the VM after successfully re-checking the accessibility state. Note
8262 * that in case of normal Machine or SnapshotMachine uninitialization (as
8263 * a result of unregistering or deleting the snapshot), outdated media
8264 * attachments will already be uninitialized and deleted, so this
8265 * code will not affect them. */
8266 if ( !!mMediaData
8267 && (!isSessionMachine())
8268 )
8269 {
8270 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8271 it != mMediaData->mAttachments.end();
8272 ++it)
8273 {
8274 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8275 if (pMedium.isNull())
8276 continue;
8277 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8278 AssertComRC(rc);
8279 }
8280 }
8281
8282 if (!isSessionMachine() && !isSnapshotMachine())
8283 {
8284 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8285 if (mData->mFirstSnapshot)
8286 {
8287 // snapshots tree is protected by machine write lock; strictly
8288 // this isn't necessary here since we're deleting the entire
8289 // machine, but otherwise we assert in Snapshot::uninit()
8290 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8291 mData->mFirstSnapshot->uninit();
8292 mData->mFirstSnapshot.setNull();
8293 }
8294
8295 mData->mCurrentSnapshot.setNull();
8296 }
8297
8298 /* free data structures (the essential mData structure is not freed here
8299 * since it may be still in use) */
8300 mMediaData.free();
8301 mStorageControllers.free();
8302 mHWData.free();
8303 mUserData.free();
8304 mSSData.free();
8305}
8306
8307/**
8308 * Returns a pointer to the Machine object for this machine that acts like a
8309 * parent for complex machine data objects such as shared folders, etc.
8310 *
8311 * For primary Machine objects and for SnapshotMachine objects, returns this
8312 * object's pointer itself. For SessionMachine objects, returns the peer
8313 * (primary) machine pointer.
8314 */
8315Machine* Machine::getMachine()
8316{
8317 if (isSessionMachine())
8318 return (Machine*)mPeer;
8319 return this;
8320}
8321
8322/**
8323 * Makes sure that there are no machine state dependents. If necessary, waits
8324 * for the number of dependents to drop to zero.
8325 *
8326 * Make sure this method is called from under this object's write lock to
8327 * guarantee that no new dependents may be added when this method returns
8328 * control to the caller.
8329 *
8330 * @note Locks this object for writing. The lock will be released while waiting
8331 * (if necessary).
8332 *
8333 * @warning To be used only in methods that change the machine state!
8334 */
8335void Machine::ensureNoStateDependencies()
8336{
8337 AssertReturnVoid(isWriteLockOnCurrentThread());
8338
8339 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8340
8341 /* Wait for all state dependents if necessary */
8342 if (mData->mMachineStateDeps != 0)
8343 {
8344 /* lazy semaphore creation */
8345 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8346 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8347
8348 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8349 mData->mMachineStateDeps));
8350
8351 ++mData->mMachineStateChangePending;
8352
8353 /* reset the semaphore before waiting, the last dependent will signal
8354 * it */
8355 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8356
8357 alock.release();
8358
8359 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8360
8361 alock.acquire();
8362
8363 -- mData->mMachineStateChangePending;
8364 }
8365}
8366
8367/**
8368 * Changes the machine state and informs callbacks.
8369 *
8370 * This method is not intended to fail so it either returns S_OK or asserts (and
8371 * returns a failure).
8372 *
8373 * @note Locks this object for writing.
8374 */
8375HRESULT Machine::setMachineState(MachineState_T aMachineState)
8376{
8377 LogFlowThisFuncEnter();
8378 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8379
8380 AutoCaller autoCaller(this);
8381 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8382
8383 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8384
8385 /* wait for state dependents to drop to zero */
8386 ensureNoStateDependencies();
8387
8388 if (mData->mMachineState != aMachineState)
8389 {
8390 mData->mMachineState = aMachineState;
8391
8392 RTTimeNow(&mData->mLastStateChange);
8393
8394 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8395 }
8396
8397 LogFlowThisFuncLeave();
8398 return S_OK;
8399}
8400
8401/**
8402 * Searches for a shared folder with the given logical name
8403 * in the collection of shared folders.
8404 *
8405 * @param aName logical name of the shared folder
8406 * @param aSharedFolder where to return the found object
8407 * @param aSetError whether to set the error info if the folder is
8408 * not found
8409 * @return
8410 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8411 *
8412 * @note
8413 * must be called from under the object's lock!
8414 */
8415HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8416 ComObjPtr<SharedFolder> &aSharedFolder,
8417 bool aSetError /* = false */)
8418{
8419 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8420 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8421 it != mHWData->mSharedFolders.end();
8422 ++it)
8423 {
8424 SharedFolder *pSF = *it;
8425 AutoCaller autoCaller(pSF);
8426 if (pSF->getName() == aName)
8427 {
8428 aSharedFolder = pSF;
8429 rc = S_OK;
8430 break;
8431 }
8432 }
8433
8434 if (aSetError && FAILED(rc))
8435 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8436
8437 return rc;
8438}
8439
8440/**
8441 * Initializes all machine instance data from the given settings structures
8442 * from XML. The exception is the machine UUID which needs special handling
8443 * depending on the caller's use case, so the caller needs to set that herself.
8444 *
8445 * This gets called in several contexts during machine initialization:
8446 *
8447 * -- When machine XML exists on disk already and needs to be loaded into memory,
8448 * for example, from registeredInit() to load all registered machines on
8449 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8450 * attached to the machine should be part of some media registry already.
8451 *
8452 * -- During OVF import, when a machine config has been constructed from an
8453 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8454 * ensure that the media listed as attachments in the config (which have
8455 * been imported from the OVF) receive the correct registry ID.
8456 *
8457 * -- During VM cloning.
8458 *
8459 * @param config Machine settings from XML.
8460 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8461 * @return
8462 */
8463HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8464 const Guid *puuidRegistry)
8465{
8466 // copy name, description, OS type, teleporter, UTC etc.
8467 mUserData->s = config.machineUserData;
8468
8469 // look up the object by Id to check it is valid
8470 ComPtr<IGuestOSType> guestOSType;
8471 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8472 guestOSType.asOutParam());
8473 if (FAILED(rc)) return rc;
8474
8475 // stateFile (optional)
8476 if (config.strStateFile.isEmpty())
8477 mSSData->strStateFilePath.setNull();
8478 else
8479 {
8480 Utf8Str stateFilePathFull(config.strStateFile);
8481 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8482 if (RT_FAILURE(vrc))
8483 return setError(E_FAIL,
8484 tr("Invalid saved state file path '%s' (%Rrc)"),
8485 config.strStateFile.c_str(),
8486 vrc);
8487 mSSData->strStateFilePath = stateFilePathFull;
8488 }
8489
8490 // snapshot folder needs special processing so set it again
8491 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8492 if (FAILED(rc)) return rc;
8493
8494 /* Copy the extra data items (Not in any case config is already the same as
8495 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8496 * make sure the extra data map is copied). */
8497 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8498
8499 /* currentStateModified (optional, default is true) */
8500 mData->mCurrentStateModified = config.fCurrentStateModified;
8501
8502 mData->mLastStateChange = config.timeLastStateChange;
8503
8504 /*
8505 * note: all mUserData members must be assigned prior this point because
8506 * we need to commit changes in order to let mUserData be shared by all
8507 * snapshot machine instances.
8508 */
8509 mUserData.commitCopy();
8510
8511 // machine registry, if present (must be loaded before snapshots)
8512 if (config.canHaveOwnMediaRegistry())
8513 {
8514 // determine machine folder
8515 Utf8Str strMachineFolder = getSettingsFileFull();
8516 strMachineFolder.stripFilename();
8517 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8518 config.mediaRegistry,
8519 strMachineFolder);
8520 if (FAILED(rc)) return rc;
8521 }
8522
8523 /* Snapshot node (optional) */
8524 size_t cRootSnapshots;
8525 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8526 {
8527 // there must be only one root snapshot
8528 Assert(cRootSnapshots == 1);
8529
8530 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8531
8532 rc = loadSnapshot(snap,
8533 config.uuidCurrentSnapshot,
8534 NULL); // no parent == first snapshot
8535 if (FAILED(rc)) return rc;
8536 }
8537
8538 // hardware data
8539 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8540 if (FAILED(rc)) return rc;
8541
8542 // load storage controllers
8543 rc = loadStorageControllers(config.storageMachine,
8544 puuidRegistry,
8545 NULL /* puuidSnapshot */);
8546 if (FAILED(rc)) return rc;
8547
8548 /*
8549 * NOTE: the assignment below must be the last thing to do,
8550 * otherwise it will be not possible to change the settings
8551 * somewhere in the code above because all setters will be
8552 * blocked by checkStateDependency(MutableStateDep).
8553 */
8554
8555 /* set the machine state to Aborted or Saved when appropriate */
8556 if (config.fAborted)
8557 {
8558 mSSData->strStateFilePath.setNull();
8559
8560 /* no need to use setMachineState() during init() */
8561 mData->mMachineState = MachineState_Aborted;
8562 }
8563 else if (!mSSData->strStateFilePath.isEmpty())
8564 {
8565 /* no need to use setMachineState() during init() */
8566 mData->mMachineState = MachineState_Saved;
8567 }
8568
8569 // after loading settings, we are no longer different from the XML on disk
8570 mData->flModifications = 0;
8571
8572 return S_OK;
8573}
8574
8575/**
8576 * Recursively loads all snapshots starting from the given.
8577 *
8578 * @param aNode <Snapshot> node.
8579 * @param aCurSnapshotId Current snapshot ID from the settings file.
8580 * @param aParentSnapshot Parent snapshot.
8581 */
8582HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8583 const Guid &aCurSnapshotId,
8584 Snapshot *aParentSnapshot)
8585{
8586 AssertReturn(!isSnapshotMachine(), E_FAIL);
8587 AssertReturn(!isSessionMachine(), E_FAIL);
8588
8589 HRESULT rc = S_OK;
8590
8591 Utf8Str strStateFile;
8592 if (!data.strStateFile.isEmpty())
8593 {
8594 /* optional */
8595 strStateFile = data.strStateFile;
8596 int vrc = calculateFullPath(strStateFile, strStateFile);
8597 if (RT_FAILURE(vrc))
8598 return setError(E_FAIL,
8599 tr("Invalid saved state file path '%s' (%Rrc)"),
8600 strStateFile.c_str(),
8601 vrc);
8602 }
8603
8604 /* create a snapshot machine object */
8605 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8606 pSnapshotMachine.createObject();
8607 rc = pSnapshotMachine->initFromSettings(this,
8608 data.hardware,
8609 &data.debugging,
8610 &data.autostart,
8611 data.storage,
8612 data.uuid.ref(),
8613 strStateFile);
8614 if (FAILED(rc)) return rc;
8615
8616 /* create a snapshot object */
8617 ComObjPtr<Snapshot> pSnapshot;
8618 pSnapshot.createObject();
8619 /* initialize the snapshot */
8620 rc = pSnapshot->init(mParent, // VirtualBox object
8621 data.uuid,
8622 data.strName,
8623 data.strDescription,
8624 data.timestamp,
8625 pSnapshotMachine,
8626 aParentSnapshot);
8627 if (FAILED(rc)) return rc;
8628
8629 /* memorize the first snapshot if necessary */
8630 if (!mData->mFirstSnapshot)
8631 mData->mFirstSnapshot = pSnapshot;
8632
8633 /* memorize the current snapshot when appropriate */
8634 if ( !mData->mCurrentSnapshot
8635 && pSnapshot->getId() == aCurSnapshotId
8636 )
8637 mData->mCurrentSnapshot = pSnapshot;
8638
8639 // now create the children
8640 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8641 it != data.llChildSnapshots.end();
8642 ++it)
8643 {
8644 const settings::Snapshot &childData = *it;
8645 // recurse
8646 rc = loadSnapshot(childData,
8647 aCurSnapshotId,
8648 pSnapshot); // parent = the one we created above
8649 if (FAILED(rc)) return rc;
8650 }
8651
8652 return rc;
8653}
8654
8655/**
8656 * Loads settings into mHWData.
8657 *
8658 * @param data Reference to the hardware settings.
8659 * @param pDbg Pointer to the debugging settings.
8660 * @param pAutostart Pointer to the autostart settings.
8661 */
8662HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8663 const settings::Autostart *pAutostart)
8664{
8665 AssertReturn(!isSessionMachine(), E_FAIL);
8666
8667 HRESULT rc = S_OK;
8668
8669 try
8670 {
8671 /* The hardware version attribute (optional). */
8672 mHWData->mHWVersion = data.strVersion;
8673 mHWData->mHardwareUUID = data.uuid;
8674
8675 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8676 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8677 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8678 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8679 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8680 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8681 mHWData->mPAEEnabled = data.fPAE;
8682 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8683 mHWData->mLongMode = data.enmLongMode;
8684 mHWData->mCPUCount = data.cCPUs;
8685 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8686 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8687
8688 // cpu
8689 if (mHWData->mCPUHotPlugEnabled)
8690 {
8691 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8692 it != data.llCpus.end();
8693 ++it)
8694 {
8695 const settings::Cpu &cpu = *it;
8696
8697 mHWData->mCPUAttached[cpu.ulId] = true;
8698 }
8699 }
8700
8701 // cpuid leafs
8702 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8703 it != data.llCpuIdLeafs.end();
8704 ++it)
8705 {
8706 const settings::CpuIdLeaf &leaf = *it;
8707
8708 switch (leaf.ulId)
8709 {
8710 case 0x0:
8711 case 0x1:
8712 case 0x2:
8713 case 0x3:
8714 case 0x4:
8715 case 0x5:
8716 case 0x6:
8717 case 0x7:
8718 case 0x8:
8719 case 0x9:
8720 case 0xA:
8721 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8722 break;
8723
8724 case 0x80000000:
8725 case 0x80000001:
8726 case 0x80000002:
8727 case 0x80000003:
8728 case 0x80000004:
8729 case 0x80000005:
8730 case 0x80000006:
8731 case 0x80000007:
8732 case 0x80000008:
8733 case 0x80000009:
8734 case 0x8000000A:
8735 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8736 break;
8737
8738 default:
8739 /* just ignore */
8740 break;
8741 }
8742 }
8743
8744 mHWData->mMemorySize = data.ulMemorySizeMB;
8745 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8746
8747 // boot order
8748 for (size_t i = 0;
8749 i < RT_ELEMENTS(mHWData->mBootOrder);
8750 i++)
8751 {
8752 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8753 if (it == data.mapBootOrder.end())
8754 mHWData->mBootOrder[i] = DeviceType_Null;
8755 else
8756 mHWData->mBootOrder[i] = it->second;
8757 }
8758
8759 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8760 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8761 mHWData->mMonitorCount = data.cMonitors;
8762 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8763 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8764 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8765 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8766 mHWData->mVideoCaptureEnabled = false; /* @todo r=klaus restore to data.fVideoCaptureEnabled */
8767 mHWData->mVideoCaptureFile = data.strVideoCaptureFile;
8768 mHWData->mFirmwareType = data.firmwareType;
8769 mHWData->mPointingHIDType = data.pointingHIDType;
8770 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8771 mHWData->mChipsetType = data.chipsetType;
8772 mHWData->mEmulatedUSBWebcamEnabled = data.fEmulatedUSBWebcam;
8773 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8774 mHWData->mHPETEnabled = data.fHPETEnabled;
8775
8776 /* VRDEServer */
8777 rc = mVRDEServer->loadSettings(data.vrdeSettings);
8778 if (FAILED(rc)) return rc;
8779
8780 /* BIOS */
8781 rc = mBIOSSettings->loadSettings(data.biosSettings);
8782 if (FAILED(rc)) return rc;
8783
8784 // Bandwidth control (must come before network adapters)
8785 rc = mBandwidthControl->loadSettings(data.ioSettings);
8786 if (FAILED(rc)) return rc;
8787
8788 /* USB Controller */
8789 rc = mUSBController->loadSettings(data.usbController);
8790 if (FAILED(rc)) return rc;
8791
8792 // network adapters
8793 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8794 uint32_t oldCount = mNetworkAdapters.size();
8795 if (newCount > oldCount)
8796 {
8797 mNetworkAdapters.resize(newCount);
8798 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
8799 {
8800 unconst(mNetworkAdapters[slot]).createObject();
8801 mNetworkAdapters[slot]->init(this, slot);
8802 }
8803 }
8804 else if (newCount < oldCount)
8805 mNetworkAdapters.resize(newCount);
8806 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8807 it != data.llNetworkAdapters.end();
8808 ++it)
8809 {
8810 const settings::NetworkAdapter &nic = *it;
8811
8812 /* slot unicity is guaranteed by XML Schema */
8813 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8814 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
8815 if (FAILED(rc)) return rc;
8816 }
8817
8818 // serial ports
8819 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8820 it != data.llSerialPorts.end();
8821 ++it)
8822 {
8823 const settings::SerialPort &s = *it;
8824
8825 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8826 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
8827 if (FAILED(rc)) return rc;
8828 }
8829
8830 // parallel ports (optional)
8831 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8832 it != data.llParallelPorts.end();
8833 ++it)
8834 {
8835 const settings::ParallelPort &p = *it;
8836
8837 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8838 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
8839 if (FAILED(rc)) return rc;
8840 }
8841
8842 /* AudioAdapter */
8843 rc = mAudioAdapter->loadSettings(data.audioAdapter);
8844 if (FAILED(rc)) return rc;
8845
8846 /* Shared folders */
8847 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8848 it != data.llSharedFolders.end();
8849 ++it)
8850 {
8851 const settings::SharedFolder &sf = *it;
8852
8853 ComObjPtr<SharedFolder> sharedFolder;
8854 /* Check for double entries. Not allowed! */
8855 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8856 if (SUCCEEDED(rc))
8857 return setError(VBOX_E_OBJECT_IN_USE,
8858 tr("Shared folder named '%s' already exists"),
8859 sf.strName.c_str());
8860
8861 /* Create the new shared folder. Don't break on error. This will be
8862 * reported when the machine starts. */
8863 sharedFolder.createObject();
8864 rc = sharedFolder->init(getMachine(),
8865 sf.strName,
8866 sf.strHostPath,
8867 RT_BOOL(sf.fWritable),
8868 RT_BOOL(sf.fAutoMount),
8869 false /* fFailOnError */);
8870 if (FAILED(rc)) return rc;
8871 mHWData->mSharedFolders.push_back(sharedFolder);
8872 }
8873
8874 // Clipboard
8875 mHWData->mClipboardMode = data.clipboardMode;
8876
8877 // drag'n'drop
8878 mHWData->mDragAndDropMode = data.dragAndDropMode;
8879
8880 // guest settings
8881 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8882
8883 // IO settings
8884 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8885 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8886
8887 // Host PCI devices
8888 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8889 it != data.pciAttachments.end();
8890 ++it)
8891 {
8892 const settings::HostPCIDeviceAttachment &hpda = *it;
8893 ComObjPtr<PCIDeviceAttachment> pda;
8894
8895 pda.createObject();
8896 pda->loadSettings(this, hpda);
8897 mHWData->mPCIDeviceAssignments.push_back(pda);
8898 }
8899
8900 /*
8901 * (The following isn't really real hardware, but it lives in HWData
8902 * for reasons of convenience.)
8903 */
8904
8905#ifdef VBOX_WITH_GUEST_PROPS
8906 /* Guest properties (optional) */
8907 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8908 it != data.llGuestProperties.end();
8909 ++it)
8910 {
8911 const settings::GuestProperty &prop = *it;
8912 uint32_t fFlags = guestProp::NILFLAG;
8913 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8914 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8915 mHWData->mGuestProperties[prop.strName] = property;
8916 }
8917
8918 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8919#endif /* VBOX_WITH_GUEST_PROPS defined */
8920
8921 rc = loadDebugging(pDbg);
8922 if (FAILED(rc))
8923 return rc;
8924
8925 mHWData->mAutostart = *pAutostart;
8926
8927 /* default frontend */
8928 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8929 }
8930 catch(std::bad_alloc &)
8931 {
8932 return E_OUTOFMEMORY;
8933 }
8934
8935 AssertComRC(rc);
8936 return rc;
8937}
8938
8939/**
8940 * Called from Machine::loadHardware() to load the debugging settings of the
8941 * machine.
8942 *
8943 * @param pDbg Pointer to the settings.
8944 */
8945HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
8946{
8947 mHWData->mDebugging = *pDbg;
8948 /* no more processing currently required, this will probably change. */
8949 return S_OK;
8950}
8951
8952/**
8953 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
8954 *
8955 * @param data
8956 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8957 * @param puuidSnapshot
8958 * @return
8959 */
8960HRESULT Machine::loadStorageControllers(const settings::Storage &data,
8961 const Guid *puuidRegistry,
8962 const Guid *puuidSnapshot)
8963{
8964 AssertReturn(!isSessionMachine(), E_FAIL);
8965
8966 HRESULT rc = S_OK;
8967
8968 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8969 it != data.llStorageControllers.end();
8970 ++it)
8971 {
8972 const settings::StorageController &ctlData = *it;
8973
8974 ComObjPtr<StorageController> pCtl;
8975 /* Try to find one with the name first. */
8976 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8977 if (SUCCEEDED(rc))
8978 return setError(VBOX_E_OBJECT_IN_USE,
8979 tr("Storage controller named '%s' already exists"),
8980 ctlData.strName.c_str());
8981
8982 pCtl.createObject();
8983 rc = pCtl->init(this,
8984 ctlData.strName,
8985 ctlData.storageBus,
8986 ctlData.ulInstance,
8987 ctlData.fBootable);
8988 if (FAILED(rc)) return rc;
8989
8990 mStorageControllers->push_back(pCtl);
8991
8992 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8993 if (FAILED(rc)) return rc;
8994
8995 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8996 if (FAILED(rc)) return rc;
8997
8998 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8999 if (FAILED(rc)) return rc;
9000
9001 /* Set IDE emulation settings (only for AHCI controller). */
9002 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9003 {
9004 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9005 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9006 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9007 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9008 )
9009 return rc;
9010 }
9011
9012 /* Load the attached devices now. */
9013 rc = loadStorageDevices(pCtl,
9014 ctlData,
9015 puuidRegistry,
9016 puuidSnapshot);
9017 if (FAILED(rc)) return rc;
9018 }
9019
9020 return S_OK;
9021}
9022
9023/**
9024 * Called from loadStorageControllers for a controller's devices.
9025 *
9026 * @param aStorageController
9027 * @param data
9028 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9029 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9030 * @return
9031 */
9032HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
9033 const settings::StorageController &data,
9034 const Guid *puuidRegistry,
9035 const Guid *puuidSnapshot)
9036{
9037 HRESULT rc = S_OK;
9038
9039 /* paranoia: detect duplicate attachments */
9040 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9041 it != data.llAttachedDevices.end();
9042 ++it)
9043 {
9044 const settings::AttachedDevice &ad = *it;
9045
9046 for (settings::AttachedDevicesList::const_iterator it2 = it;
9047 it2 != data.llAttachedDevices.end();
9048 ++it2)
9049 {
9050 if (it == it2)
9051 continue;
9052
9053 const settings::AttachedDevice &ad2 = *it2;
9054
9055 if ( ad.lPort == ad2.lPort
9056 && ad.lDevice == ad2.lDevice)
9057 {
9058 return setError(E_FAIL,
9059 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9060 aStorageController->getName().c_str(),
9061 ad.lPort,
9062 ad.lDevice,
9063 mUserData->s.strName.c_str());
9064 }
9065 }
9066 }
9067
9068 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9069 it != data.llAttachedDevices.end();
9070 ++it)
9071 {
9072 const settings::AttachedDevice &dev = *it;
9073 ComObjPtr<Medium> medium;
9074
9075 switch (dev.deviceType)
9076 {
9077 case DeviceType_Floppy:
9078 case DeviceType_DVD:
9079 if (dev.strHostDriveSrc.isNotEmpty())
9080 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9081 else
9082 rc = mParent->findRemoveableMedium(dev.deviceType,
9083 dev.uuid,
9084 false /* fRefresh */,
9085 false /* aSetError */,
9086 medium);
9087 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9088 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9089 rc = S_OK;
9090 break;
9091
9092 case DeviceType_HardDisk:
9093 {
9094 /* find a hard disk by UUID */
9095 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9096 if (FAILED(rc))
9097 {
9098 if (isSnapshotMachine())
9099 {
9100 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9101 // so the user knows that the bad disk is in a snapshot somewhere
9102 com::ErrorInfo info;
9103 return setError(E_FAIL,
9104 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9105 puuidSnapshot->raw(),
9106 info.getText().raw());
9107 }
9108 else
9109 return rc;
9110 }
9111
9112 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9113
9114 if (medium->getType() == MediumType_Immutable)
9115 {
9116 if (isSnapshotMachine())
9117 return setError(E_FAIL,
9118 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9119 "of the virtual machine '%s' ('%s')"),
9120 medium->getLocationFull().c_str(),
9121 dev.uuid.raw(),
9122 puuidSnapshot->raw(),
9123 mUserData->s.strName.c_str(),
9124 mData->m_strConfigFileFull.c_str());
9125
9126 return setError(E_FAIL,
9127 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9128 medium->getLocationFull().c_str(),
9129 dev.uuid.raw(),
9130 mUserData->s.strName.c_str(),
9131 mData->m_strConfigFileFull.c_str());
9132 }
9133
9134 if (medium->getType() == MediumType_MultiAttach)
9135 {
9136 if (isSnapshotMachine())
9137 return setError(E_FAIL,
9138 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9139 "of the virtual machine '%s' ('%s')"),
9140 medium->getLocationFull().c_str(),
9141 dev.uuid.raw(),
9142 puuidSnapshot->raw(),
9143 mUserData->s.strName.c_str(),
9144 mData->m_strConfigFileFull.c_str());
9145
9146 return setError(E_FAIL,
9147 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9148 medium->getLocationFull().c_str(),
9149 dev.uuid.raw(),
9150 mUserData->s.strName.c_str(),
9151 mData->m_strConfigFileFull.c_str());
9152 }
9153
9154 if ( !isSnapshotMachine()
9155 && medium->getChildren().size() != 0
9156 )
9157 return setError(E_FAIL,
9158 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9159 "because it has %d differencing child hard disks"),
9160 medium->getLocationFull().c_str(),
9161 dev.uuid.raw(),
9162 mUserData->s.strName.c_str(),
9163 mData->m_strConfigFileFull.c_str(),
9164 medium->getChildren().size());
9165
9166 if (findAttachment(mMediaData->mAttachments,
9167 medium))
9168 return setError(E_FAIL,
9169 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9170 medium->getLocationFull().c_str(),
9171 dev.uuid.raw(),
9172 mUserData->s.strName.c_str(),
9173 mData->m_strConfigFileFull.c_str());
9174
9175 break;
9176 }
9177
9178 default:
9179 return setError(E_FAIL,
9180 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9181 medium->getLocationFull().c_str(),
9182 mUserData->s.strName.c_str(),
9183 mData->m_strConfigFileFull.c_str());
9184 }
9185
9186 if (FAILED(rc))
9187 break;
9188
9189 /* Bandwidth groups are loaded at this point. */
9190 ComObjPtr<BandwidthGroup> pBwGroup;
9191
9192 if (!dev.strBwGroup.isEmpty())
9193 {
9194 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9195 if (FAILED(rc))
9196 return setError(E_FAIL,
9197 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9198 medium->getLocationFull().c_str(),
9199 dev.strBwGroup.c_str(),
9200 mUserData->s.strName.c_str(),
9201 mData->m_strConfigFileFull.c_str());
9202 pBwGroup->reference();
9203 }
9204
9205 const Bstr controllerName = aStorageController->getName();
9206 ComObjPtr<MediumAttachment> pAttachment;
9207 pAttachment.createObject();
9208 rc = pAttachment->init(this,
9209 medium,
9210 controllerName,
9211 dev.lPort,
9212 dev.lDevice,
9213 dev.deviceType,
9214 false,
9215 dev.fPassThrough,
9216 dev.fTempEject,
9217 dev.fNonRotational,
9218 dev.fDiscard,
9219 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
9220 if (FAILED(rc)) break;
9221
9222 /* associate the medium with this machine and snapshot */
9223 if (!medium.isNull())
9224 {
9225 AutoCaller medCaller(medium);
9226 if (FAILED(medCaller.rc())) return medCaller.rc();
9227 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9228
9229 if (isSnapshotMachine())
9230 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
9231 else
9232 rc = medium->addBackReference(mData->mUuid);
9233 /* If the medium->addBackReference fails it sets an appropriate
9234 * error message, so no need to do any guesswork here. */
9235
9236 if (puuidRegistry)
9237 // caller wants registry ID to be set on all attached media (OVF import case)
9238 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9239 }
9240
9241 if (FAILED(rc))
9242 break;
9243
9244 /* back up mMediaData to let registeredInit() properly rollback on failure
9245 * (= limited accessibility) */
9246 setModified(IsModified_Storage);
9247 mMediaData.backup();
9248 mMediaData->mAttachments.push_back(pAttachment);
9249 }
9250
9251 return rc;
9252}
9253
9254/**
9255 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9256 *
9257 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9258 * @param aSnapshot where to return the found snapshot
9259 * @param aSetError true to set extended error info on failure
9260 */
9261HRESULT Machine::findSnapshotById(const Guid &aId,
9262 ComObjPtr<Snapshot> &aSnapshot,
9263 bool aSetError /* = false */)
9264{
9265 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9266
9267 if (!mData->mFirstSnapshot)
9268 {
9269 if (aSetError)
9270 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9271 return E_FAIL;
9272 }
9273
9274 if (aId.isZero())
9275 aSnapshot = mData->mFirstSnapshot;
9276 else
9277 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9278
9279 if (!aSnapshot)
9280 {
9281 if (aSetError)
9282 return setError(E_FAIL,
9283 tr("Could not find a snapshot with UUID {%s}"),
9284 aId.toString().c_str());
9285 return E_FAIL;
9286 }
9287
9288 return S_OK;
9289}
9290
9291/**
9292 * Returns the snapshot with the given name or fails of no such snapshot.
9293 *
9294 * @param aName snapshot name to find
9295 * @param aSnapshot where to return the found snapshot
9296 * @param aSetError true to set extended error info on failure
9297 */
9298HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9299 ComObjPtr<Snapshot> &aSnapshot,
9300 bool aSetError /* = false */)
9301{
9302 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9303
9304 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9305
9306 if (!mData->mFirstSnapshot)
9307 {
9308 if (aSetError)
9309 return setError(VBOX_E_OBJECT_NOT_FOUND,
9310 tr("This machine does not have any snapshots"));
9311 return VBOX_E_OBJECT_NOT_FOUND;
9312 }
9313
9314 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9315
9316 if (!aSnapshot)
9317 {
9318 if (aSetError)
9319 return setError(VBOX_E_OBJECT_NOT_FOUND,
9320 tr("Could not find a snapshot named '%s'"), strName.c_str());
9321 return VBOX_E_OBJECT_NOT_FOUND;
9322 }
9323
9324 return S_OK;
9325}
9326
9327/**
9328 * Returns a storage controller object with the given name.
9329 *
9330 * @param aName storage controller name to find
9331 * @param aStorageController where to return the found storage controller
9332 * @param aSetError true to set extended error info on failure
9333 */
9334HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9335 ComObjPtr<StorageController> &aStorageController,
9336 bool aSetError /* = false */)
9337{
9338 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9339
9340 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9341 it != mStorageControllers->end();
9342 ++it)
9343 {
9344 if ((*it)->getName() == aName)
9345 {
9346 aStorageController = (*it);
9347 return S_OK;
9348 }
9349 }
9350
9351 if (aSetError)
9352 return setError(VBOX_E_OBJECT_NOT_FOUND,
9353 tr("Could not find a storage controller named '%s'"),
9354 aName.c_str());
9355 return VBOX_E_OBJECT_NOT_FOUND;
9356}
9357
9358HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9359 MediaData::AttachmentList &atts)
9360{
9361 AutoCaller autoCaller(this);
9362 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9363
9364 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9365
9366 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9367 it != mMediaData->mAttachments.end();
9368 ++it)
9369 {
9370 const ComObjPtr<MediumAttachment> &pAtt = *it;
9371
9372 // should never happen, but deal with NULL pointers in the list.
9373 AssertStmt(!pAtt.isNull(), continue);
9374
9375 // getControllerName() needs caller+read lock
9376 AutoCaller autoAttCaller(pAtt);
9377 if (FAILED(autoAttCaller.rc()))
9378 {
9379 atts.clear();
9380 return autoAttCaller.rc();
9381 }
9382 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9383
9384 if (pAtt->getControllerName() == aName)
9385 atts.push_back(pAtt);
9386 }
9387
9388 return S_OK;
9389}
9390
9391/**
9392 * Helper for #saveSettings. Cares about renaming the settings directory and
9393 * file if the machine name was changed and about creating a new settings file
9394 * if this is a new machine.
9395 *
9396 * @note Must be never called directly but only from #saveSettings().
9397 */
9398HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9399{
9400 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9401
9402 HRESULT rc = S_OK;
9403
9404 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9405
9406 /// @todo need to handle primary group change, too
9407
9408 /* attempt to rename the settings file if machine name is changed */
9409 if ( mUserData->s.fNameSync
9410 && mUserData.isBackedUp()
9411 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9412 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9413 )
9414 {
9415 bool dirRenamed = false;
9416 bool fileRenamed = false;
9417
9418 Utf8Str configFile, newConfigFile;
9419 Utf8Str configFilePrev, newConfigFilePrev;
9420 Utf8Str configDir, newConfigDir;
9421
9422 do
9423 {
9424 int vrc = VINF_SUCCESS;
9425
9426 Utf8Str name = mUserData.backedUpData()->s.strName;
9427 Utf8Str newName = mUserData->s.strName;
9428 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9429 if (group == "/")
9430 group.setNull();
9431 Utf8Str newGroup = mUserData->s.llGroups.front();
9432 if (newGroup == "/")
9433 newGroup.setNull();
9434
9435 configFile = mData->m_strConfigFileFull;
9436
9437 /* first, rename the directory if it matches the group and machine name */
9438 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9439 group.c_str(), RTPATH_DELIMITER, name.c_str());
9440 /** @todo hack, make somehow use of ComposeMachineFilename */
9441 if (mUserData->s.fDirectoryIncludesUUID)
9442 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9443 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9444 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9445 /** @todo hack, make somehow use of ComposeMachineFilename */
9446 if (mUserData->s.fDirectoryIncludesUUID)
9447 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9448 configDir = configFile;
9449 configDir.stripFilename();
9450 newConfigDir = configDir;
9451 if ( configDir.length() >= groupPlusName.length()
9452 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9453 {
9454 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9455 Utf8Str newConfigBaseDir(newConfigDir);
9456 newConfigDir.append(newGroupPlusName);
9457 /* consistency: use \ if appropriate on the platform */
9458 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9459 /* new dir and old dir cannot be equal here because of 'if'
9460 * above and because name != newName */
9461 Assert(configDir != newConfigDir);
9462 if (!fSettingsFileIsNew)
9463 {
9464 /* perform real rename only if the machine is not new */
9465 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9466 if ( vrc == VERR_FILE_NOT_FOUND
9467 || vrc == VERR_PATH_NOT_FOUND)
9468 {
9469 /* create the parent directory, then retry renaming */
9470 Utf8Str parent(newConfigDir);
9471 parent.stripFilename();
9472 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9473 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9474 }
9475 if (RT_FAILURE(vrc))
9476 {
9477 rc = setError(E_FAIL,
9478 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9479 configDir.c_str(),
9480 newConfigDir.c_str(),
9481 vrc);
9482 break;
9483 }
9484 /* delete subdirectories which are no longer needed */
9485 Utf8Str dir(configDir);
9486 dir.stripFilename();
9487 while (dir != newConfigBaseDir && dir != ".")
9488 {
9489 vrc = RTDirRemove(dir.c_str());
9490 if (RT_FAILURE(vrc))
9491 break;
9492 dir.stripFilename();
9493 }
9494 dirRenamed = true;
9495 }
9496 }
9497
9498 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9499 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9500
9501 /* then try to rename the settings file itself */
9502 if (newConfigFile != configFile)
9503 {
9504 /* get the path to old settings file in renamed directory */
9505 configFile = Utf8StrFmt("%s%c%s",
9506 newConfigDir.c_str(),
9507 RTPATH_DELIMITER,
9508 RTPathFilename(configFile.c_str()));
9509 if (!fSettingsFileIsNew)
9510 {
9511 /* perform real rename only if the machine is not new */
9512 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9513 if (RT_FAILURE(vrc))
9514 {
9515 rc = setError(E_FAIL,
9516 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9517 configFile.c_str(),
9518 newConfigFile.c_str(),
9519 vrc);
9520 break;
9521 }
9522 fileRenamed = true;
9523 configFilePrev = configFile;
9524 configFilePrev += "-prev";
9525 newConfigFilePrev = newConfigFile;
9526 newConfigFilePrev += "-prev";
9527 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9528 }
9529 }
9530
9531 // update m_strConfigFileFull amd mConfigFile
9532 mData->m_strConfigFileFull = newConfigFile;
9533 // compute the relative path too
9534 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9535
9536 // store the old and new so that VirtualBox::saveSettings() can update
9537 // the media registry
9538 if ( mData->mRegistered
9539 && configDir != newConfigDir)
9540 {
9541 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9542
9543 if (pfNeedsGlobalSaveSettings)
9544 *pfNeedsGlobalSaveSettings = true;
9545 }
9546
9547 // in the saved state file path, replace the old directory with the new directory
9548 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9549 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9550
9551 // and do the same thing for the saved state file paths of all the online snapshots
9552 if (mData->mFirstSnapshot)
9553 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9554 newConfigDir.c_str());
9555 }
9556 while (0);
9557
9558 if (FAILED(rc))
9559 {
9560 /* silently try to rename everything back */
9561 if (fileRenamed)
9562 {
9563 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9564 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9565 }
9566 if (dirRenamed)
9567 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9568 }
9569
9570 if (FAILED(rc)) return rc;
9571 }
9572
9573 if (fSettingsFileIsNew)
9574 {
9575 /* create a virgin config file */
9576 int vrc = VINF_SUCCESS;
9577
9578 /* ensure the settings directory exists */
9579 Utf8Str path(mData->m_strConfigFileFull);
9580 path.stripFilename();
9581 if (!RTDirExists(path.c_str()))
9582 {
9583 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9584 if (RT_FAILURE(vrc))
9585 {
9586 return setError(E_FAIL,
9587 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9588 path.c_str(),
9589 vrc);
9590 }
9591 }
9592
9593 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9594 path = Utf8Str(mData->m_strConfigFileFull);
9595 RTFILE f = NIL_RTFILE;
9596 vrc = RTFileOpen(&f, path.c_str(),
9597 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9598 if (RT_FAILURE(vrc))
9599 return setError(E_FAIL,
9600 tr("Could not create the settings file '%s' (%Rrc)"),
9601 path.c_str(),
9602 vrc);
9603 RTFileClose(f);
9604 }
9605
9606 return rc;
9607}
9608
9609/**
9610 * Saves and commits machine data, user data and hardware data.
9611 *
9612 * Note that on failure, the data remains uncommitted.
9613 *
9614 * @a aFlags may combine the following flags:
9615 *
9616 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9617 * Used when saving settings after an operation that makes them 100%
9618 * correspond to the settings from the current snapshot.
9619 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9620 * #isReallyModified() returns false. This is necessary for cases when we
9621 * change machine data directly, not through the backup()/commit() mechanism.
9622 * - SaveS_Force: settings will be saved without doing a deep compare of the
9623 * settings structures. This is used when this is called because snapshots
9624 * have changed to avoid the overhead of the deep compare.
9625 *
9626 * @note Must be called from under this object's write lock. Locks children for
9627 * writing.
9628 *
9629 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9630 * initialized to false and that will be set to true by this function if
9631 * the caller must invoke VirtualBox::saveSettings() because the global
9632 * settings have changed. This will happen if a machine rename has been
9633 * saved and the global machine and media registries will therefore need
9634 * updating.
9635 */
9636HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9637 int aFlags /*= 0*/)
9638{
9639 LogFlowThisFuncEnter();
9640
9641 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9642
9643 /* make sure child objects are unable to modify the settings while we are
9644 * saving them */
9645 ensureNoStateDependencies();
9646
9647 AssertReturn(!isSnapshotMachine(),
9648 E_FAIL);
9649
9650 HRESULT rc = S_OK;
9651 bool fNeedsWrite = false;
9652
9653 /* First, prepare to save settings. It will care about renaming the
9654 * settings directory and file if the machine name was changed and about
9655 * creating a new settings file if this is a new machine. */
9656 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9657 if (FAILED(rc)) return rc;
9658
9659 // keep a pointer to the current settings structures
9660 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9661 settings::MachineConfigFile *pNewConfig = NULL;
9662
9663 try
9664 {
9665 // make a fresh one to have everyone write stuff into
9666 pNewConfig = new settings::MachineConfigFile(NULL);
9667 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9668
9669 // now go and copy all the settings data from COM to the settings structures
9670 // (this calles saveSettings() on all the COM objects in the machine)
9671 copyMachineDataToSettings(*pNewConfig);
9672
9673 if (aFlags & SaveS_ResetCurStateModified)
9674 {
9675 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9676 mData->mCurrentStateModified = FALSE;
9677 fNeedsWrite = true; // always, no need to compare
9678 }
9679 else if (aFlags & SaveS_Force)
9680 {
9681 fNeedsWrite = true; // always, no need to compare
9682 }
9683 else
9684 {
9685 if (!mData->mCurrentStateModified)
9686 {
9687 // do a deep compare of the settings that we just saved with the settings
9688 // previously stored in the config file; this invokes MachineConfigFile::operator==
9689 // which does a deep compare of all the settings, which is expensive but less expensive
9690 // than writing out XML in vain
9691 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9692
9693 // could still be modified if any settings changed
9694 mData->mCurrentStateModified = fAnySettingsChanged;
9695
9696 fNeedsWrite = fAnySettingsChanged;
9697 }
9698 else
9699 fNeedsWrite = true;
9700 }
9701
9702 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9703
9704 if (fNeedsWrite)
9705 // now spit it all out!
9706 pNewConfig->write(mData->m_strConfigFileFull);
9707
9708 mData->pMachineConfigFile = pNewConfig;
9709 delete pOldConfig;
9710 commit();
9711
9712 // after saving settings, we are no longer different from the XML on disk
9713 mData->flModifications = 0;
9714 }
9715 catch (HRESULT err)
9716 {
9717 // we assume that error info is set by the thrower
9718 rc = err;
9719
9720 // restore old config
9721 delete pNewConfig;
9722 mData->pMachineConfigFile = pOldConfig;
9723 }
9724 catch (...)
9725 {
9726 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9727 }
9728
9729 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9730 {
9731 /* Fire the data change event, even on failure (since we've already
9732 * committed all data). This is done only for SessionMachines because
9733 * mutable Machine instances are always not registered (i.e. private
9734 * to the client process that creates them) and thus don't need to
9735 * inform callbacks. */
9736 if (isSessionMachine())
9737 mParent->onMachineDataChange(mData->mUuid);
9738 }
9739
9740 LogFlowThisFunc(("rc=%08X\n", rc));
9741 LogFlowThisFuncLeave();
9742 return rc;
9743}
9744
9745/**
9746 * Implementation for saving the machine settings into the given
9747 * settings::MachineConfigFile instance. This copies machine extradata
9748 * from the previous machine config file in the instance data, if any.
9749 *
9750 * This gets called from two locations:
9751 *
9752 * -- Machine::saveSettings(), during the regular XML writing;
9753 *
9754 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9755 * exported to OVF and we write the VirtualBox proprietary XML
9756 * into a <vbox:Machine> tag.
9757 *
9758 * This routine fills all the fields in there, including snapshots, *except*
9759 * for the following:
9760 *
9761 * -- fCurrentStateModified. There is some special logic associated with that.
9762 *
9763 * The caller can then call MachineConfigFile::write() or do something else
9764 * with it.
9765 *
9766 * Caller must hold the machine lock!
9767 *
9768 * This throws XML errors and HRESULT, so the caller must have a catch block!
9769 */
9770void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
9771{
9772 // deep copy extradata
9773 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9774
9775 config.uuid = mData->mUuid;
9776
9777 // copy name, description, OS type, teleport, UTC etc.
9778 config.machineUserData = mUserData->s;
9779
9780 if ( mData->mMachineState == MachineState_Saved
9781 || mData->mMachineState == MachineState_Restoring
9782 // when deleting a snapshot we may or may not have a saved state in the current state,
9783 // so let's not assert here please
9784 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9785 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9786 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9787 && (!mSSData->strStateFilePath.isEmpty())
9788 )
9789 )
9790 {
9791 Assert(!mSSData->strStateFilePath.isEmpty());
9792 /* try to make the file name relative to the settings file dir */
9793 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9794 }
9795 else
9796 {
9797 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9798 config.strStateFile.setNull();
9799 }
9800
9801 if (mData->mCurrentSnapshot)
9802 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
9803 else
9804 config.uuidCurrentSnapshot.clear();
9805
9806 config.timeLastStateChange = mData->mLastStateChange;
9807 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9808 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9809
9810 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9811 if (FAILED(rc)) throw rc;
9812
9813 rc = saveStorageControllers(config.storageMachine);
9814 if (FAILED(rc)) throw rc;
9815
9816 // save machine's media registry if this is VirtualBox 4.0 or later
9817 if (config.canHaveOwnMediaRegistry())
9818 {
9819 // determine machine folder
9820 Utf8Str strMachineFolder = getSettingsFileFull();
9821 strMachineFolder.stripFilename();
9822 mParent->saveMediaRegistry(config.mediaRegistry,
9823 getId(), // only media with registry ID == machine UUID
9824 strMachineFolder);
9825 // this throws HRESULT
9826 }
9827
9828 // save snapshots
9829 rc = saveAllSnapshots(config);
9830 if (FAILED(rc)) throw rc;
9831}
9832
9833/**
9834 * Saves all snapshots of the machine into the given machine config file. Called
9835 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9836 * @param config
9837 * @return
9838 */
9839HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
9840{
9841 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9842
9843 HRESULT rc = S_OK;
9844
9845 try
9846 {
9847 config.llFirstSnapshot.clear();
9848
9849 if (mData->mFirstSnapshot)
9850 {
9851 settings::Snapshot snapNew;
9852 config.llFirstSnapshot.push_back(snapNew);
9853
9854 // get reference to the fresh copy of the snapshot on the list and
9855 // work on that copy directly to avoid excessive copying later
9856 settings::Snapshot &snap = config.llFirstSnapshot.front();
9857
9858 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
9859 if (FAILED(rc)) throw rc;
9860 }
9861
9862// if (mType == IsSessionMachine)
9863// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9864
9865 }
9866 catch (HRESULT err)
9867 {
9868 /* we assume that error info is set by the thrower */
9869 rc = err;
9870 }
9871 catch (...)
9872 {
9873 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9874 }
9875
9876 return rc;
9877}
9878
9879/**
9880 * Saves the VM hardware configuration. It is assumed that the
9881 * given node is empty.
9882 *
9883 * @param data Reference to the settings object for the hardware config.
9884 * @param pDbg Pointer to the settings object for the debugging config
9885 * which happens to live in mHWData.
9886 * @param pAutostart Pointer to the settings object for the autostart config
9887 * which happens to live in mHWData.
9888 */
9889HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9890 settings::Autostart *pAutostart)
9891{
9892 HRESULT rc = S_OK;
9893
9894 try
9895 {
9896 /* The hardware version attribute (optional).
9897 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9898 if ( mHWData->mHWVersion == "1"
9899 && mSSData->strStateFilePath.isEmpty()
9900 )
9901 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. */
9902
9903 data.strVersion = mHWData->mHWVersion;
9904 data.uuid = mHWData->mHardwareUUID;
9905
9906 // CPU
9907 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9908 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
9909 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9910 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9911 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9912 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9913 data.fPAE = !!mHWData->mPAEEnabled;
9914 data.enmLongMode = mHWData->mLongMode;
9915 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9916
9917 /* Standard and Extended CPUID leafs. */
9918 data.llCpuIdLeafs.clear();
9919 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
9920 {
9921 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9922 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9923 }
9924 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
9925 {
9926 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9927 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9928 }
9929
9930 data.cCPUs = mHWData->mCPUCount;
9931 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9932 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9933
9934 data.llCpus.clear();
9935 if (data.fCpuHotPlug)
9936 {
9937 for (unsigned idx = 0; idx < data.cCPUs; idx++)
9938 {
9939 if (mHWData->mCPUAttached[idx])
9940 {
9941 settings::Cpu cpu;
9942 cpu.ulId = idx;
9943 data.llCpus.push_back(cpu);
9944 }
9945 }
9946 }
9947
9948 // memory
9949 data.ulMemorySizeMB = mHWData->mMemorySize;
9950 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9951
9952 // firmware
9953 data.firmwareType = mHWData->mFirmwareType;
9954
9955 // HID
9956 data.pointingHIDType = mHWData->mPointingHIDType;
9957 data.keyboardHIDType = mHWData->mKeyboardHIDType;
9958
9959 // chipset
9960 data.chipsetType = mHWData->mChipsetType;
9961
9962 data.fEmulatedUSBWebcam = !!mHWData->mEmulatedUSBWebcamEnabled;
9963 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9964
9965 // HPET
9966 data.fHPETEnabled = !!mHWData->mHPETEnabled;
9967
9968 // boot order
9969 data.mapBootOrder.clear();
9970 for (size_t i = 0;
9971 i < RT_ELEMENTS(mHWData->mBootOrder);
9972 ++i)
9973 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9974
9975 // display
9976 data.graphicsControllerType = mHWData->mGraphicsControllerType;
9977 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9978 data.cMonitors = mHWData->mMonitorCount;
9979 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9980 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9981 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
9982 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
9983 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
9984 data.strVideoCaptureFile = mHWData->mVideoCaptureFile;
9985
9986 /* VRDEServer settings (optional) */
9987 rc = mVRDEServer->saveSettings(data.vrdeSettings);
9988 if (FAILED(rc)) throw rc;
9989
9990 /* BIOS (required) */
9991 rc = mBIOSSettings->saveSettings(data.biosSettings);
9992 if (FAILED(rc)) throw rc;
9993
9994 /* USB Controller (required) */
9995 rc = mUSBController->saveSettings(data.usbController);
9996 if (FAILED(rc)) throw rc;
9997
9998 /* Network adapters (required) */
9999 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10000 data.llNetworkAdapters.clear();
10001 /* Write out only the nominal number of network adapters for this
10002 * chipset type. Since Machine::commit() hasn't been called there
10003 * may be extra NIC settings in the vector. */
10004 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10005 {
10006 settings::NetworkAdapter nic;
10007 nic.ulSlot = slot;
10008 /* paranoia check... must not be NULL, but must not crash either. */
10009 if (mNetworkAdapters[slot])
10010 {
10011 rc = mNetworkAdapters[slot]->saveSettings(nic);
10012 if (FAILED(rc)) throw rc;
10013
10014 data.llNetworkAdapters.push_back(nic);
10015 }
10016 }
10017
10018 /* Serial ports */
10019 data.llSerialPorts.clear();
10020 for (ULONG slot = 0;
10021 slot < RT_ELEMENTS(mSerialPorts);
10022 ++slot)
10023 {
10024 settings::SerialPort s;
10025 s.ulSlot = slot;
10026 rc = mSerialPorts[slot]->saveSettings(s);
10027 if (FAILED(rc)) return rc;
10028
10029 data.llSerialPorts.push_back(s);
10030 }
10031
10032 /* Parallel ports */
10033 data.llParallelPorts.clear();
10034 for (ULONG slot = 0;
10035 slot < RT_ELEMENTS(mParallelPorts);
10036 ++slot)
10037 {
10038 settings::ParallelPort p;
10039 p.ulSlot = slot;
10040 rc = mParallelPorts[slot]->saveSettings(p);
10041 if (FAILED(rc)) return rc;
10042
10043 data.llParallelPorts.push_back(p);
10044 }
10045
10046 /* Audio adapter */
10047 rc = mAudioAdapter->saveSettings(data.audioAdapter);
10048 if (FAILED(rc)) return rc;
10049
10050 /* Shared folders */
10051 data.llSharedFolders.clear();
10052 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10053 it != mHWData->mSharedFolders.end();
10054 ++it)
10055 {
10056 SharedFolder *pSF = *it;
10057 AutoCaller sfCaller(pSF);
10058 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10059 settings::SharedFolder sf;
10060 sf.strName = pSF->getName();
10061 sf.strHostPath = pSF->getHostPath();
10062 sf.fWritable = !!pSF->isWritable();
10063 sf.fAutoMount = !!pSF->isAutoMounted();
10064
10065 data.llSharedFolders.push_back(sf);
10066 }
10067
10068 // clipboard
10069 data.clipboardMode = mHWData->mClipboardMode;
10070
10071 // drag'n'drop
10072 data.dragAndDropMode = mHWData->mDragAndDropMode;
10073
10074 /* Guest */
10075 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10076
10077 // IO settings
10078 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10079 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10080
10081 /* BandwidthControl (required) */
10082 rc = mBandwidthControl->saveSettings(data.ioSettings);
10083 if (FAILED(rc)) throw rc;
10084
10085 /* Host PCI devices */
10086 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10087 it != mHWData->mPCIDeviceAssignments.end();
10088 ++it)
10089 {
10090 ComObjPtr<PCIDeviceAttachment> pda = *it;
10091 settings::HostPCIDeviceAttachment hpda;
10092
10093 rc = pda->saveSettings(hpda);
10094 if (FAILED(rc)) throw rc;
10095
10096 data.pciAttachments.push_back(hpda);
10097 }
10098
10099
10100 // guest properties
10101 data.llGuestProperties.clear();
10102#ifdef VBOX_WITH_GUEST_PROPS
10103 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10104 it != mHWData->mGuestProperties.end();
10105 ++it)
10106 {
10107 HWData::GuestProperty property = it->second;
10108
10109 /* Remove transient guest properties at shutdown unless we
10110 * are saving state */
10111 if ( ( mData->mMachineState == MachineState_PoweredOff
10112 || mData->mMachineState == MachineState_Aborted
10113 || mData->mMachineState == MachineState_Teleported)
10114 && ( property.mFlags & guestProp::TRANSIENT
10115 || property.mFlags & guestProp::TRANSRESET))
10116 continue;
10117 settings::GuestProperty prop;
10118 prop.strName = it->first;
10119 prop.strValue = property.strValue;
10120 prop.timestamp = property.mTimestamp;
10121 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10122 guestProp::writeFlags(property.mFlags, szFlags);
10123 prop.strFlags = szFlags;
10124
10125 data.llGuestProperties.push_back(prop);
10126 }
10127
10128 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10129 /* I presume this doesn't require a backup(). */
10130 mData->mGuestPropertiesModified = FALSE;
10131#endif /* VBOX_WITH_GUEST_PROPS defined */
10132
10133 *pDbg = mHWData->mDebugging;
10134 *pAutostart = mHWData->mAutostart;
10135
10136 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10137 }
10138 catch(std::bad_alloc &)
10139 {
10140 return E_OUTOFMEMORY;
10141 }
10142
10143 AssertComRC(rc);
10144 return rc;
10145}
10146
10147/**
10148 * Saves the storage controller configuration.
10149 *
10150 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10151 */
10152HRESULT Machine::saveStorageControllers(settings::Storage &data)
10153{
10154 data.llStorageControllers.clear();
10155
10156 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10157 it != mStorageControllers->end();
10158 ++it)
10159 {
10160 HRESULT rc;
10161 ComObjPtr<StorageController> pCtl = *it;
10162
10163 settings::StorageController ctl;
10164 ctl.strName = pCtl->getName();
10165 ctl.controllerType = pCtl->getControllerType();
10166 ctl.storageBus = pCtl->getStorageBus();
10167 ctl.ulInstance = pCtl->getInstance();
10168 ctl.fBootable = pCtl->getBootable();
10169
10170 /* Save the port count. */
10171 ULONG portCount;
10172 rc = pCtl->COMGETTER(PortCount)(&portCount);
10173 ComAssertComRCRet(rc, rc);
10174 ctl.ulPortCount = portCount;
10175
10176 /* Save fUseHostIOCache */
10177 BOOL fUseHostIOCache;
10178 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10179 ComAssertComRCRet(rc, rc);
10180 ctl.fUseHostIOCache = !!fUseHostIOCache;
10181
10182 /* Save IDE emulation settings. */
10183 if (ctl.controllerType == StorageControllerType_IntelAhci)
10184 {
10185 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10186 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10187 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10188 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10189 )
10190 ComAssertComRCRet(rc, rc);
10191 }
10192
10193 /* save the devices now. */
10194 rc = saveStorageDevices(pCtl, ctl);
10195 ComAssertComRCRet(rc, rc);
10196
10197 data.llStorageControllers.push_back(ctl);
10198 }
10199
10200 return S_OK;
10201}
10202
10203/**
10204 * Saves the hard disk configuration.
10205 */
10206HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10207 settings::StorageController &data)
10208{
10209 MediaData::AttachmentList atts;
10210
10211 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
10212 if (FAILED(rc)) return rc;
10213
10214 data.llAttachedDevices.clear();
10215 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10216 it != atts.end();
10217 ++it)
10218 {
10219 settings::AttachedDevice dev;
10220
10221 MediumAttachment *pAttach = *it;
10222 Medium *pMedium = pAttach->getMedium();
10223
10224 dev.deviceType = pAttach->getType();
10225 dev.lPort = pAttach->getPort();
10226 dev.lDevice = pAttach->getDevice();
10227 if (pMedium)
10228 {
10229 if (pMedium->isHostDrive())
10230 dev.strHostDriveSrc = pMedium->getLocationFull();
10231 else
10232 dev.uuid = pMedium->getId();
10233 dev.fPassThrough = pAttach->getPassthrough();
10234 dev.fTempEject = pAttach->getTempEject();
10235 dev.fNonRotational = pAttach->getNonRotational();
10236 dev.fDiscard = pAttach->getDiscard();
10237 }
10238
10239 dev.strBwGroup = pAttach->getBandwidthGroup();
10240
10241 data.llAttachedDevices.push_back(dev);
10242 }
10243
10244 return S_OK;
10245}
10246
10247/**
10248 * Saves machine state settings as defined by aFlags
10249 * (SaveSTS_* values).
10250 *
10251 * @param aFlags Combination of SaveSTS_* flags.
10252 *
10253 * @note Locks objects for writing.
10254 */
10255HRESULT Machine::saveStateSettings(int aFlags)
10256{
10257 if (aFlags == 0)
10258 return S_OK;
10259
10260 AutoCaller autoCaller(this);
10261 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10262
10263 /* This object's write lock is also necessary to serialize file access
10264 * (prevent concurrent reads and writes) */
10265 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10266
10267 HRESULT rc = S_OK;
10268
10269 Assert(mData->pMachineConfigFile);
10270
10271 try
10272 {
10273 if (aFlags & SaveSTS_CurStateModified)
10274 mData->pMachineConfigFile->fCurrentStateModified = true;
10275
10276 if (aFlags & SaveSTS_StateFilePath)
10277 {
10278 if (!mSSData->strStateFilePath.isEmpty())
10279 /* try to make the file name relative to the settings file dir */
10280 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10281 else
10282 mData->pMachineConfigFile->strStateFile.setNull();
10283 }
10284
10285 if (aFlags & SaveSTS_StateTimeStamp)
10286 {
10287 Assert( mData->mMachineState != MachineState_Aborted
10288 || mSSData->strStateFilePath.isEmpty());
10289
10290 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10291
10292 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10293//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10294 }
10295
10296 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10297 }
10298 catch (...)
10299 {
10300 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10301 }
10302
10303 return rc;
10304}
10305
10306/**
10307 * Ensures that the given medium is added to a media registry. If this machine
10308 * was created with 4.0 or later, then the machine registry is used. Otherwise
10309 * the global VirtualBox media registry is used.
10310 *
10311 * Caller must NOT hold machine lock, media tree or any medium locks!
10312 *
10313 * @param pMedium
10314 */
10315void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10316{
10317 /* Paranoia checks: do not hold machine or media tree locks. */
10318 AssertReturnVoid(!isWriteLockOnCurrentThread());
10319 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10320
10321 ComObjPtr<Medium> pBase;
10322 {
10323 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10324 pBase = pMedium->getBase();
10325 }
10326
10327 /* Paranoia checks: do not hold medium locks. */
10328 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10329 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10330
10331 // decide which medium registry to use now that the medium is attached:
10332 Guid uuid;
10333 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10334 // machine XML is VirtualBox 4.0 or higher:
10335 uuid = getId(); // machine UUID
10336 else
10337 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10338
10339 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10340 mParent->markRegistryModified(uuid);
10341
10342 /* For more complex hard disk structures it can happen that the base
10343 * medium isn't yet associated with any medium registry. Do that now. */
10344 if (pMedium != pBase)
10345 {
10346 if (pBase->addRegistry(uuid, true /* fRecurse */))
10347 mParent->markRegistryModified(uuid);
10348 }
10349}
10350
10351/**
10352 * Creates differencing hard disks for all normal hard disks attached to this
10353 * machine and a new set of attachments to refer to created disks.
10354 *
10355 * Used when taking a snapshot or when deleting the current state. Gets called
10356 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10357 *
10358 * This method assumes that mMediaData contains the original hard disk attachments
10359 * it needs to create diffs for. On success, these attachments will be replaced
10360 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10361 * called to delete created diffs which will also rollback mMediaData and restore
10362 * whatever was backed up before calling this method.
10363 *
10364 * Attachments with non-normal hard disks are left as is.
10365 *
10366 * If @a aOnline is @c false then the original hard disks that require implicit
10367 * diffs will be locked for reading. Otherwise it is assumed that they are
10368 * already locked for writing (when the VM was started). Note that in the latter
10369 * case it is responsibility of the caller to lock the newly created diffs for
10370 * writing if this method succeeds.
10371 *
10372 * @param aProgress Progress object to run (must contain at least as
10373 * many operations left as the number of hard disks
10374 * attached).
10375 * @param aOnline Whether the VM was online prior to this operation.
10376 *
10377 * @note The progress object is not marked as completed, neither on success nor
10378 * on failure. This is a responsibility of the caller.
10379 *
10380 * @note Locks this object and the media tree for writing.
10381 */
10382HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10383 ULONG aWeight,
10384 bool aOnline)
10385{
10386 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10387
10388 AutoCaller autoCaller(this);
10389 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10390
10391 AutoMultiWriteLock2 alock(this->lockHandle(),
10392 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10393
10394 /* must be in a protective state because we release the lock below */
10395 AssertReturn( mData->mMachineState == MachineState_Saving
10396 || mData->mMachineState == MachineState_LiveSnapshotting
10397 || mData->mMachineState == MachineState_RestoringSnapshot
10398 || mData->mMachineState == MachineState_DeletingSnapshot
10399 , E_FAIL);
10400
10401 HRESULT rc = S_OK;
10402
10403 // use appropriate locked media map (online or offline)
10404 MediumLockListMap lockedMediaOffline;
10405 MediumLockListMap *lockedMediaMap;
10406 if (aOnline)
10407 lockedMediaMap = &mData->mSession.mLockedMedia;
10408 else
10409 lockedMediaMap = &lockedMediaOffline;
10410
10411 try
10412 {
10413 if (!aOnline)
10414 {
10415 /* lock all attached hard disks early to detect "in use"
10416 * situations before creating actual diffs */
10417 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10418 it != mMediaData->mAttachments.end();
10419 ++it)
10420 {
10421 MediumAttachment* pAtt = *it;
10422 if (pAtt->getType() == DeviceType_HardDisk)
10423 {
10424 Medium* pMedium = pAtt->getMedium();
10425 Assert(pMedium);
10426
10427 MediumLockList *pMediumLockList(new MediumLockList());
10428 alock.release();
10429 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10430 false /* fMediumLockWrite */,
10431 NULL,
10432 *pMediumLockList);
10433 alock.acquire();
10434 if (FAILED(rc))
10435 {
10436 delete pMediumLockList;
10437 throw rc;
10438 }
10439 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10440 if (FAILED(rc))
10441 {
10442 throw setError(rc,
10443 tr("Collecting locking information for all attached media failed"));
10444 }
10445 }
10446 }
10447
10448 /* Now lock all media. If this fails, nothing is locked. */
10449 alock.release();
10450 rc = lockedMediaMap->Lock();
10451 alock.acquire();
10452 if (FAILED(rc))
10453 {
10454 throw setError(rc,
10455 tr("Locking of attached media failed"));
10456 }
10457 }
10458
10459 /* remember the current list (note that we don't use backup() since
10460 * mMediaData may be already backed up) */
10461 MediaData::AttachmentList atts = mMediaData->mAttachments;
10462
10463 /* start from scratch */
10464 mMediaData->mAttachments.clear();
10465
10466 /* go through remembered attachments and create diffs for normal hard
10467 * disks and attach them */
10468 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10469 it != atts.end();
10470 ++it)
10471 {
10472 MediumAttachment* pAtt = *it;
10473
10474 DeviceType_T devType = pAtt->getType();
10475 Medium* pMedium = pAtt->getMedium();
10476
10477 if ( devType != DeviceType_HardDisk
10478 || pMedium == NULL
10479 || pMedium->getType() != MediumType_Normal)
10480 {
10481 /* copy the attachment as is */
10482
10483 /** @todo the progress object created in Console::TakeSnaphot
10484 * only expects operations for hard disks. Later other
10485 * device types need to show up in the progress as well. */
10486 if (devType == DeviceType_HardDisk)
10487 {
10488 if (pMedium == NULL)
10489 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10490 aWeight); // weight
10491 else
10492 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10493 pMedium->getBase()->getName().c_str()).raw(),
10494 aWeight); // weight
10495 }
10496
10497 mMediaData->mAttachments.push_back(pAtt);
10498 continue;
10499 }
10500
10501 /* need a diff */
10502 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10503 pMedium->getBase()->getName().c_str()).raw(),
10504 aWeight); // weight
10505
10506 Utf8Str strFullSnapshotFolder;
10507 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10508
10509 ComObjPtr<Medium> diff;
10510 diff.createObject();
10511 // store the diff in the same registry as the parent
10512 // (this cannot fail here because we can't create implicit diffs for
10513 // unregistered images)
10514 Guid uuidRegistryParent;
10515 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10516 Assert(fInRegistry); NOREF(fInRegistry);
10517 rc = diff->init(mParent,
10518 pMedium->getPreferredDiffFormat(),
10519 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10520 uuidRegistryParent);
10521 if (FAILED(rc)) throw rc;
10522
10523 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10524 * the push_back? Looks like we're going to release medium with the
10525 * wrong kind of lock (general issue with if we fail anywhere at all)
10526 * and an orphaned VDI in the snapshots folder. */
10527
10528 /* update the appropriate lock list */
10529 MediumLockList *pMediumLockList;
10530 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10531 AssertComRCThrowRC(rc);
10532 if (aOnline)
10533 {
10534 alock.release();
10535 /* The currently attached medium will be read-only, change
10536 * the lock type to read. */
10537 rc = pMediumLockList->Update(pMedium, false);
10538 alock.acquire();
10539 AssertComRCThrowRC(rc);
10540 }
10541
10542 /* release the locks before the potentially lengthy operation */
10543 alock.release();
10544 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10545 pMediumLockList,
10546 NULL /* aProgress */,
10547 true /* aWait */);
10548 alock.acquire();
10549 if (FAILED(rc)) throw rc;
10550
10551 rc = lockedMediaMap->Unlock();
10552 AssertComRCThrowRC(rc);
10553 alock.release();
10554 rc = pMediumLockList->Append(diff, true);
10555 alock.acquire();
10556 AssertComRCThrowRC(rc);
10557 alock.release();
10558 rc = lockedMediaMap->Lock();
10559 alock.acquire();
10560 AssertComRCThrowRC(rc);
10561
10562 rc = diff->addBackReference(mData->mUuid);
10563 AssertComRCThrowRC(rc);
10564
10565 /* add a new attachment */
10566 ComObjPtr<MediumAttachment> attachment;
10567 attachment.createObject();
10568 rc = attachment->init(this,
10569 diff,
10570 pAtt->getControllerName(),
10571 pAtt->getPort(),
10572 pAtt->getDevice(),
10573 DeviceType_HardDisk,
10574 true /* aImplicit */,
10575 false /* aPassthrough */,
10576 false /* aTempEject */,
10577 pAtt->getNonRotational(),
10578 pAtt->getDiscard(),
10579 pAtt->getBandwidthGroup());
10580 if (FAILED(rc)) throw rc;
10581
10582 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10583 AssertComRCThrowRC(rc);
10584 mMediaData->mAttachments.push_back(attachment);
10585 }
10586 }
10587 catch (HRESULT aRC) { rc = aRC; }
10588
10589 /* unlock all hard disks we locked when there is no VM */
10590 if (!aOnline)
10591 {
10592 ErrorInfoKeeper eik;
10593
10594 HRESULT rc1 = lockedMediaMap->Clear();
10595 AssertComRC(rc1);
10596 }
10597
10598 return rc;
10599}
10600
10601/**
10602 * Deletes implicit differencing hard disks created either by
10603 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10604 *
10605 * Note that to delete hard disks created by #AttachDevice() this method is
10606 * called from #fixupMedia() when the changes are rolled back.
10607 *
10608 * @note Locks this object and the media tree for writing.
10609 */
10610HRESULT Machine::deleteImplicitDiffs(bool aOnline)
10611{
10612 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10613
10614 AutoCaller autoCaller(this);
10615 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10616
10617 AutoMultiWriteLock2 alock(this->lockHandle(),
10618 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10619
10620 /* We absolutely must have backed up state. */
10621 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10622
10623 /* Check if there are any implicitly created diff images. */
10624 bool fImplicitDiffs = false;
10625 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10626 it != mMediaData->mAttachments.end();
10627 ++it)
10628 {
10629 const ComObjPtr<MediumAttachment> &pAtt = *it;
10630 if (pAtt->isImplicit())
10631 {
10632 fImplicitDiffs = true;
10633 break;
10634 }
10635 }
10636 /* If there is nothing to do, leave early. This saves lots of image locking
10637 * effort. It also avoids a MachineStateChanged event without real reason.
10638 * This is important e.g. when loading a VM config, because there should be
10639 * no events. Otherwise API clients can become thoroughly confused for
10640 * inaccessible VMs (the code for loading VM configs uses this method for
10641 * cleanup if the config makes no sense), as they take such events as an
10642 * indication that the VM is alive, and they would force the VM config to
10643 * be reread, leading to an endless loop. */
10644 if (!fImplicitDiffs)
10645 return S_OK;
10646
10647 HRESULT rc = S_OK;
10648 MachineState_T oldState = mData->mMachineState;
10649
10650 /* will release the lock before the potentially lengthy operation,
10651 * so protect with the special state (unless already protected) */
10652 if ( oldState != MachineState_Saving
10653 && oldState != MachineState_LiveSnapshotting
10654 && oldState != MachineState_RestoringSnapshot
10655 && oldState != MachineState_DeletingSnapshot
10656 && oldState != MachineState_DeletingSnapshotOnline
10657 && oldState != MachineState_DeletingSnapshotPaused
10658 )
10659 setMachineState(MachineState_SettingUp);
10660
10661 // use appropriate locked media map (online or offline)
10662 MediumLockListMap lockedMediaOffline;
10663 MediumLockListMap *lockedMediaMap;
10664 if (aOnline)
10665 lockedMediaMap = &mData->mSession.mLockedMedia;
10666 else
10667 lockedMediaMap = &lockedMediaOffline;
10668
10669 try
10670 {
10671 if (!aOnline)
10672 {
10673 /* lock all attached hard disks early to detect "in use"
10674 * situations before deleting actual diffs */
10675 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10676 it != mMediaData->mAttachments.end();
10677 ++it)
10678 {
10679 MediumAttachment* pAtt = *it;
10680 if (pAtt->getType() == DeviceType_HardDisk)
10681 {
10682 Medium* pMedium = pAtt->getMedium();
10683 Assert(pMedium);
10684
10685 MediumLockList *pMediumLockList(new MediumLockList());
10686 alock.release();
10687 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10688 false /* fMediumLockWrite */,
10689 NULL,
10690 *pMediumLockList);
10691 alock.acquire();
10692
10693 if (FAILED(rc))
10694 {
10695 delete pMediumLockList;
10696 throw rc;
10697 }
10698
10699 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10700 if (FAILED(rc))
10701 throw rc;
10702 }
10703 }
10704
10705 if (FAILED(rc))
10706 throw rc;
10707 } // end of offline
10708
10709 /* Lock lists are now up to date and include implicitly created media */
10710
10711 /* Go through remembered attachments and delete all implicitly created
10712 * diffs and fix up the attachment information */
10713 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10714 MediaData::AttachmentList implicitAtts;
10715 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10716 it != mMediaData->mAttachments.end();
10717 ++it)
10718 {
10719 ComObjPtr<MediumAttachment> pAtt = *it;
10720 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10721 if (pMedium.isNull())
10722 continue;
10723
10724 // Implicit attachments go on the list for deletion and back references are removed.
10725 if (pAtt->isImplicit())
10726 {
10727 /* Deassociate and mark for deletion */
10728 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
10729 rc = pMedium->removeBackReference(mData->mUuid);
10730 if (FAILED(rc))
10731 throw rc;
10732 implicitAtts.push_back(pAtt);
10733 continue;
10734 }
10735
10736 /* Was this medium attached before? */
10737 if (!findAttachment(oldAtts, pMedium))
10738 {
10739 /* no: de-associate */
10740 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
10741 rc = pMedium->removeBackReference(mData->mUuid);
10742 if (FAILED(rc))
10743 throw rc;
10744 continue;
10745 }
10746 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
10747 }
10748
10749 /* If there are implicit attachments to delete, throw away the lock
10750 * map contents (which will unlock all media) since the medium
10751 * attachments will be rolled back. Below we need to completely
10752 * recreate the lock map anyway since it is infinitely complex to
10753 * do this incrementally (would need reconstructing each attachment
10754 * change, which would be extremely hairy). */
10755 if (implicitAtts.size() != 0)
10756 {
10757 ErrorInfoKeeper eik;
10758
10759 HRESULT rc1 = lockedMediaMap->Clear();
10760 AssertComRC(rc1);
10761 }
10762
10763 /* rollback hard disk changes */
10764 mMediaData.rollback();
10765
10766 MultiResult mrc(S_OK);
10767
10768 // Delete unused implicit diffs.
10769 if (implicitAtts.size() != 0)
10770 {
10771 alock.release();
10772
10773 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
10774 it != implicitAtts.end();
10775 ++it)
10776 {
10777 // Remove medium associated with this attachment.
10778 ComObjPtr<MediumAttachment> pAtt = *it;
10779 Assert(pAtt);
10780 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
10781 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10782 Assert(pMedium);
10783
10784 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10785 // continue on delete failure, just collect error messages
10786 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
10787 mrc = rc;
10788 }
10789
10790 alock.acquire();
10791
10792 /* if there is a VM recreate media lock map as mentioned above,
10793 * otherwise it is a waste of time and we leave things unlocked */
10794 if (aOnline)
10795 {
10796 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10797 /* must never be NULL, but better safe than sorry */
10798 if (!pMachine.isNull())
10799 {
10800 alock.release();
10801 rc = mData->mSession.mMachine->lockMedia();
10802 alock.acquire();
10803 if (FAILED(rc))
10804 throw rc;
10805 }
10806 }
10807 }
10808 }
10809 catch (HRESULT aRC) {rc = aRC;}
10810
10811 if (mData->mMachineState == MachineState_SettingUp)
10812 setMachineState(oldState);
10813
10814 /* unlock all hard disks we locked when there is no VM */
10815 if (!aOnline)
10816 {
10817 ErrorInfoKeeper eik;
10818
10819 HRESULT rc1 = lockedMediaMap->Clear();
10820 AssertComRC(rc1);
10821 }
10822
10823 return rc;
10824}
10825
10826
10827/**
10828 * Looks through the given list of media attachments for one with the given parameters
10829 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10830 * can be searched as well if needed.
10831 *
10832 * @param list
10833 * @param aControllerName
10834 * @param aControllerPort
10835 * @param aDevice
10836 * @return
10837 */
10838MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10839 IN_BSTR aControllerName,
10840 LONG aControllerPort,
10841 LONG aDevice)
10842{
10843 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10844 it != ll.end();
10845 ++it)
10846 {
10847 MediumAttachment *pAttach = *it;
10848 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
10849 return pAttach;
10850 }
10851
10852 return NULL;
10853}
10854
10855/**
10856 * Looks through the given list of media attachments for one with the given parameters
10857 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10858 * can be searched as well if needed.
10859 *
10860 * @param list
10861 * @param aControllerName
10862 * @param aControllerPort
10863 * @param aDevice
10864 * @return
10865 */
10866MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10867 ComObjPtr<Medium> pMedium)
10868{
10869 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10870 it != ll.end();
10871 ++it)
10872 {
10873 MediumAttachment *pAttach = *it;
10874 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10875 if (pMediumThis == pMedium)
10876 return pAttach;
10877 }
10878
10879 return NULL;
10880}
10881
10882/**
10883 * Looks through the given list of media attachments for one with the given parameters
10884 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10885 * can be searched as well if needed.
10886 *
10887 * @param list
10888 * @param aControllerName
10889 * @param aControllerPort
10890 * @param aDevice
10891 * @return
10892 */
10893MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10894 Guid &id)
10895{
10896 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10897 it != ll.end();
10898 ++it)
10899 {
10900 MediumAttachment *pAttach = *it;
10901 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10902 if (pMediumThis->getId() == id)
10903 return pAttach;
10904 }
10905
10906 return NULL;
10907}
10908
10909/**
10910 * Main implementation for Machine::DetachDevice. This also gets called
10911 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10912 *
10913 * @param pAttach Medium attachment to detach.
10914 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10915 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
10916 * @return
10917 */
10918HRESULT Machine::detachDevice(MediumAttachment *pAttach,
10919 AutoWriteLock &writeLock,
10920 Snapshot *pSnapshot)
10921{
10922 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
10923 DeviceType_T mediumType = pAttach->getType();
10924
10925 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
10926
10927 if (pAttach->isImplicit())
10928 {
10929 /* attempt to implicitly delete the implicitly created diff */
10930
10931 /// @todo move the implicit flag from MediumAttachment to Medium
10932 /// and forbid any hard disk operation when it is implicit. Or maybe
10933 /// a special media state for it to make it even more simple.
10934
10935 Assert(mMediaData.isBackedUp());
10936
10937 /* will release the lock before the potentially lengthy operation, so
10938 * protect with the special state */
10939 MachineState_T oldState = mData->mMachineState;
10940 setMachineState(MachineState_SettingUp);
10941
10942 writeLock.release();
10943
10944 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
10945 true /*aWait*/);
10946
10947 writeLock.acquire();
10948
10949 setMachineState(oldState);
10950
10951 if (FAILED(rc)) return rc;
10952 }
10953
10954 setModified(IsModified_Storage);
10955 mMediaData.backup();
10956 mMediaData->mAttachments.remove(pAttach);
10957
10958 if (!oldmedium.isNull())
10959 {
10960 // if this is from a snapshot, do not defer detachment to commitMedia()
10961 if (pSnapshot)
10962 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
10963 // else if non-hard disk media, do not defer detachment to commitMedia() either
10964 else if (mediumType != DeviceType_HardDisk)
10965 oldmedium->removeBackReference(mData->mUuid);
10966 }
10967
10968 return S_OK;
10969}
10970
10971/**
10972 * Goes thru all media of the given list and
10973 *
10974 * 1) calls detachDevice() on each of them for this machine and
10975 * 2) adds all Medium objects found in the process to the given list,
10976 * depending on cleanupMode.
10977 *
10978 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10979 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10980 * media to the list.
10981 *
10982 * This gets called from Machine::Unregister, both for the actual Machine and
10983 * the SnapshotMachine objects that might be found in the snapshots.
10984 *
10985 * Requires caller and locking. The machine lock must be passed in because it
10986 * will be passed on to detachDevice which needs it for temporary unlocking.
10987 *
10988 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
10989 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
10990 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
10991 * otherwise no media get added.
10992 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
10993 * @return
10994 */
10995HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
10996 Snapshot *pSnapshot,
10997 CleanupMode_T cleanupMode,
10998 MediaList &llMedia)
10999{
11000 Assert(isWriteLockOnCurrentThread());
11001
11002 HRESULT rc;
11003
11004 // make a temporary list because detachDevice invalidates iterators into
11005 // mMediaData->mAttachments
11006 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11007
11008 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
11009 it != llAttachments2.end();
11010 ++it)
11011 {
11012 ComObjPtr<MediumAttachment> &pAttach = *it;
11013 ComObjPtr<Medium> pMedium = pAttach->getMedium();
11014
11015 if (!pMedium.isNull())
11016 {
11017 AutoCaller mac(pMedium);
11018 if (FAILED(mac.rc())) return mac.rc();
11019 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11020 DeviceType_T devType = pMedium->getDeviceType();
11021 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11022 && devType == DeviceType_HardDisk)
11023 || (cleanupMode == CleanupMode_Full)
11024 )
11025 {
11026 llMedia.push_back(pMedium);
11027 ComObjPtr<Medium> pParent = pMedium->getParent();
11028 /*
11029 * Search for medias which are not attached to any machine, but
11030 * in the chain to an attached disk. Mediums are only consided
11031 * if they are:
11032 * - have only one child
11033 * - no references to any machines
11034 * - are of normal medium type
11035 */
11036 while (!pParent.isNull())
11037 {
11038 AutoCaller mac1(pParent);
11039 if (FAILED(mac1.rc())) return mac1.rc();
11040 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11041 if (pParent->getChildren().size() == 1)
11042 {
11043 if ( pParent->getMachineBackRefCount() == 0
11044 && pParent->getType() == MediumType_Normal
11045 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11046 llMedia.push_back(pParent);
11047 }
11048 else
11049 break;
11050 pParent = pParent->getParent();
11051 }
11052 }
11053 }
11054
11055 // real machine: then we need to use the proper method
11056 rc = detachDevice(pAttach, writeLock, pSnapshot);
11057
11058 if (FAILED(rc))
11059 return rc;
11060 }
11061
11062 return S_OK;
11063}
11064
11065/**
11066 * Perform deferred hard disk detachments.
11067 *
11068 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11069 * backed up).
11070 *
11071 * If @a aOnline is @c true then this method will also unlock the old hard disks
11072 * for which the new implicit diffs were created and will lock these new diffs for
11073 * writing.
11074 *
11075 * @param aOnline Whether the VM was online prior to this operation.
11076 *
11077 * @note Locks this object for writing!
11078 */
11079void Machine::commitMedia(bool aOnline /*= false*/)
11080{
11081 AutoCaller autoCaller(this);
11082 AssertComRCReturnVoid(autoCaller.rc());
11083
11084 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11085
11086 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11087
11088 HRESULT rc = S_OK;
11089
11090 /* no attach/detach operations -- nothing to do */
11091 if (!mMediaData.isBackedUp())
11092 return;
11093
11094 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11095 bool fMediaNeedsLocking = false;
11096
11097 /* enumerate new attachments */
11098 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11099 it != mMediaData->mAttachments.end();
11100 ++it)
11101 {
11102 MediumAttachment *pAttach = *it;
11103
11104 pAttach->commit();
11105
11106 Medium* pMedium = pAttach->getMedium();
11107 bool fImplicit = pAttach->isImplicit();
11108
11109 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11110 (pMedium) ? pMedium->getName().c_str() : "NULL",
11111 fImplicit));
11112
11113 /** @todo convert all this Machine-based voodoo to MediumAttachment
11114 * based commit logic. */
11115 if (fImplicit)
11116 {
11117 /* convert implicit attachment to normal */
11118 pAttach->setImplicit(false);
11119
11120 if ( aOnline
11121 && pMedium
11122 && pAttach->getType() == DeviceType_HardDisk
11123 )
11124 {
11125 ComObjPtr<Medium> parent = pMedium->getParent();
11126 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11127
11128 /* update the appropriate lock list */
11129 MediumLockList *pMediumLockList;
11130 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11131 AssertComRC(rc);
11132 if (pMediumLockList)
11133 {
11134 /* unlock if there's a need to change the locking */
11135 if (!fMediaNeedsLocking)
11136 {
11137 rc = mData->mSession.mLockedMedia.Unlock();
11138 AssertComRC(rc);
11139 fMediaNeedsLocking = true;
11140 }
11141 rc = pMediumLockList->Update(parent, false);
11142 AssertComRC(rc);
11143 rc = pMediumLockList->Append(pMedium, true);
11144 AssertComRC(rc);
11145 }
11146 }
11147
11148 continue;
11149 }
11150
11151 if (pMedium)
11152 {
11153 /* was this medium attached before? */
11154 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11155 oldIt != oldAtts.end();
11156 ++oldIt)
11157 {
11158 MediumAttachment *pOldAttach = *oldIt;
11159 if (pOldAttach->getMedium() == pMedium)
11160 {
11161 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
11162
11163 /* yes: remove from old to avoid de-association */
11164 oldAtts.erase(oldIt);
11165 break;
11166 }
11167 }
11168 }
11169 }
11170
11171 /* enumerate remaining old attachments and de-associate from the
11172 * current machine state */
11173 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11174 it != oldAtts.end();
11175 ++it)
11176 {
11177 MediumAttachment *pAttach = *it;
11178 Medium* pMedium = pAttach->getMedium();
11179
11180 /* Detach only hard disks, since DVD/floppy media is detached
11181 * instantly in MountMedium. */
11182 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
11183 {
11184 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
11185
11186 /* now de-associate from the current machine state */
11187 rc = pMedium->removeBackReference(mData->mUuid);
11188 AssertComRC(rc);
11189
11190 if (aOnline)
11191 {
11192 /* unlock since medium is not used anymore */
11193 MediumLockList *pMediumLockList;
11194 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11195 AssertComRC(rc);
11196 if (pMediumLockList)
11197 {
11198 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11199 AssertComRC(rc);
11200 }
11201 }
11202 }
11203 }
11204
11205 /* take media locks again so that the locking state is consistent */
11206 if (fMediaNeedsLocking)
11207 {
11208 Assert(aOnline);
11209 rc = mData->mSession.mLockedMedia.Lock();
11210 AssertComRC(rc);
11211 }
11212
11213 /* commit the hard disk changes */
11214 mMediaData.commit();
11215
11216 if (isSessionMachine())
11217 {
11218 /*
11219 * Update the parent machine to point to the new owner.
11220 * This is necessary because the stored parent will point to the
11221 * session machine otherwise and cause crashes or errors later
11222 * when the session machine gets invalid.
11223 */
11224 /** @todo Change the MediumAttachment class to behave like any other
11225 * class in this regard by creating peer MediumAttachment
11226 * objects for session machines and share the data with the peer
11227 * machine.
11228 */
11229 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11230 it != mMediaData->mAttachments.end();
11231 ++it)
11232 {
11233 (*it)->updateParentMachine(mPeer);
11234 }
11235
11236 /* attach new data to the primary machine and reshare it */
11237 mPeer->mMediaData.attach(mMediaData);
11238 }
11239
11240 return;
11241}
11242
11243/**
11244 * Perform deferred deletion of implicitly created diffs.
11245 *
11246 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11247 * backed up).
11248 *
11249 * @note Locks this object for writing!
11250 */
11251void Machine::rollbackMedia()
11252{
11253 AutoCaller autoCaller(this);
11254 AssertComRCReturnVoid(autoCaller.rc());
11255
11256 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11257 LogFlowThisFunc(("Entering rollbackMedia\n"));
11258
11259 HRESULT rc = S_OK;
11260
11261 /* no attach/detach operations -- nothing to do */
11262 if (!mMediaData.isBackedUp())
11263 return;
11264
11265 /* enumerate new attachments */
11266 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11267 it != mMediaData->mAttachments.end();
11268 ++it)
11269 {
11270 MediumAttachment *pAttach = *it;
11271 /* Fix up the backrefs for DVD/floppy media. */
11272 if (pAttach->getType() != DeviceType_HardDisk)
11273 {
11274 Medium* pMedium = pAttach->getMedium();
11275 if (pMedium)
11276 {
11277 rc = pMedium->removeBackReference(mData->mUuid);
11278 AssertComRC(rc);
11279 }
11280 }
11281
11282 (*it)->rollback();
11283
11284 pAttach = *it;
11285 /* Fix up the backrefs for DVD/floppy media. */
11286 if (pAttach->getType() != DeviceType_HardDisk)
11287 {
11288 Medium* pMedium = pAttach->getMedium();
11289 if (pMedium)
11290 {
11291 rc = pMedium->addBackReference(mData->mUuid);
11292 AssertComRC(rc);
11293 }
11294 }
11295 }
11296
11297 /** @todo convert all this Machine-based voodoo to MediumAttachment
11298 * based rollback logic. */
11299 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11300
11301 return;
11302}
11303
11304/**
11305 * Returns true if the settings file is located in the directory named exactly
11306 * as the machine; this means, among other things, that the machine directory
11307 * should be auto-renamed.
11308 *
11309 * @param aSettingsDir if not NULL, the full machine settings file directory
11310 * name will be assigned there.
11311 *
11312 * @note Doesn't lock anything.
11313 * @note Not thread safe (must be called from this object's lock).
11314 */
11315bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11316{
11317 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11318 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11319 if (aSettingsDir)
11320 *aSettingsDir = strMachineDirName;
11321 strMachineDirName.stripPath(); // vmname
11322 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11323 strConfigFileOnly.stripPath() // vmname.vbox
11324 .stripExt(); // vmname
11325 /** @todo hack, make somehow use of ComposeMachineFilename */
11326 if (mUserData->s.fDirectoryIncludesUUID)
11327 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11328
11329 AssertReturn(!strMachineDirName.isEmpty(), false);
11330 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11331
11332 return strMachineDirName == strConfigFileOnly;
11333}
11334
11335/**
11336 * Discards all changes to machine settings.
11337 *
11338 * @param aNotify Whether to notify the direct session about changes or not.
11339 *
11340 * @note Locks objects for writing!
11341 */
11342void Machine::rollback(bool aNotify)
11343{
11344 AutoCaller autoCaller(this);
11345 AssertComRCReturn(autoCaller.rc(), (void)0);
11346
11347 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11348
11349 if (!mStorageControllers.isNull())
11350 {
11351 if (mStorageControllers.isBackedUp())
11352 {
11353 /* unitialize all new devices (absent in the backed up list). */
11354 StorageControllerList::const_iterator it = mStorageControllers->begin();
11355 StorageControllerList *backedList = mStorageControllers.backedUpData();
11356 while (it != mStorageControllers->end())
11357 {
11358 if ( std::find(backedList->begin(), backedList->end(), *it)
11359 == backedList->end()
11360 )
11361 {
11362 (*it)->uninit();
11363 }
11364 ++it;
11365 }
11366
11367 /* restore the list */
11368 mStorageControllers.rollback();
11369 }
11370
11371 /* rollback any changes to devices after restoring the list */
11372 if (mData->flModifications & IsModified_Storage)
11373 {
11374 StorageControllerList::const_iterator it = mStorageControllers->begin();
11375 while (it != mStorageControllers->end())
11376 {
11377 (*it)->rollback();
11378 ++it;
11379 }
11380 }
11381 }
11382
11383 mUserData.rollback();
11384
11385 mHWData.rollback();
11386
11387 if (mData->flModifications & IsModified_Storage)
11388 rollbackMedia();
11389
11390 if (mBIOSSettings)
11391 mBIOSSettings->rollback();
11392
11393 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11394 mVRDEServer->rollback();
11395
11396 if (mAudioAdapter)
11397 mAudioAdapter->rollback();
11398
11399 if (mUSBController && (mData->flModifications & IsModified_USB))
11400 mUSBController->rollback();
11401
11402 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11403 mBandwidthControl->rollback();
11404
11405 if (!mHWData.isNull())
11406 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11407 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11408 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11409 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11410
11411 if (mData->flModifications & IsModified_NetworkAdapters)
11412 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11413 if ( mNetworkAdapters[slot]
11414 && mNetworkAdapters[slot]->isModified())
11415 {
11416 mNetworkAdapters[slot]->rollback();
11417 networkAdapters[slot] = mNetworkAdapters[slot];
11418 }
11419
11420 if (mData->flModifications & IsModified_SerialPorts)
11421 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11422 if ( mSerialPorts[slot]
11423 && mSerialPorts[slot]->isModified())
11424 {
11425 mSerialPorts[slot]->rollback();
11426 serialPorts[slot] = mSerialPorts[slot];
11427 }
11428
11429 if (mData->flModifications & IsModified_ParallelPorts)
11430 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11431 if ( mParallelPorts[slot]
11432 && mParallelPorts[slot]->isModified())
11433 {
11434 mParallelPorts[slot]->rollback();
11435 parallelPorts[slot] = mParallelPorts[slot];
11436 }
11437
11438 if (aNotify)
11439 {
11440 /* inform the direct session about changes */
11441
11442 ComObjPtr<Machine> that = this;
11443 uint32_t flModifications = mData->flModifications;
11444 alock.release();
11445
11446 if (flModifications & IsModified_SharedFolders)
11447 that->onSharedFolderChange();
11448
11449 if (flModifications & IsModified_VRDEServer)
11450 that->onVRDEServerChange(/* aRestart */ TRUE);
11451 if (flModifications & IsModified_USB)
11452 that->onUSBControllerChange();
11453
11454 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
11455 if (networkAdapters[slot])
11456 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
11457 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
11458 if (serialPorts[slot])
11459 that->onSerialPortChange(serialPorts[slot]);
11460 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
11461 if (parallelPorts[slot])
11462 that->onParallelPortChange(parallelPorts[slot]);
11463
11464 if (flModifications & IsModified_Storage)
11465 that->onStorageControllerChange();
11466
11467#if 0
11468 if (flModifications & IsModified_BandwidthControl)
11469 that->onBandwidthControlChange();
11470#endif
11471 }
11472}
11473
11474/**
11475 * Commits all the changes to machine settings.
11476 *
11477 * Note that this operation is supposed to never fail.
11478 *
11479 * @note Locks this object and children for writing.
11480 */
11481void Machine::commit()
11482{
11483 AutoCaller autoCaller(this);
11484 AssertComRCReturnVoid(autoCaller.rc());
11485
11486 AutoCaller peerCaller(mPeer);
11487 AssertComRCReturnVoid(peerCaller.rc());
11488
11489 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11490
11491 /*
11492 * use safe commit to ensure Snapshot machines (that share mUserData)
11493 * will still refer to a valid memory location
11494 */
11495 mUserData.commitCopy();
11496
11497 mHWData.commit();
11498
11499 if (mMediaData.isBackedUp())
11500 commitMedia(Global::IsOnline(mData->mMachineState));
11501
11502 mBIOSSettings->commit();
11503 mVRDEServer->commit();
11504 mAudioAdapter->commit();
11505 mUSBController->commit();
11506 mBandwidthControl->commit();
11507
11508 /* Since mNetworkAdapters is a list which might have been changed (resized)
11509 * without using the Backupable<> template we need to handle the copying
11510 * of the list entries manually, including the creation of peers for the
11511 * new objects. */
11512 bool commitNetworkAdapters = false;
11513 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11514 if (mPeer)
11515 {
11516 /* commit everything, even the ones which will go away */
11517 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11518 mNetworkAdapters[slot]->commit();
11519 /* copy over the new entries, creating a peer and uninit the original */
11520 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11521 for (size_t slot = 0; slot < newSize; slot++)
11522 {
11523 /* look if this adapter has a peer device */
11524 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
11525 if (!peer)
11526 {
11527 /* no peer means the adapter is a newly created one;
11528 * create a peer owning data this data share it with */
11529 peer.createObject();
11530 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11531 }
11532 mPeer->mNetworkAdapters[slot] = peer;
11533 }
11534 /* uninit any no longer needed network adapters */
11535 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
11536 mNetworkAdapters[slot]->uninit();
11537 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
11538 {
11539 if (mPeer->mNetworkAdapters[slot])
11540 mPeer->mNetworkAdapters[slot]->uninit();
11541 }
11542 /* Keep the original network adapter count until this point, so that
11543 * discarding a chipset type change will not lose settings. */
11544 mNetworkAdapters.resize(newSize);
11545 mPeer->mNetworkAdapters.resize(newSize);
11546 }
11547 else
11548 {
11549 /* we have no peer (our parent is the newly created machine);
11550 * just commit changes to the network adapters */
11551 commitNetworkAdapters = true;
11552 }
11553 if (commitNetworkAdapters)
11554 {
11555 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11556 mNetworkAdapters[slot]->commit();
11557 }
11558
11559 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11560 mSerialPorts[slot]->commit();
11561 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11562 mParallelPorts[slot]->commit();
11563
11564 bool commitStorageControllers = false;
11565
11566 if (mStorageControllers.isBackedUp())
11567 {
11568 mStorageControllers.commit();
11569
11570 if (mPeer)
11571 {
11572 /* Commit all changes to new controllers (this will reshare data with
11573 * peers for those who have peers) */
11574 StorageControllerList *newList = new StorageControllerList();
11575 StorageControllerList::const_iterator it = mStorageControllers->begin();
11576 while (it != mStorageControllers->end())
11577 {
11578 (*it)->commit();
11579
11580 /* look if this controller has a peer device */
11581 ComObjPtr<StorageController> peer = (*it)->getPeer();
11582 if (!peer)
11583 {
11584 /* no peer means the device is a newly created one;
11585 * create a peer owning data this device share it with */
11586 peer.createObject();
11587 peer->init(mPeer, *it, true /* aReshare */);
11588 }
11589 else
11590 {
11591 /* remove peer from the old list */
11592 mPeer->mStorageControllers->remove(peer);
11593 }
11594 /* and add it to the new list */
11595 newList->push_back(peer);
11596
11597 ++it;
11598 }
11599
11600 /* uninit old peer's controllers that are left */
11601 it = mPeer->mStorageControllers->begin();
11602 while (it != mPeer->mStorageControllers->end())
11603 {
11604 (*it)->uninit();
11605 ++it;
11606 }
11607
11608 /* attach new list of controllers to our peer */
11609 mPeer->mStorageControllers.attach(newList);
11610 }
11611 else
11612 {
11613 /* we have no peer (our parent is the newly created machine);
11614 * just commit changes to devices */
11615 commitStorageControllers = true;
11616 }
11617 }
11618 else
11619 {
11620 /* the list of controllers itself is not changed,
11621 * just commit changes to controllers themselves */
11622 commitStorageControllers = true;
11623 }
11624
11625 if (commitStorageControllers)
11626 {
11627 StorageControllerList::const_iterator it = mStorageControllers->begin();
11628 while (it != mStorageControllers->end())
11629 {
11630 (*it)->commit();
11631 ++it;
11632 }
11633 }
11634
11635 if (isSessionMachine())
11636 {
11637 /* attach new data to the primary machine and reshare it */
11638 mPeer->mUserData.attach(mUserData);
11639 mPeer->mHWData.attach(mHWData);
11640 /* mMediaData is reshared by fixupMedia */
11641 // mPeer->mMediaData.attach(mMediaData);
11642 Assert(mPeer->mMediaData.data() == mMediaData.data());
11643 }
11644}
11645
11646/**
11647 * Copies all the hardware data from the given machine.
11648 *
11649 * Currently, only called when the VM is being restored from a snapshot. In
11650 * particular, this implies that the VM is not running during this method's
11651 * call.
11652 *
11653 * @note This method must be called from under this object's lock.
11654 *
11655 * @note This method doesn't call #commit(), so all data remains backed up and
11656 * unsaved.
11657 */
11658void Machine::copyFrom(Machine *aThat)
11659{
11660 AssertReturnVoid(!isSnapshotMachine());
11661 AssertReturnVoid(aThat->isSnapshotMachine());
11662
11663 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11664
11665 mHWData.assignCopy(aThat->mHWData);
11666
11667 // create copies of all shared folders (mHWData after attaching a copy
11668 // contains just references to original objects)
11669 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11670 it != mHWData->mSharedFolders.end();
11671 ++it)
11672 {
11673 ComObjPtr<SharedFolder> folder;
11674 folder.createObject();
11675 HRESULT rc = folder->initCopy(getMachine(), *it);
11676 AssertComRC(rc);
11677 *it = folder;
11678 }
11679
11680 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
11681 mVRDEServer->copyFrom(aThat->mVRDEServer);
11682 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
11683 mUSBController->copyFrom(aThat->mUSBController);
11684 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
11685
11686 /* create private copies of all controllers */
11687 mStorageControllers.backup();
11688 mStorageControllers->clear();
11689 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11690 it != aThat->mStorageControllers->end();
11691 ++it)
11692 {
11693 ComObjPtr<StorageController> ctrl;
11694 ctrl.createObject();
11695 ctrl->initCopy(this, *it);
11696 mStorageControllers->push_back(ctrl);
11697 }
11698
11699 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11700 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11701 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
11702 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11703 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
11704 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11705 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
11706}
11707
11708/**
11709 * Returns whether the given storage controller is hotplug capable.
11710 *
11711 * @returns true if the controller supports hotplugging
11712 * false otherwise.
11713 * @param enmCtrlType The controller type to check for.
11714 */
11715bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11716{
11717 switch (enmCtrlType)
11718 {
11719 case StorageControllerType_IntelAhci:
11720 return true;
11721 case StorageControllerType_LsiLogic:
11722 case StorageControllerType_LsiLogicSas:
11723 case StorageControllerType_BusLogic:
11724 case StorageControllerType_PIIX3:
11725 case StorageControllerType_PIIX4:
11726 case StorageControllerType_ICH6:
11727 case StorageControllerType_I82078:
11728 default:
11729 return false;
11730 }
11731}
11732
11733#ifdef VBOX_WITH_RESOURCE_USAGE_API
11734
11735void Machine::getDiskList(MediaList &list)
11736{
11737 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11738 it != mMediaData->mAttachments.end();
11739 ++it)
11740 {
11741 MediumAttachment* pAttach = *it;
11742 /* just in case */
11743 AssertStmt(pAttach, continue);
11744
11745 AutoCaller localAutoCallerA(pAttach);
11746 if (FAILED(localAutoCallerA.rc())) continue;
11747
11748 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11749
11750 if (pAttach->getType() == DeviceType_HardDisk)
11751 list.push_back(pAttach->getMedium());
11752 }
11753}
11754
11755void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11756{
11757 AssertReturnVoid(isWriteLockOnCurrentThread());
11758 AssertPtrReturnVoid(aCollector);
11759
11760 pm::CollectorHAL *hal = aCollector->getHAL();
11761 /* Create sub metrics */
11762 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11763 "Percentage of processor time spent in user mode by the VM process.");
11764 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11765 "Percentage of processor time spent in kernel mode by the VM process.");
11766 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11767 "Size of resident portion of VM process in memory.");
11768 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11769 "Actual size of all VM disks combined.");
11770 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11771 "Network receive rate.");
11772 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11773 "Network transmit rate.");
11774 /* Create and register base metrics */
11775 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11776 cpuLoadUser, cpuLoadKernel);
11777 aCollector->registerBaseMetric(cpuLoad);
11778 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11779 ramUsageUsed);
11780 aCollector->registerBaseMetric(ramUsage);
11781 MediaList disks;
11782 getDiskList(disks);
11783 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11784 diskUsageUsed);
11785 aCollector->registerBaseMetric(diskUsage);
11786
11787 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11788 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11789 new pm::AggregateAvg()));
11790 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11791 new pm::AggregateMin()));
11792 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11793 new pm::AggregateMax()));
11794 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11795 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11796 new pm::AggregateAvg()));
11797 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11798 new pm::AggregateMin()));
11799 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11800 new pm::AggregateMax()));
11801
11802 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11803 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11804 new pm::AggregateAvg()));
11805 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11806 new pm::AggregateMin()));
11807 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11808 new pm::AggregateMax()));
11809
11810 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11811 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11812 new pm::AggregateAvg()));
11813 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11814 new pm::AggregateMin()));
11815 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11816 new pm::AggregateMax()));
11817
11818
11819 /* Guest metrics collector */
11820 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11821 aCollector->registerGuest(mCollectorGuest);
11822 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11823 this, __PRETTY_FUNCTION__, mCollectorGuest));
11824
11825 /* Create sub metrics */
11826 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11827 "Percentage of processor time spent in user mode as seen by the guest.");
11828 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11829 "Percentage of processor time spent in kernel mode as seen by the guest.");
11830 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11831 "Percentage of processor time spent idling as seen by the guest.");
11832
11833 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11834 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11835 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11836 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11837 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11838 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11839
11840 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11841
11842 /* Create and register base metrics */
11843 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
11844 machineNetRx, machineNetTx);
11845 aCollector->registerBaseMetric(machineNetRate);
11846
11847 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11848 guestLoadUser, guestLoadKernel, guestLoadIdle);
11849 aCollector->registerBaseMetric(guestCpuLoad);
11850
11851 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11852 guestMemTotal, guestMemFree,
11853 guestMemBalloon, guestMemShared,
11854 guestMemCache, guestPagedTotal);
11855 aCollector->registerBaseMetric(guestCpuMem);
11856
11857 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
11858 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
11859 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
11860 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
11861
11862 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
11863 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
11864 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
11865 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
11866
11867 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11868 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11869 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11870 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11871
11872 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11873 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11874 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11875 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11876
11877 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11878 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11879 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11880 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11881
11882 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11883 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11884 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11885 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11886
11887 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11888 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11889 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11890 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11891
11892 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11893 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11894 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11895 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11896
11897 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11898 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11899 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11900 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11901
11902 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11903 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11904 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11905 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11906
11907 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11908 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11909 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11910 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11911}
11912
11913void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11914{
11915 AssertReturnVoid(isWriteLockOnCurrentThread());
11916
11917 if (aCollector)
11918 {
11919 aCollector->unregisterMetricsFor(aMachine);
11920 aCollector->unregisterBaseMetricsFor(aMachine);
11921 }
11922}
11923
11924#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11925
11926
11927////////////////////////////////////////////////////////////////////////////////
11928
11929DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11930
11931HRESULT SessionMachine::FinalConstruct()
11932{
11933 LogFlowThisFunc(("\n"));
11934
11935#if defined(RT_OS_WINDOWS)
11936 mIPCSem = NULL;
11937#elif defined(RT_OS_OS2)
11938 mIPCSem = NULLHANDLE;
11939#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11940 mIPCSem = -1;
11941#else
11942# error "Port me!"
11943#endif
11944
11945 return BaseFinalConstruct();
11946}
11947
11948void SessionMachine::FinalRelease()
11949{
11950 LogFlowThisFunc(("\n"));
11951
11952 uninit(Uninit::Unexpected);
11953
11954 BaseFinalRelease();
11955}
11956
11957/**
11958 * @note Must be called only by Machine::openSession() from its own write lock.
11959 */
11960HRESULT SessionMachine::init(Machine *aMachine)
11961{
11962 LogFlowThisFuncEnter();
11963 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
11964
11965 AssertReturn(aMachine, E_INVALIDARG);
11966
11967 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
11968
11969 /* Enclose the state transition NotReady->InInit->Ready */
11970 AutoInitSpan autoInitSpan(this);
11971 AssertReturn(autoInitSpan.isOk(), E_FAIL);
11972
11973 /* create the interprocess semaphore */
11974#if defined(RT_OS_WINDOWS)
11975 mIPCSemName = aMachine->mData->m_strConfigFileFull;
11976 for (size_t i = 0; i < mIPCSemName.length(); i++)
11977 if (mIPCSemName.raw()[i] == '\\')
11978 mIPCSemName.raw()[i] = '/';
11979 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
11980 ComAssertMsgRet(mIPCSem,
11981 ("Cannot create IPC mutex '%ls', err=%d",
11982 mIPCSemName.raw(), ::GetLastError()),
11983 E_FAIL);
11984#elif defined(RT_OS_OS2)
11985 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
11986 aMachine->mData->mUuid.raw());
11987 mIPCSemName = ipcSem;
11988 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
11989 ComAssertMsgRet(arc == NO_ERROR,
11990 ("Cannot create IPC mutex '%s', arc=%ld",
11991 ipcSem.c_str(), arc),
11992 E_FAIL);
11993#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11994# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11995# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
11996 /** @todo Check that this still works correctly. */
11997 AssertCompileSize(key_t, 8);
11998# else
11999 AssertCompileSize(key_t, 4);
12000# endif
12001 key_t key;
12002 mIPCSem = -1;
12003 mIPCKey = "0";
12004 for (uint32_t i = 0; i < 1 << 24; i++)
12005 {
12006 key = ((uint32_t)'V' << 24) | i;
12007 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
12008 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
12009 {
12010 mIPCSem = sem;
12011 if (sem >= 0)
12012 mIPCKey = BstrFmt("%u", key);
12013 break;
12014 }
12015 }
12016# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12017 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
12018 char *pszSemName = NULL;
12019 RTStrUtf8ToCurrentCP(&pszSemName, semName);
12020 key_t key = ::ftok(pszSemName, 'V');
12021 RTStrFree(pszSemName);
12022
12023 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
12024# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12025
12026 int errnoSave = errno;
12027 if (mIPCSem < 0 && errnoSave == ENOSYS)
12028 {
12029 setError(E_FAIL,
12030 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
12031 "support for SysV IPC. Check the host kernel configuration for "
12032 "CONFIG_SYSVIPC=y"));
12033 return E_FAIL;
12034 }
12035 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
12036 * the IPC semaphores */
12037 if (mIPCSem < 0 && errnoSave == ENOSPC)
12038 {
12039#ifdef RT_OS_LINUX
12040 setError(E_FAIL,
12041 tr("Cannot create IPC semaphore because the system limit for the "
12042 "maximum number of semaphore sets (SEMMNI), or the system wide "
12043 "maximum number of semaphores (SEMMNS) would be exceeded. The "
12044 "current set of SysV IPC semaphores can be determined from "
12045 "the file /proc/sysvipc/sem"));
12046#else
12047 setError(E_FAIL,
12048 tr("Cannot create IPC semaphore because the system-imposed limit "
12049 "on the maximum number of allowed semaphores or semaphore "
12050 "identifiers system-wide would be exceeded"));
12051#endif
12052 return E_FAIL;
12053 }
12054 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
12055 E_FAIL);
12056 /* set the initial value to 1 */
12057 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
12058 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
12059 E_FAIL);
12060#else
12061# error "Port me!"
12062#endif
12063
12064 /* memorize the peer Machine */
12065 unconst(mPeer) = aMachine;
12066 /* share the parent pointer */
12067 unconst(mParent) = aMachine->mParent;
12068
12069 /* take the pointers to data to share */
12070 mData.share(aMachine->mData);
12071 mSSData.share(aMachine->mSSData);
12072
12073 mUserData.share(aMachine->mUserData);
12074 mHWData.share(aMachine->mHWData);
12075 mMediaData.share(aMachine->mMediaData);
12076
12077 mStorageControllers.allocate();
12078 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12079 it != aMachine->mStorageControllers->end();
12080 ++it)
12081 {
12082 ComObjPtr<StorageController> ctl;
12083 ctl.createObject();
12084 ctl->init(this, *it);
12085 mStorageControllers->push_back(ctl);
12086 }
12087
12088 unconst(mBIOSSettings).createObject();
12089 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12090 /* create another VRDEServer object that will be mutable */
12091 unconst(mVRDEServer).createObject();
12092 mVRDEServer->init(this, aMachine->mVRDEServer);
12093 /* create another audio adapter object that will be mutable */
12094 unconst(mAudioAdapter).createObject();
12095 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12096 /* create a list of serial ports that will be mutable */
12097 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12098 {
12099 unconst(mSerialPorts[slot]).createObject();
12100 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12101 }
12102 /* create a list of parallel ports that will be mutable */
12103 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12104 {
12105 unconst(mParallelPorts[slot]).createObject();
12106 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12107 }
12108 /* create another USB controller object that will be mutable */
12109 unconst(mUSBController).createObject();
12110 mUSBController->init(this, aMachine->mUSBController);
12111
12112 /* create a list of network adapters that will be mutable */
12113 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12114 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12115 {
12116 unconst(mNetworkAdapters[slot]).createObject();
12117 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12118 }
12119
12120 /* create another bandwidth control object that will be mutable */
12121 unconst(mBandwidthControl).createObject();
12122 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12123
12124 /* default is to delete saved state on Saved -> PoweredOff transition */
12125 mRemoveSavedState = true;
12126
12127 /* Confirm a successful initialization when it's the case */
12128 autoInitSpan.setSucceeded();
12129
12130 LogFlowThisFuncLeave();
12131 return S_OK;
12132}
12133
12134/**
12135 * Uninitializes this session object. If the reason is other than
12136 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
12137 *
12138 * @param aReason uninitialization reason
12139 *
12140 * @note Locks mParent + this object for writing.
12141 */
12142void SessionMachine::uninit(Uninit::Reason aReason)
12143{
12144 LogFlowThisFuncEnter();
12145 LogFlowThisFunc(("reason=%d\n", aReason));
12146
12147 /*
12148 * Strongly reference ourselves to prevent this object deletion after
12149 * mData->mSession.mMachine.setNull() below (which can release the last
12150 * reference and call the destructor). Important: this must be done before
12151 * accessing any members (and before AutoUninitSpan that does it as well).
12152 * This self reference will be released as the very last step on return.
12153 */
12154 ComObjPtr<SessionMachine> selfRef = this;
12155
12156 /* Enclose the state transition Ready->InUninit->NotReady */
12157 AutoUninitSpan autoUninitSpan(this);
12158 if (autoUninitSpan.uninitDone())
12159 {
12160 LogFlowThisFunc(("Already uninitialized\n"));
12161 LogFlowThisFuncLeave();
12162 return;
12163 }
12164
12165 if (autoUninitSpan.initFailed())
12166 {
12167 /* We've been called by init() because it's failed. It's not really
12168 * necessary (nor it's safe) to perform the regular uninit sequence
12169 * below, the following is enough.
12170 */
12171 LogFlowThisFunc(("Initialization failed.\n"));
12172#if defined(RT_OS_WINDOWS)
12173 if (mIPCSem)
12174 ::CloseHandle(mIPCSem);
12175 mIPCSem = NULL;
12176#elif defined(RT_OS_OS2)
12177 if (mIPCSem != NULLHANDLE)
12178 ::DosCloseMutexSem(mIPCSem);
12179 mIPCSem = NULLHANDLE;
12180#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12181 if (mIPCSem >= 0)
12182 ::semctl(mIPCSem, 0, IPC_RMID);
12183 mIPCSem = -1;
12184# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12185 mIPCKey = "0";
12186# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12187#else
12188# error "Port me!"
12189#endif
12190 uninitDataAndChildObjects();
12191 mData.free();
12192 unconst(mParent) = NULL;
12193 unconst(mPeer) = NULL;
12194 LogFlowThisFuncLeave();
12195 return;
12196 }
12197
12198 MachineState_T lastState;
12199 {
12200 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12201 lastState = mData->mMachineState;
12202 }
12203 NOREF(lastState);
12204
12205#ifdef VBOX_WITH_USB
12206 // release all captured USB devices, but do this before requesting the locks below
12207 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12208 {
12209 /* Console::captureUSBDevices() is called in the VM process only after
12210 * setting the machine state to Starting or Restoring.
12211 * Console::detachAllUSBDevices() will be called upon successful
12212 * termination. So, we need to release USB devices only if there was
12213 * an abnormal termination of a running VM.
12214 *
12215 * This is identical to SessionMachine::DetachAllUSBDevices except
12216 * for the aAbnormal argument. */
12217 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12218 AssertComRC(rc);
12219 NOREF(rc);
12220
12221 USBProxyService *service = mParent->host()->usbProxyService();
12222 if (service)
12223 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12224 }
12225#endif /* VBOX_WITH_USB */
12226
12227 // we need to lock this object in uninit() because the lock is shared
12228 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12229 // and others need mParent lock, and USB needs host lock.
12230 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12231
12232#if 0
12233 // Trigger async cleanup tasks, avoid doing things here which are not
12234 // vital to be done immediately and maybe need more locks. This calls
12235 // Machine::unregisterMetrics().
12236 mParent->onMachineUninit(mPeer);
12237#else
12238 /*
12239 * It is safe to call Machine::unregisterMetrics() here because
12240 * PerformanceCollector::samplerCallback no longer accesses guest methods
12241 * holding the lock.
12242 */
12243 unregisterMetrics(mParent->performanceCollector(), mPeer);
12244#endif
12245 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12246 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12247 this, __PRETTY_FUNCTION__, mCollectorGuest));
12248 if (mCollectorGuest)
12249 {
12250 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
12251 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12252 mCollectorGuest = NULL;
12253 }
12254
12255 if (aReason == Uninit::Abnormal)
12256 {
12257 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12258 Global::IsOnlineOrTransient(lastState)));
12259
12260 /* reset the state to Aborted */
12261 if (mData->mMachineState != MachineState_Aborted)
12262 setMachineState(MachineState_Aborted);
12263 }
12264
12265 // any machine settings modified?
12266 if (mData->flModifications)
12267 {
12268 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12269 rollback(false /* aNotify */);
12270 }
12271
12272 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12273 || !mConsoleTaskData.mSnapshot);
12274 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12275 {
12276 LogWarningThisFunc(("canceling failed save state request!\n"));
12277 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12278 }
12279 else if (!mConsoleTaskData.mSnapshot.isNull())
12280 {
12281 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12282
12283 /* delete all differencing hard disks created (this will also attach
12284 * their parents back by rolling back mMediaData) */
12285 rollbackMedia();
12286
12287 // delete the saved state file (it might have been already created)
12288 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12289 // think it's still in use
12290 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12291 mConsoleTaskData.mSnapshot->uninit();
12292 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12293 }
12294
12295 if (!mData->mSession.mType.isEmpty())
12296 {
12297 /* mType is not null when this machine's process has been started by
12298 * Machine::LaunchVMProcess(), therefore it is our child. We
12299 * need to queue the PID to reap the process (and avoid zombies on
12300 * Linux). */
12301 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12302 mParent->addProcessToReap(mData->mSession.mPID);
12303 }
12304
12305 mData->mSession.mPID = NIL_RTPROCESS;
12306
12307 if (aReason == Uninit::Unexpected)
12308 {
12309 /* Uninitialization didn't come from #checkForDeath(), so tell the
12310 * client watcher thread to update the set of machines that have open
12311 * sessions. */
12312 mParent->updateClientWatcher();
12313 }
12314
12315 /* uninitialize all remote controls */
12316 if (mData->mSession.mRemoteControls.size())
12317 {
12318 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12319 mData->mSession.mRemoteControls.size()));
12320
12321 Data::Session::RemoteControlList::iterator it =
12322 mData->mSession.mRemoteControls.begin();
12323 while (it != mData->mSession.mRemoteControls.end())
12324 {
12325 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12326 HRESULT rc = (*it)->Uninitialize();
12327 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12328 if (FAILED(rc))
12329 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12330 ++it;
12331 }
12332 mData->mSession.mRemoteControls.clear();
12333 }
12334
12335 /*
12336 * An expected uninitialization can come only from #checkForDeath().
12337 * Otherwise it means that something's gone really wrong (for example,
12338 * the Session implementation has released the VirtualBox reference
12339 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12340 * etc). However, it's also possible, that the client releases the IPC
12341 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12342 * but the VirtualBox release event comes first to the server process.
12343 * This case is practically possible, so we should not assert on an
12344 * unexpected uninit, just log a warning.
12345 */
12346
12347 if ((aReason == Uninit::Unexpected))
12348 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12349
12350 if (aReason != Uninit::Normal)
12351 {
12352 mData->mSession.mDirectControl.setNull();
12353 }
12354 else
12355 {
12356 /* this must be null here (see #OnSessionEnd()) */
12357 Assert(mData->mSession.mDirectControl.isNull());
12358 Assert(mData->mSession.mState == SessionState_Unlocking);
12359 Assert(!mData->mSession.mProgress.isNull());
12360 }
12361 if (mData->mSession.mProgress)
12362 {
12363 if (aReason == Uninit::Normal)
12364 mData->mSession.mProgress->notifyComplete(S_OK);
12365 else
12366 mData->mSession.mProgress->notifyComplete(E_FAIL,
12367 COM_IIDOF(ISession),
12368 getComponentName(),
12369 tr("The VM session was aborted"));
12370 mData->mSession.mProgress.setNull();
12371 }
12372
12373 /* remove the association between the peer machine and this session machine */
12374 Assert( (SessionMachine*)mData->mSession.mMachine == this
12375 || aReason == Uninit::Unexpected);
12376
12377 /* reset the rest of session data */
12378 mData->mSession.mMachine.setNull();
12379 mData->mSession.mState = SessionState_Unlocked;
12380 mData->mSession.mType.setNull();
12381
12382 /* close the interprocess semaphore before leaving the exclusive lock */
12383#if defined(RT_OS_WINDOWS)
12384 if (mIPCSem)
12385 ::CloseHandle(mIPCSem);
12386 mIPCSem = NULL;
12387#elif defined(RT_OS_OS2)
12388 if (mIPCSem != NULLHANDLE)
12389 ::DosCloseMutexSem(mIPCSem);
12390 mIPCSem = NULLHANDLE;
12391#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12392 if (mIPCSem >= 0)
12393 ::semctl(mIPCSem, 0, IPC_RMID);
12394 mIPCSem = -1;
12395# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12396 mIPCKey = "0";
12397# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12398#else
12399# error "Port me!"
12400#endif
12401
12402 /* fire an event */
12403 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12404
12405 uninitDataAndChildObjects();
12406
12407 /* free the essential data structure last */
12408 mData.free();
12409
12410 /* release the exclusive lock before setting the below two to NULL */
12411 multilock.release();
12412
12413 unconst(mParent) = NULL;
12414 unconst(mPeer) = NULL;
12415
12416 LogFlowThisFuncLeave();
12417}
12418
12419// util::Lockable interface
12420////////////////////////////////////////////////////////////////////////////////
12421
12422/**
12423 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12424 * with the primary Machine instance (mPeer).
12425 */
12426RWLockHandle *SessionMachine::lockHandle() const
12427{
12428 AssertReturn(mPeer != NULL, NULL);
12429 return mPeer->lockHandle();
12430}
12431
12432// IInternalMachineControl methods
12433////////////////////////////////////////////////////////////////////////////////
12434
12435/**
12436 * Passes collected guest statistics to performance collector object
12437 */
12438STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12439 ULONG aCpuKernel, ULONG aCpuIdle,
12440 ULONG aMemTotal, ULONG aMemFree,
12441 ULONG aMemBalloon, ULONG aMemShared,
12442 ULONG aMemCache, ULONG aPageTotal,
12443 ULONG aAllocVMM, ULONG aFreeVMM,
12444 ULONG aBalloonedVMM, ULONG aSharedVMM,
12445 ULONG aVmNetRx, ULONG aVmNetTx)
12446{
12447 if (mCollectorGuest)
12448 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12449 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12450 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12451 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12452
12453 return S_OK;
12454}
12455
12456/**
12457 * @note Locks this object for writing.
12458 */
12459STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12460{
12461 AutoCaller autoCaller(this);
12462 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12463
12464 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12465
12466 mRemoveSavedState = aRemove;
12467
12468 return S_OK;
12469}
12470
12471/**
12472 * @note Locks the same as #setMachineState() does.
12473 */
12474STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12475{
12476 return setMachineState(aMachineState);
12477}
12478
12479/**
12480 * @note Locks this object for reading.
12481 */
12482STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
12483{
12484 AutoCaller autoCaller(this);
12485 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12486
12487 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12488
12489#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
12490 mIPCSemName.cloneTo(aId);
12491 return S_OK;
12492#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12493# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12494 mIPCKey.cloneTo(aId);
12495# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12496 mData->m_strConfigFileFull.cloneTo(aId);
12497# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12498 return S_OK;
12499#else
12500# error "Port me!"
12501#endif
12502}
12503
12504/**
12505 * @note Locks this object for writing.
12506 */
12507STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12508{
12509 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12510 AutoCaller autoCaller(this);
12511 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12512
12513 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12514
12515 if (mData->mSession.mState != SessionState_Locked)
12516 return VBOX_E_INVALID_OBJECT_STATE;
12517
12518 if (!mData->mSession.mProgress.isNull())
12519 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12520
12521 LogFlowThisFunc(("returns S_OK.\n"));
12522 return S_OK;
12523}
12524
12525/**
12526 * @note Locks this object for writing.
12527 */
12528STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12529{
12530 AutoCaller autoCaller(this);
12531 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12532
12533 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12534
12535 if (mData->mSession.mState != SessionState_Locked)
12536 return VBOX_E_INVALID_OBJECT_STATE;
12537
12538 /* Finalize the LaunchVMProcess progress object. */
12539 if (mData->mSession.mProgress)
12540 {
12541 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12542 mData->mSession.mProgress.setNull();
12543 }
12544
12545 if (SUCCEEDED((HRESULT)iResult))
12546 {
12547#ifdef VBOX_WITH_RESOURCE_USAGE_API
12548 /* The VM has been powered up successfully, so it makes sense
12549 * now to offer the performance metrics for a running machine
12550 * object. Doing it earlier wouldn't be safe. */
12551 registerMetrics(mParent->performanceCollector(), mPeer,
12552 mData->mSession.mPID);
12553#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12554 }
12555
12556 return S_OK;
12557}
12558
12559/**
12560 * @note Locks this object for writing.
12561 */
12562STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12563{
12564 LogFlowThisFuncEnter();
12565
12566 CheckComArgOutPointerValid(aProgress);
12567
12568 AutoCaller autoCaller(this);
12569 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12570
12571 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12572
12573 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12574 E_FAIL);
12575
12576 /* create a progress object to track operation completion */
12577 ComObjPtr<Progress> pProgress;
12578 pProgress.createObject();
12579 pProgress->init(getVirtualBox(),
12580 static_cast<IMachine *>(this) /* aInitiator */,
12581 Bstr(tr("Stopping the virtual machine")).raw(),
12582 FALSE /* aCancelable */);
12583
12584 /* fill in the console task data */
12585 mConsoleTaskData.mLastState = mData->mMachineState;
12586 mConsoleTaskData.mProgress = pProgress;
12587
12588 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12589 setMachineState(MachineState_Stopping);
12590
12591 pProgress.queryInterfaceTo(aProgress);
12592
12593 return S_OK;
12594}
12595
12596/**
12597 * @note Locks this object for writing.
12598 */
12599STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12600{
12601 LogFlowThisFuncEnter();
12602
12603 AutoCaller autoCaller(this);
12604 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12605
12606 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12607
12608 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12609 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12610 && mConsoleTaskData.mLastState != MachineState_Null,
12611 E_FAIL);
12612
12613 /*
12614 * On failure, set the state to the state we had when BeginPoweringDown()
12615 * was called (this is expected by Console::PowerDown() and the associated
12616 * task). On success the VM process already changed the state to
12617 * MachineState_PoweredOff, so no need to do anything.
12618 */
12619 if (FAILED(iResult))
12620 setMachineState(mConsoleTaskData.mLastState);
12621
12622 /* notify the progress object about operation completion */
12623 Assert(mConsoleTaskData.mProgress);
12624 if (SUCCEEDED(iResult))
12625 mConsoleTaskData.mProgress->notifyComplete(S_OK);
12626 else
12627 {
12628 Utf8Str strErrMsg(aErrMsg);
12629 if (strErrMsg.length())
12630 mConsoleTaskData.mProgress->notifyComplete(iResult,
12631 COM_IIDOF(ISession),
12632 getComponentName(),
12633 strErrMsg.c_str());
12634 else
12635 mConsoleTaskData.mProgress->notifyComplete(iResult);
12636 }
12637
12638 /* clear out the temporary saved state data */
12639 mConsoleTaskData.mLastState = MachineState_Null;
12640 mConsoleTaskData.mProgress.setNull();
12641
12642 LogFlowThisFuncLeave();
12643 return S_OK;
12644}
12645
12646
12647/**
12648 * Goes through the USB filters of the given machine to see if the given
12649 * device matches any filter or not.
12650 *
12651 * @note Locks the same as USBController::hasMatchingFilter() does.
12652 */
12653STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12654 BOOL *aMatched,
12655 ULONG *aMaskedIfs)
12656{
12657 LogFlowThisFunc(("\n"));
12658
12659 CheckComArgNotNull(aUSBDevice);
12660 CheckComArgOutPointerValid(aMatched);
12661
12662 AutoCaller autoCaller(this);
12663 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12664
12665#ifdef VBOX_WITH_USB
12666 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
12667#else
12668 NOREF(aUSBDevice);
12669 NOREF(aMaskedIfs);
12670 *aMatched = FALSE;
12671#endif
12672
12673 return S_OK;
12674}
12675
12676/**
12677 * @note Locks the same as Host::captureUSBDevice() does.
12678 */
12679STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12680{
12681 LogFlowThisFunc(("\n"));
12682
12683 AutoCaller autoCaller(this);
12684 AssertComRCReturnRC(autoCaller.rc());
12685
12686#ifdef VBOX_WITH_USB
12687 /* if captureDeviceForVM() fails, it must have set extended error info */
12688 clearError();
12689 MultiResult rc = mParent->host()->checkUSBProxyService();
12690 if (FAILED(rc)) return rc;
12691
12692 USBProxyService *service = mParent->host()->usbProxyService();
12693 AssertReturn(service, E_FAIL);
12694 return service->captureDeviceForVM(this, Guid(aId).ref());
12695#else
12696 NOREF(aId);
12697 return E_NOTIMPL;
12698#endif
12699}
12700
12701/**
12702 * @note Locks the same as Host::detachUSBDevice() does.
12703 */
12704STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12705{
12706 LogFlowThisFunc(("\n"));
12707
12708 AutoCaller autoCaller(this);
12709 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12710
12711#ifdef VBOX_WITH_USB
12712 USBProxyService *service = mParent->host()->usbProxyService();
12713 AssertReturn(service, E_FAIL);
12714 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12715#else
12716 NOREF(aId);
12717 NOREF(aDone);
12718 return E_NOTIMPL;
12719#endif
12720}
12721
12722/**
12723 * Inserts all machine filters to the USB proxy service and then calls
12724 * Host::autoCaptureUSBDevices().
12725 *
12726 * Called by Console from the VM process upon VM startup.
12727 *
12728 * @note Locks what called methods lock.
12729 */
12730STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12731{
12732 LogFlowThisFunc(("\n"));
12733
12734 AutoCaller autoCaller(this);
12735 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12736
12737#ifdef VBOX_WITH_USB
12738 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
12739 AssertComRC(rc);
12740 NOREF(rc);
12741
12742 USBProxyService *service = mParent->host()->usbProxyService();
12743 AssertReturn(service, E_FAIL);
12744 return service->autoCaptureDevicesForVM(this);
12745#else
12746 return S_OK;
12747#endif
12748}
12749
12750/**
12751 * Removes all machine filters from the USB proxy service and then calls
12752 * Host::detachAllUSBDevices().
12753 *
12754 * Called by Console from the VM process upon normal VM termination or by
12755 * SessionMachine::uninit() upon abnormal VM termination (from under the
12756 * Machine/SessionMachine lock).
12757 *
12758 * @note Locks what called methods lock.
12759 */
12760STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12761{
12762 LogFlowThisFunc(("\n"));
12763
12764 AutoCaller autoCaller(this);
12765 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12766
12767#ifdef VBOX_WITH_USB
12768 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12769 AssertComRC(rc);
12770 NOREF(rc);
12771
12772 USBProxyService *service = mParent->host()->usbProxyService();
12773 AssertReturn(service, E_FAIL);
12774 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12775#else
12776 NOREF(aDone);
12777 return S_OK;
12778#endif
12779}
12780
12781/**
12782 * @note Locks this object for writing.
12783 */
12784STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12785 IProgress **aProgress)
12786{
12787 LogFlowThisFuncEnter();
12788
12789 AssertReturn(aSession, E_INVALIDARG);
12790 AssertReturn(aProgress, E_INVALIDARG);
12791
12792 AutoCaller autoCaller(this);
12793
12794 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
12795 /*
12796 * We don't assert below because it might happen that a non-direct session
12797 * informs us it is closed right after we've been uninitialized -- it's ok.
12798 */
12799 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12800
12801 /* get IInternalSessionControl interface */
12802 ComPtr<IInternalSessionControl> control(aSession);
12803
12804 ComAssertRet(!control.isNull(), E_INVALIDARG);
12805
12806 /* Creating a Progress object requires the VirtualBox lock, and
12807 * thus locking it here is required by the lock order rules. */
12808 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12809
12810 if (control == mData->mSession.mDirectControl)
12811 {
12812 ComAssertRet(aProgress, E_POINTER);
12813
12814 /* The direct session is being normally closed by the client process
12815 * ----------------------------------------------------------------- */
12816
12817 /* go to the closing state (essential for all open*Session() calls and
12818 * for #checkForDeath()) */
12819 Assert(mData->mSession.mState == SessionState_Locked);
12820 mData->mSession.mState = SessionState_Unlocking;
12821
12822 /* set direct control to NULL to release the remote instance */
12823 mData->mSession.mDirectControl.setNull();
12824 LogFlowThisFunc(("Direct control is set to NULL\n"));
12825
12826 if (mData->mSession.mProgress)
12827 {
12828 /* finalize the progress, someone might wait if a frontend
12829 * closes the session before powering on the VM. */
12830 mData->mSession.mProgress->notifyComplete(E_FAIL,
12831 COM_IIDOF(ISession),
12832 getComponentName(),
12833 tr("The VM session was closed before any attempt to power it on"));
12834 mData->mSession.mProgress.setNull();
12835 }
12836
12837 /* Create the progress object the client will use to wait until
12838 * #checkForDeath() is called to uninitialize this session object after
12839 * it releases the IPC semaphore.
12840 * Note! Because we're "reusing" mProgress here, this must be a proxy
12841 * object just like for LaunchVMProcess. */
12842 Assert(mData->mSession.mProgress.isNull());
12843 ComObjPtr<ProgressProxy> progress;
12844 progress.createObject();
12845 ComPtr<IUnknown> pPeer(mPeer);
12846 progress->init(mParent, pPeer,
12847 Bstr(tr("Closing session")).raw(),
12848 FALSE /* aCancelable */);
12849 progress.queryInterfaceTo(aProgress);
12850 mData->mSession.mProgress = progress;
12851 }
12852 else
12853 {
12854 /* the remote session is being normally closed */
12855 Data::Session::RemoteControlList::iterator it =
12856 mData->mSession.mRemoteControls.begin();
12857 while (it != mData->mSession.mRemoteControls.end())
12858 {
12859 if (control == *it)
12860 break;
12861 ++it;
12862 }
12863 BOOL found = it != mData->mSession.mRemoteControls.end();
12864 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12865 E_INVALIDARG);
12866 // This MUST be erase(it), not remove(*it) as the latter triggers a
12867 // very nasty use after free due to the place where the value "lives".
12868 mData->mSession.mRemoteControls.erase(it);
12869 }
12870
12871 /* signal the client watcher thread, because the client is going away */
12872 mParent->updateClientWatcher();
12873
12874 LogFlowThisFuncLeave();
12875 return S_OK;
12876}
12877
12878/**
12879 * @note Locks this object for writing.
12880 */
12881STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12882{
12883 LogFlowThisFuncEnter();
12884
12885 CheckComArgOutPointerValid(aProgress);
12886 CheckComArgOutPointerValid(aStateFilePath);
12887
12888 AutoCaller autoCaller(this);
12889 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12890
12891 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12892
12893 AssertReturn( mData->mMachineState == MachineState_Paused
12894 && mConsoleTaskData.mLastState == MachineState_Null
12895 && mConsoleTaskData.strStateFilePath.isEmpty(),
12896 E_FAIL);
12897
12898 /* create a progress object to track operation completion */
12899 ComObjPtr<Progress> pProgress;
12900 pProgress.createObject();
12901 pProgress->init(getVirtualBox(),
12902 static_cast<IMachine *>(this) /* aInitiator */,
12903 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12904 FALSE /* aCancelable */);
12905
12906 Utf8Str strStateFilePath;
12907 /* stateFilePath is null when the machine is not running */
12908 if (mData->mMachineState == MachineState_Paused)
12909 composeSavedStateFilename(strStateFilePath);
12910
12911 /* fill in the console task data */
12912 mConsoleTaskData.mLastState = mData->mMachineState;
12913 mConsoleTaskData.strStateFilePath = strStateFilePath;
12914 mConsoleTaskData.mProgress = pProgress;
12915
12916 /* set the state to Saving (this is expected by Console::SaveState()) */
12917 setMachineState(MachineState_Saving);
12918
12919 strStateFilePath.cloneTo(aStateFilePath);
12920 pProgress.queryInterfaceTo(aProgress);
12921
12922 return S_OK;
12923}
12924
12925/**
12926 * @note Locks mParent + this object for writing.
12927 */
12928STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12929{
12930 LogFlowThisFunc(("\n"));
12931
12932 AutoCaller autoCaller(this);
12933 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12934
12935 /* endSavingState() need mParent lock */
12936 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12937
12938 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12939 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12940 && mConsoleTaskData.mLastState != MachineState_Null
12941 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12942 E_FAIL);
12943
12944 /*
12945 * On failure, set the state to the state we had when BeginSavingState()
12946 * was called (this is expected by Console::SaveState() and the associated
12947 * task). On success the VM process already changed the state to
12948 * MachineState_Saved, so no need to do anything.
12949 */
12950 if (FAILED(iResult))
12951 setMachineState(mConsoleTaskData.mLastState);
12952
12953 return endSavingState(iResult, aErrMsg);
12954}
12955
12956/**
12957 * @note Locks this object for writing.
12958 */
12959STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12960{
12961 LogFlowThisFunc(("\n"));
12962
12963 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12964
12965 AutoCaller autoCaller(this);
12966 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12967
12968 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12969
12970 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12971 || mData->mMachineState == MachineState_Teleported
12972 || mData->mMachineState == MachineState_Aborted
12973 , E_FAIL); /** @todo setError. */
12974
12975 Utf8Str stateFilePathFull = aSavedStateFile;
12976 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
12977 if (RT_FAILURE(vrc))
12978 return setError(VBOX_E_FILE_ERROR,
12979 tr("Invalid saved state file path '%ls' (%Rrc)"),
12980 aSavedStateFile,
12981 vrc);
12982
12983 mSSData->strStateFilePath = stateFilePathFull;
12984
12985 /* The below setMachineState() will detect the state transition and will
12986 * update the settings file */
12987
12988 return setMachineState(MachineState_Saved);
12989}
12990
12991STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
12992 ComSafeArrayOut(BSTR, aValues),
12993 ComSafeArrayOut(LONG64, aTimestamps),
12994 ComSafeArrayOut(BSTR, aFlags))
12995{
12996 LogFlowThisFunc(("\n"));
12997
12998#ifdef VBOX_WITH_GUEST_PROPS
12999 using namespace guestProp;
13000
13001 AutoCaller autoCaller(this);
13002 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13003
13004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13005
13006 CheckComArgOutSafeArrayPointerValid(aNames);
13007 CheckComArgOutSafeArrayPointerValid(aValues);
13008 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13009 CheckComArgOutSafeArrayPointerValid(aFlags);
13010
13011 size_t cEntries = mHWData->mGuestProperties.size();
13012 com::SafeArray<BSTR> names(cEntries);
13013 com::SafeArray<BSTR> values(cEntries);
13014 com::SafeArray<LONG64> timestamps(cEntries);
13015 com::SafeArray<BSTR> flags(cEntries);
13016 unsigned i = 0;
13017 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13018 it != mHWData->mGuestProperties.end();
13019 ++it)
13020 {
13021 char szFlags[MAX_FLAGS_LEN + 1];
13022 it->first.cloneTo(&names[i]);
13023 it->second.strValue.cloneTo(&values[i]);
13024 timestamps[i] = it->second.mTimestamp;
13025 /* If it is NULL, keep it NULL. */
13026 if (it->second.mFlags)
13027 {
13028 writeFlags(it->second.mFlags, szFlags);
13029 Bstr(szFlags).cloneTo(&flags[i]);
13030 }
13031 else
13032 flags[i] = NULL;
13033 ++i;
13034 }
13035 names.detachTo(ComSafeArrayOutArg(aNames));
13036 values.detachTo(ComSafeArrayOutArg(aValues));
13037 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13038 flags.detachTo(ComSafeArrayOutArg(aFlags));
13039 return S_OK;
13040#else
13041 ReturnComNotImplemented();
13042#endif
13043}
13044
13045STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13046 IN_BSTR aValue,
13047 LONG64 aTimestamp,
13048 IN_BSTR aFlags)
13049{
13050 LogFlowThisFunc(("\n"));
13051
13052#ifdef VBOX_WITH_GUEST_PROPS
13053 using namespace guestProp;
13054
13055 CheckComArgStrNotEmptyOrNull(aName);
13056 CheckComArgNotNull(aValue);
13057 CheckComArgNotNull(aFlags);
13058
13059 try
13060 {
13061 /*
13062 * Convert input up front.
13063 */
13064 Utf8Str utf8Name(aName);
13065 uint32_t fFlags = NILFLAG;
13066 if (aFlags)
13067 {
13068 Utf8Str utf8Flags(aFlags);
13069 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13070 AssertRCReturn(vrc, E_INVALIDARG);
13071 }
13072
13073 /*
13074 * Now grab the object lock, validate the state and do the update.
13075 */
13076 AutoCaller autoCaller(this);
13077 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13078
13079 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13080
13081 switch (mData->mMachineState)
13082 {
13083 case MachineState_Paused:
13084 case MachineState_Running:
13085 case MachineState_Teleporting:
13086 case MachineState_TeleportingPausedVM:
13087 case MachineState_LiveSnapshotting:
13088 case MachineState_DeletingSnapshotOnline:
13089 case MachineState_DeletingSnapshotPaused:
13090 case MachineState_Saving:
13091 case MachineState_Stopping:
13092 break;
13093
13094 default:
13095 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13096 VBOX_E_INVALID_VM_STATE);
13097 }
13098
13099 setModified(IsModified_MachineData);
13100 mHWData.backup();
13101
13102 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13103 if (it != mHWData->mGuestProperties.end())
13104 {
13105 if (RT_VALID_PTR(aValue) && *(aValue) != '\0')
13106 {
13107 it->second.strValue = aValue;
13108 it->second.mFlags = fFlags;
13109 it->second.mTimestamp = aTimestamp;
13110 }
13111 else
13112 mHWData->mGuestProperties.erase(it);
13113
13114 mData->mGuestPropertiesModified = TRUE;
13115 }
13116
13117 /*
13118 * Send a callback notification if appropriate
13119 */
13120 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13121 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13122 RTSTR_MAX,
13123 utf8Name.c_str(),
13124 RTSTR_MAX, NULL)
13125 )
13126 {
13127 alock.release();
13128
13129 mParent->onGuestPropertyChange(mData->mUuid,
13130 aName,
13131 aValue,
13132 aFlags);
13133 }
13134 }
13135 catch (...)
13136 {
13137 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13138 }
13139 return S_OK;
13140#else
13141 ReturnComNotImplemented();
13142#endif
13143}
13144
13145STDMETHODIMP SessionMachine::LockMedia()
13146{
13147 AutoCaller autoCaller(this);
13148 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13149
13150 AutoMultiWriteLock2 alock(this->lockHandle(),
13151 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13152
13153 AssertReturn( mData->mMachineState == MachineState_Starting
13154 || mData->mMachineState == MachineState_Restoring
13155 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13156
13157 clearError();
13158 alock.release();
13159 return lockMedia();
13160}
13161
13162STDMETHODIMP SessionMachine::UnlockMedia()
13163{
13164 unlockMedia();
13165 return S_OK;
13166}
13167
13168STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13169 IMediumAttachment **aNewAttachment)
13170{
13171 CheckComArgNotNull(aAttachment);
13172 CheckComArgOutPointerValid(aNewAttachment);
13173
13174 AutoCaller autoCaller(this);
13175 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13176
13177 // request the host lock first, since might be calling Host methods for getting host drives;
13178 // next, protect the media tree all the while we're in here, as well as our member variables
13179 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13180 this->lockHandle(),
13181 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13182
13183 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13184
13185 Bstr ctrlName;
13186 LONG lPort;
13187 LONG lDevice;
13188 bool fTempEject;
13189 {
13190 AutoCaller autoAttachCaller(this);
13191 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13192
13193 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13194
13195 /* Need to query the details first, as the IMediumAttachment reference
13196 * might be to the original settings, which we are going to change. */
13197 ctrlName = pAttach->getControllerName();
13198 lPort = pAttach->getPort();
13199 lDevice = pAttach->getDevice();
13200 fTempEject = pAttach->getTempEject();
13201 }
13202
13203 if (!fTempEject)
13204 {
13205 /* Remember previously mounted medium. The medium before taking the
13206 * backup is not necessarily the same thing. */
13207 ComObjPtr<Medium> oldmedium;
13208 oldmedium = pAttach->getMedium();
13209
13210 setModified(IsModified_Storage);
13211 mMediaData.backup();
13212
13213 // The backup operation makes the pAttach reference point to the
13214 // old settings. Re-get the correct reference.
13215 pAttach = findAttachment(mMediaData->mAttachments,
13216 ctrlName.raw(),
13217 lPort,
13218 lDevice);
13219
13220 {
13221 AutoCaller autoAttachCaller(this);
13222 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13223
13224 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13225 if (!oldmedium.isNull())
13226 oldmedium->removeBackReference(mData->mUuid);
13227
13228 pAttach->updateMedium(NULL);
13229 pAttach->updateEjected();
13230 }
13231
13232 setModified(IsModified_Storage);
13233 }
13234 else
13235 {
13236 {
13237 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13238 pAttach->updateEjected();
13239 }
13240 }
13241
13242 pAttach.queryInterfaceTo(aNewAttachment);
13243
13244 return S_OK;
13245}
13246
13247// public methods only for internal purposes
13248/////////////////////////////////////////////////////////////////////////////
13249
13250/**
13251 * Called from the client watcher thread to check for expected or unexpected
13252 * death of the client process that has a direct session to this machine.
13253 *
13254 * On Win32 and on OS/2, this method is called only when we've got the
13255 * mutex (i.e. the client has either died or terminated normally) so it always
13256 * returns @c true (the client is terminated, the session machine is
13257 * uninitialized).
13258 *
13259 * On other platforms, the method returns @c true if the client process has
13260 * terminated normally or abnormally and the session machine was uninitialized,
13261 * and @c false if the client process is still alive.
13262 *
13263 * @note Locks this object for writing.
13264 */
13265bool SessionMachine::checkForDeath()
13266{
13267 Uninit::Reason reason;
13268 bool terminated = false;
13269
13270 /* Enclose autoCaller with a block because calling uninit() from under it
13271 * will deadlock. */
13272 {
13273 AutoCaller autoCaller(this);
13274 if (!autoCaller.isOk())
13275 {
13276 /* return true if not ready, to cause the client watcher to exclude
13277 * the corresponding session from watching */
13278 LogFlowThisFunc(("Already uninitialized!\n"));
13279 return true;
13280 }
13281
13282 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13283
13284 /* Determine the reason of death: if the session state is Closing here,
13285 * everything is fine. Otherwise it means that the client did not call
13286 * OnSessionEnd() before it released the IPC semaphore. This may happen
13287 * either because the client process has abnormally terminated, or
13288 * because it simply forgot to call ISession::Close() before exiting. We
13289 * threat the latter also as an abnormal termination (see
13290 * Session::uninit() for details). */
13291 reason = mData->mSession.mState == SessionState_Unlocking ?
13292 Uninit::Normal :
13293 Uninit::Abnormal;
13294
13295#if defined(RT_OS_WINDOWS)
13296
13297 AssertMsg(mIPCSem, ("semaphore must be created"));
13298
13299 /* release the IPC mutex */
13300 ::ReleaseMutex(mIPCSem);
13301
13302 terminated = true;
13303
13304#elif defined(RT_OS_OS2)
13305
13306 AssertMsg(mIPCSem, ("semaphore must be created"));
13307
13308 /* release the IPC mutex */
13309 ::DosReleaseMutexSem(mIPCSem);
13310
13311 terminated = true;
13312
13313#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
13314
13315 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
13316
13317 int val = ::semctl(mIPCSem, 0, GETVAL);
13318 if (val > 0)
13319 {
13320 /* the semaphore is signaled, meaning the session is terminated */
13321 terminated = true;
13322 }
13323
13324#else
13325# error "Port me!"
13326#endif
13327
13328 } /* AutoCaller block */
13329
13330 if (terminated)
13331 uninit(reason);
13332
13333 return terminated;
13334}
13335
13336/**
13337 * @note Locks this object for reading.
13338 */
13339HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13340{
13341 LogFlowThisFunc(("\n"));
13342
13343 AutoCaller autoCaller(this);
13344 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13345
13346 ComPtr<IInternalSessionControl> directControl;
13347 {
13348 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13349 directControl = mData->mSession.mDirectControl;
13350 }
13351
13352 /* ignore notifications sent after #OnSessionEnd() is called */
13353 if (!directControl)
13354 return S_OK;
13355
13356 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13357}
13358
13359/**
13360 * @note Locks this object for reading.
13361 */
13362HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13363 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
13364{
13365 LogFlowThisFunc(("\n"));
13366
13367 AutoCaller autoCaller(this);
13368 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13369
13370 ComPtr<IInternalSessionControl> directControl;
13371 {
13372 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13373 directControl = mData->mSession.mDirectControl;
13374 }
13375
13376 /* ignore notifications sent after #OnSessionEnd() is called */
13377 if (!directControl)
13378 return S_OK;
13379 /*
13380 * instead acting like callback we ask IVirtualBox deliver corresponding event
13381 */
13382
13383 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13384 return S_OK;
13385}
13386
13387/**
13388 * @note Locks this object for reading.
13389 */
13390HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
13391{
13392 LogFlowThisFunc(("\n"));
13393
13394 AutoCaller autoCaller(this);
13395 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13396
13397 ComPtr<IInternalSessionControl> directControl;
13398 {
13399 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13400 directControl = mData->mSession.mDirectControl;
13401 }
13402
13403 /* ignore notifications sent after #OnSessionEnd() is called */
13404 if (!directControl)
13405 return S_OK;
13406
13407 return directControl->OnSerialPortChange(serialPort);
13408}
13409
13410/**
13411 * @note Locks this object for reading.
13412 */
13413HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
13414{
13415 LogFlowThisFunc(("\n"));
13416
13417 AutoCaller autoCaller(this);
13418 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13419
13420 ComPtr<IInternalSessionControl> directControl;
13421 {
13422 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13423 directControl = mData->mSession.mDirectControl;
13424 }
13425
13426 /* ignore notifications sent after #OnSessionEnd() is called */
13427 if (!directControl)
13428 return S_OK;
13429
13430 return directControl->OnParallelPortChange(parallelPort);
13431}
13432
13433/**
13434 * @note Locks this object for reading.
13435 */
13436HRESULT SessionMachine::onStorageControllerChange()
13437{
13438 LogFlowThisFunc(("\n"));
13439
13440 AutoCaller autoCaller(this);
13441 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13442
13443 ComPtr<IInternalSessionControl> directControl;
13444 {
13445 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13446 directControl = mData->mSession.mDirectControl;
13447 }
13448
13449 /* ignore notifications sent after #OnSessionEnd() is called */
13450 if (!directControl)
13451 return S_OK;
13452
13453 return directControl->OnStorageControllerChange();
13454}
13455
13456/**
13457 * @note Locks this object for reading.
13458 */
13459HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13460{
13461 LogFlowThisFunc(("\n"));
13462
13463 AutoCaller autoCaller(this);
13464 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13465
13466 ComPtr<IInternalSessionControl> directControl;
13467 {
13468 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13469 directControl = mData->mSession.mDirectControl;
13470 }
13471
13472 /* ignore notifications sent after #OnSessionEnd() is called */
13473 if (!directControl)
13474 return S_OK;
13475
13476 return directControl->OnMediumChange(aAttachment, aForce);
13477}
13478
13479/**
13480 * @note Locks this object for reading.
13481 */
13482HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
13483{
13484 LogFlowThisFunc(("\n"));
13485
13486 AutoCaller autoCaller(this);
13487 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13488
13489 ComPtr<IInternalSessionControl> directControl;
13490 {
13491 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13492 directControl = mData->mSession.mDirectControl;
13493 }
13494
13495 /* ignore notifications sent after #OnSessionEnd() is called */
13496 if (!directControl)
13497 return S_OK;
13498
13499 return directControl->OnCPUChange(aCPU, aRemove);
13500}
13501
13502HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
13503{
13504 LogFlowThisFunc(("\n"));
13505
13506 AutoCaller autoCaller(this);
13507 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13508
13509 ComPtr<IInternalSessionControl> directControl;
13510 {
13511 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13512 directControl = mData->mSession.mDirectControl;
13513 }
13514
13515 /* ignore notifications sent after #OnSessionEnd() is called */
13516 if (!directControl)
13517 return S_OK;
13518
13519 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13520}
13521
13522/**
13523 * @note Locks this object for reading.
13524 */
13525HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
13526{
13527 LogFlowThisFunc(("\n"));
13528
13529 AutoCaller autoCaller(this);
13530 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13531
13532 ComPtr<IInternalSessionControl> directControl;
13533 {
13534 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13535 directControl = mData->mSession.mDirectControl;
13536 }
13537
13538 /* ignore notifications sent after #OnSessionEnd() is called */
13539 if (!directControl)
13540 return S_OK;
13541
13542 return directControl->OnVRDEServerChange(aRestart);
13543}
13544
13545/**
13546 * @note Locks this object for reading.
13547 */
13548HRESULT SessionMachine::onUSBControllerChange()
13549{
13550 LogFlowThisFunc(("\n"));
13551
13552 AutoCaller autoCaller(this);
13553 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13554
13555 ComPtr<IInternalSessionControl> directControl;
13556 {
13557 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13558 directControl = mData->mSession.mDirectControl;
13559 }
13560
13561 /* ignore notifications sent after #OnSessionEnd() is called */
13562 if (!directControl)
13563 return S_OK;
13564
13565 return directControl->OnUSBControllerChange();
13566}
13567
13568/**
13569 * @note Locks this object for reading.
13570 */
13571HRESULT SessionMachine::onSharedFolderChange()
13572{
13573 LogFlowThisFunc(("\n"));
13574
13575 AutoCaller autoCaller(this);
13576 AssertComRCReturnRC(autoCaller.rc());
13577
13578 ComPtr<IInternalSessionControl> directControl;
13579 {
13580 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13581 directControl = mData->mSession.mDirectControl;
13582 }
13583
13584 /* ignore notifications sent after #OnSessionEnd() is called */
13585 if (!directControl)
13586 return S_OK;
13587
13588 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13589}
13590
13591/**
13592 * @note Locks this object for reading.
13593 */
13594HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
13595{
13596 LogFlowThisFunc(("\n"));
13597
13598 AutoCaller autoCaller(this);
13599 AssertComRCReturnRC(autoCaller.rc());
13600
13601 ComPtr<IInternalSessionControl> directControl;
13602 {
13603 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13604 directControl = mData->mSession.mDirectControl;
13605 }
13606
13607 /* ignore notifications sent after #OnSessionEnd() is called */
13608 if (!directControl)
13609 return S_OK;
13610
13611 return directControl->OnClipboardModeChange(aClipboardMode);
13612}
13613
13614/**
13615 * @note Locks this object for reading.
13616 */
13617HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
13618{
13619 LogFlowThisFunc(("\n"));
13620
13621 AutoCaller autoCaller(this);
13622 AssertComRCReturnRC(autoCaller.rc());
13623
13624 ComPtr<IInternalSessionControl> directControl;
13625 {
13626 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13627 directControl = mData->mSession.mDirectControl;
13628 }
13629
13630 /* ignore notifications sent after #OnSessionEnd() is called */
13631 if (!directControl)
13632 return S_OK;
13633
13634 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
13635}
13636
13637/**
13638 * @note Locks this object for reading.
13639 */
13640HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13641{
13642 LogFlowThisFunc(("\n"));
13643
13644 AutoCaller autoCaller(this);
13645 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13646
13647 ComPtr<IInternalSessionControl> directControl;
13648 {
13649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13650 directControl = mData->mSession.mDirectControl;
13651 }
13652
13653 /* ignore notifications sent after #OnSessionEnd() is called */
13654 if (!directControl)
13655 return S_OK;
13656
13657 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13658}
13659
13660/**
13661 * @note Locks this object for reading.
13662 */
13663HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13664{
13665 LogFlowThisFunc(("\n"));
13666
13667 AutoCaller autoCaller(this);
13668 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13669
13670 ComPtr<IInternalSessionControl> directControl;
13671 {
13672 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13673 directControl = mData->mSession.mDirectControl;
13674 }
13675
13676 /* ignore notifications sent after #OnSessionEnd() is called */
13677 if (!directControl)
13678 return S_OK;
13679
13680 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13681}
13682
13683/**
13684 * Returns @c true if this machine's USB controller reports it has a matching
13685 * filter for the given USB device and @c false otherwise.
13686 *
13687 * @note locks this object for reading.
13688 */
13689bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13690{
13691 AutoCaller autoCaller(this);
13692 /* silently return if not ready -- this method may be called after the
13693 * direct machine session has been called */
13694 if (!autoCaller.isOk())
13695 return false;
13696
13697#ifdef VBOX_WITH_USB
13698 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13699
13700 switch (mData->mMachineState)
13701 {
13702 case MachineState_Starting:
13703 case MachineState_Restoring:
13704 case MachineState_TeleportingIn:
13705 case MachineState_Paused:
13706 case MachineState_Running:
13707 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13708 * elsewhere... */
13709 alock.release();
13710 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
13711 default: break;
13712 }
13713#else
13714 NOREF(aDevice);
13715 NOREF(aMaskedIfs);
13716#endif
13717 return false;
13718}
13719
13720/**
13721 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13722 */
13723HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
13724 IVirtualBoxErrorInfo *aError,
13725 ULONG aMaskedIfs)
13726{
13727 LogFlowThisFunc(("\n"));
13728
13729 AutoCaller autoCaller(this);
13730
13731 /* This notification may happen after the machine object has been
13732 * uninitialized (the session was closed), so don't assert. */
13733 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13734
13735 ComPtr<IInternalSessionControl> directControl;
13736 {
13737 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13738 directControl = mData->mSession.mDirectControl;
13739 }
13740
13741 /* fail on notifications sent after #OnSessionEnd() is called, it is
13742 * expected by the caller */
13743 if (!directControl)
13744 return E_FAIL;
13745
13746 /* No locks should be held at this point. */
13747 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13748 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13749
13750 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13751}
13752
13753/**
13754 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13755 */
13756HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
13757 IVirtualBoxErrorInfo *aError)
13758{
13759 LogFlowThisFunc(("\n"));
13760
13761 AutoCaller autoCaller(this);
13762
13763 /* This notification may happen after the machine object has been
13764 * uninitialized (the session was closed), so don't assert. */
13765 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13766
13767 ComPtr<IInternalSessionControl> directControl;
13768 {
13769 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13770 directControl = mData->mSession.mDirectControl;
13771 }
13772
13773 /* fail on notifications sent after #OnSessionEnd() is called, it is
13774 * expected by the caller */
13775 if (!directControl)
13776 return E_FAIL;
13777
13778 /* No locks should be held at this point. */
13779 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13780 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13781
13782 return directControl->OnUSBDeviceDetach(aId, aError);
13783}
13784
13785// protected methods
13786/////////////////////////////////////////////////////////////////////////////
13787
13788/**
13789 * Helper method to finalize saving the state.
13790 *
13791 * @note Must be called from under this object's lock.
13792 *
13793 * @param aRc S_OK if the snapshot has been taken successfully
13794 * @param aErrMsg human readable error message for failure
13795 *
13796 * @note Locks mParent + this objects for writing.
13797 */
13798HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13799{
13800 LogFlowThisFuncEnter();
13801
13802 AutoCaller autoCaller(this);
13803 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13804
13805 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13806
13807 HRESULT rc = S_OK;
13808
13809 if (SUCCEEDED(aRc))
13810 {
13811 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13812
13813 /* save all VM settings */
13814 rc = saveSettings(NULL);
13815 // no need to check whether VirtualBox.xml needs saving also since
13816 // we can't have a name change pending at this point
13817 }
13818 else
13819 {
13820 // delete the saved state file (it might have been already created);
13821 // we need not check whether this is shared with a snapshot here because
13822 // we certainly created this saved state file here anew
13823 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13824 }
13825
13826 /* notify the progress object about operation completion */
13827 Assert(mConsoleTaskData.mProgress);
13828 if (SUCCEEDED(aRc))
13829 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13830 else
13831 {
13832 if (aErrMsg.length())
13833 mConsoleTaskData.mProgress->notifyComplete(aRc,
13834 COM_IIDOF(ISession),
13835 getComponentName(),
13836 aErrMsg.c_str());
13837 else
13838 mConsoleTaskData.mProgress->notifyComplete(aRc);
13839 }
13840
13841 /* clear out the temporary saved state data */
13842 mConsoleTaskData.mLastState = MachineState_Null;
13843 mConsoleTaskData.strStateFilePath.setNull();
13844 mConsoleTaskData.mProgress.setNull();
13845
13846 LogFlowThisFuncLeave();
13847 return rc;
13848}
13849
13850/**
13851 * Deletes the given file if it is no longer in use by either the current machine state
13852 * (if the machine is "saved") or any of the machine's snapshots.
13853 *
13854 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13855 * but is different for each SnapshotMachine. When calling this, the order of calling this
13856 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13857 * is therefore critical. I know, it's all rather messy.
13858 *
13859 * @param strStateFile
13860 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
13861 */
13862void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
13863 Snapshot *pSnapshotToIgnore)
13864{
13865 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13866 if ( (strStateFile.isNotEmpty())
13867 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13868 )
13869 // ... and it must also not be shared with other snapshots
13870 if ( !mData->mFirstSnapshot
13871 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13872 // this checks the SnapshotMachine's state file paths
13873 )
13874 RTFileDelete(strStateFile.c_str());
13875}
13876
13877/**
13878 * Locks the attached media.
13879 *
13880 * All attached hard disks are locked for writing and DVD/floppy are locked for
13881 * reading. Parents of attached hard disks (if any) are locked for reading.
13882 *
13883 * This method also performs accessibility check of all media it locks: if some
13884 * media is inaccessible, the method will return a failure and a bunch of
13885 * extended error info objects per each inaccessible medium.
13886 *
13887 * Note that this method is atomic: if it returns a success, all media are
13888 * locked as described above; on failure no media is locked at all (all
13889 * succeeded individual locks will be undone).
13890 *
13891 * The caller is responsible for doing the necessary state sanity checks.
13892 *
13893 * The locks made by this method must be undone by calling #unlockMedia() when
13894 * no more needed.
13895 */
13896HRESULT SessionMachine::lockMedia()
13897{
13898 AutoCaller autoCaller(this);
13899 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13900
13901 AutoMultiWriteLock2 alock(this->lockHandle(),
13902 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13903
13904 /* bail out if trying to lock things with already set up locking */
13905 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13906
13907 MultiResult mrc(S_OK);
13908
13909 /* Collect locking information for all medium objects attached to the VM. */
13910 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13911 it != mMediaData->mAttachments.end();
13912 ++it)
13913 {
13914 MediumAttachment* pAtt = *it;
13915 DeviceType_T devType = pAtt->getType();
13916 Medium *pMedium = pAtt->getMedium();
13917
13918 MediumLockList *pMediumLockList(new MediumLockList());
13919 // There can be attachments without a medium (floppy/dvd), and thus
13920 // it's impossible to create a medium lock list. It still makes sense
13921 // to have the empty medium lock list in the map in case a medium is
13922 // attached later.
13923 if (pMedium != NULL)
13924 {
13925 MediumType_T mediumType = pMedium->getType();
13926 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13927 || mediumType == MediumType_Shareable;
13928 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13929
13930 alock.release();
13931 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13932 !fIsReadOnlyLock /* fMediumLockWrite */,
13933 NULL,
13934 *pMediumLockList);
13935 alock.acquire();
13936 if (FAILED(mrc))
13937 {
13938 delete pMediumLockList;
13939 mData->mSession.mLockedMedia.Clear();
13940 break;
13941 }
13942 }
13943
13944 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13945 if (FAILED(rc))
13946 {
13947 mData->mSession.mLockedMedia.Clear();
13948 mrc = setError(rc,
13949 tr("Collecting locking information for all attached media failed"));
13950 break;
13951 }
13952 }
13953
13954 if (SUCCEEDED(mrc))
13955 {
13956 /* Now lock all media. If this fails, nothing is locked. */
13957 alock.release();
13958 HRESULT rc = mData->mSession.mLockedMedia.Lock();
13959 alock.acquire();
13960 if (FAILED(rc))
13961 {
13962 mrc = setError(rc,
13963 tr("Locking of attached media failed"));
13964 }
13965 }
13966
13967 return mrc;
13968}
13969
13970/**
13971 * Undoes the locks made by by #lockMedia().
13972 */
13973void SessionMachine::unlockMedia()
13974{
13975 AutoCaller autoCaller(this);
13976 AssertComRCReturnVoid(autoCaller.rc());
13977
13978 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13979
13980 /* we may be holding important error info on the current thread;
13981 * preserve it */
13982 ErrorInfoKeeper eik;
13983
13984 HRESULT rc = mData->mSession.mLockedMedia.Clear();
13985 AssertComRC(rc);
13986}
13987
13988/**
13989 * Helper to change the machine state (reimplementation).
13990 *
13991 * @note Locks this object for writing.
13992 * @note This method must not call saveSettings or SaveSettings, otherwise
13993 * it can cause crashes in random places due to unexpectedly committing
13994 * the current settings. The caller is responsible for that. The call
13995 * to saveStateSettings is fine, because this method does not commit.
13996 */
13997HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
13998{
13999 LogFlowThisFuncEnter();
14000 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14001
14002 AutoCaller autoCaller(this);
14003 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14004
14005 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14006
14007 MachineState_T oldMachineState = mData->mMachineState;
14008
14009 AssertMsgReturn(oldMachineState != aMachineState,
14010 ("oldMachineState=%s, aMachineState=%s\n",
14011 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14012 E_FAIL);
14013
14014 HRESULT rc = S_OK;
14015
14016 int stsFlags = 0;
14017 bool deleteSavedState = false;
14018
14019 /* detect some state transitions */
14020
14021 if ( ( oldMachineState == MachineState_Saved
14022 && aMachineState == MachineState_Restoring)
14023 || ( ( oldMachineState == MachineState_PoweredOff
14024 || oldMachineState == MachineState_Teleported
14025 || oldMachineState == MachineState_Aborted
14026 )
14027 && ( aMachineState == MachineState_TeleportingIn
14028 || aMachineState == MachineState_Starting
14029 )
14030 )
14031 )
14032 {
14033 /* The EMT thread is about to start */
14034
14035 /* Nothing to do here for now... */
14036
14037 /// @todo NEWMEDIA don't let mDVDDrive and other children
14038 /// change anything when in the Starting/Restoring state
14039 }
14040 else if ( ( oldMachineState == MachineState_Running
14041 || oldMachineState == MachineState_Paused
14042 || oldMachineState == MachineState_Teleporting
14043 || oldMachineState == MachineState_LiveSnapshotting
14044 || oldMachineState == MachineState_Stuck
14045 || oldMachineState == MachineState_Starting
14046 || oldMachineState == MachineState_Stopping
14047 || oldMachineState == MachineState_Saving
14048 || oldMachineState == MachineState_Restoring
14049 || oldMachineState == MachineState_TeleportingPausedVM
14050 || oldMachineState == MachineState_TeleportingIn
14051 )
14052 && ( aMachineState == MachineState_PoweredOff
14053 || aMachineState == MachineState_Saved
14054 || aMachineState == MachineState_Teleported
14055 || aMachineState == MachineState_Aborted
14056 )
14057 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14058 * snapshot */
14059 && ( mConsoleTaskData.mSnapshot.isNull()
14060 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14061 )
14062 )
14063 {
14064 /* The EMT thread has just stopped, unlock attached media. Note that as
14065 * opposed to locking that is done from Console, we do unlocking here
14066 * because the VM process may have aborted before having a chance to
14067 * properly unlock all media it locked. */
14068
14069 unlockMedia();
14070 }
14071
14072 if (oldMachineState == MachineState_Restoring)
14073 {
14074 if (aMachineState != MachineState_Saved)
14075 {
14076 /*
14077 * delete the saved state file once the machine has finished
14078 * restoring from it (note that Console sets the state from
14079 * Restoring to Saved if the VM couldn't restore successfully,
14080 * to give the user an ability to fix an error and retry --
14081 * we keep the saved state file in this case)
14082 */
14083 deleteSavedState = true;
14084 }
14085 }
14086 else if ( oldMachineState == MachineState_Saved
14087 && ( aMachineState == MachineState_PoweredOff
14088 || aMachineState == MachineState_Aborted
14089 || aMachineState == MachineState_Teleported
14090 )
14091 )
14092 {
14093 /*
14094 * delete the saved state after Console::ForgetSavedState() is called
14095 * or if the VM process (owning a direct VM session) crashed while the
14096 * VM was Saved
14097 */
14098
14099 /// @todo (dmik)
14100 // Not sure that deleting the saved state file just because of the
14101 // client death before it attempted to restore the VM is a good
14102 // thing. But when it crashes we need to go to the Aborted state
14103 // which cannot have the saved state file associated... The only
14104 // way to fix this is to make the Aborted condition not a VM state
14105 // but a bool flag: i.e., when a crash occurs, set it to true and
14106 // change the state to PoweredOff or Saved depending on the
14107 // saved state presence.
14108
14109 deleteSavedState = true;
14110 mData->mCurrentStateModified = TRUE;
14111 stsFlags |= SaveSTS_CurStateModified;
14112 }
14113
14114 if ( aMachineState == MachineState_Starting
14115 || aMachineState == MachineState_Restoring
14116 || aMachineState == MachineState_TeleportingIn
14117 )
14118 {
14119 /* set the current state modified flag to indicate that the current
14120 * state is no more identical to the state in the
14121 * current snapshot */
14122 if (!mData->mCurrentSnapshot.isNull())
14123 {
14124 mData->mCurrentStateModified = TRUE;
14125 stsFlags |= SaveSTS_CurStateModified;
14126 }
14127 }
14128
14129 if (deleteSavedState)
14130 {
14131 if (mRemoveSavedState)
14132 {
14133 Assert(!mSSData->strStateFilePath.isEmpty());
14134
14135 // it is safe to delete the saved state file if ...
14136 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14137 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14138 // ... none of the snapshots share the saved state file
14139 )
14140 RTFileDelete(mSSData->strStateFilePath.c_str());
14141 }
14142
14143 mSSData->strStateFilePath.setNull();
14144 stsFlags |= SaveSTS_StateFilePath;
14145 }
14146
14147 /* redirect to the underlying peer machine */
14148 mPeer->setMachineState(aMachineState);
14149
14150 if ( aMachineState == MachineState_PoweredOff
14151 || aMachineState == MachineState_Teleported
14152 || aMachineState == MachineState_Aborted
14153 || aMachineState == MachineState_Saved)
14154 {
14155 /* the machine has stopped execution
14156 * (or the saved state file was adopted) */
14157 stsFlags |= SaveSTS_StateTimeStamp;
14158 }
14159
14160 if ( ( oldMachineState == MachineState_PoweredOff
14161 || oldMachineState == MachineState_Aborted
14162 || oldMachineState == MachineState_Teleported
14163 )
14164 && aMachineState == MachineState_Saved)
14165 {
14166 /* the saved state file was adopted */
14167 Assert(!mSSData->strStateFilePath.isEmpty());
14168 stsFlags |= SaveSTS_StateFilePath;
14169 }
14170
14171#ifdef VBOX_WITH_GUEST_PROPS
14172 if ( aMachineState == MachineState_PoweredOff
14173 || aMachineState == MachineState_Aborted
14174 || aMachineState == MachineState_Teleported)
14175 {
14176 /* Make sure any transient guest properties get removed from the
14177 * property store on shutdown. */
14178
14179 HWData::GuestPropertyMap::const_iterator it;
14180 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14181 if (!fNeedsSaving)
14182 for (it = mHWData->mGuestProperties.begin();
14183 it != mHWData->mGuestProperties.end(); ++it)
14184 if ( (it->second.mFlags & guestProp::TRANSIENT)
14185 || (it->second.mFlags & guestProp::TRANSRESET))
14186 {
14187 fNeedsSaving = true;
14188 break;
14189 }
14190 if (fNeedsSaving)
14191 {
14192 mData->mCurrentStateModified = TRUE;
14193 stsFlags |= SaveSTS_CurStateModified;
14194 }
14195 }
14196#endif
14197
14198 rc = saveStateSettings(stsFlags);
14199
14200 if ( ( oldMachineState != MachineState_PoweredOff
14201 && oldMachineState != MachineState_Aborted
14202 && oldMachineState != MachineState_Teleported
14203 )
14204 && ( aMachineState == MachineState_PoweredOff
14205 || aMachineState == MachineState_Aborted
14206 || aMachineState == MachineState_Teleported
14207 )
14208 )
14209 {
14210 /* we've been shut down for any reason */
14211 /* no special action so far */
14212 }
14213
14214 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14215 LogFlowThisFuncLeave();
14216 return rc;
14217}
14218
14219/**
14220 * Sends the current machine state value to the VM process.
14221 *
14222 * @note Locks this object for reading, then calls a client process.
14223 */
14224HRESULT SessionMachine::updateMachineStateOnClient()
14225{
14226 AutoCaller autoCaller(this);
14227 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14228
14229 ComPtr<IInternalSessionControl> directControl;
14230 {
14231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14232 AssertReturn(!!mData, E_FAIL);
14233 directControl = mData->mSession.mDirectControl;
14234
14235 /* directControl may be already set to NULL here in #OnSessionEnd()
14236 * called too early by the direct session process while there is still
14237 * some operation (like deleting the snapshot) in progress. The client
14238 * process in this case is waiting inside Session::close() for the
14239 * "end session" process object to complete, while #uninit() called by
14240 * #checkForDeath() on the Watcher thread is waiting for the pending
14241 * operation to complete. For now, we accept this inconsistent behavior
14242 * and simply do nothing here. */
14243
14244 if (mData->mSession.mState == SessionState_Unlocking)
14245 return S_OK;
14246
14247 AssertReturn(!directControl.isNull(), E_FAIL);
14248 }
14249
14250 return directControl->UpdateMachineState(mData->mMachineState);
14251}
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