VirtualBox

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

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

Main/Machine+Console+Display+VRDEServer,VBoxManage: allow VMs without graphics controller, eliminate annoying spurious error messages about Console not yet powered up when taking screenshots, getting/setting guest properties and updating metrics, make as many parameters to modifyvm as possible case insensitive

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

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