VirtualBox

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

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

Main: assembler functions not required here

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 476.8 KB
Line 
1/* $Id: MachineImpl.cpp 45629 2013-04-19 07:19:43Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
27# include <errno.h>
28# include <sys/types.h>
29# include <sys/stat.h>
30# include <sys/ipc.h>
31# include <sys/sem.h>
32#endif
33
34#include "Logging.h"
35#include "VirtualBoxImpl.h"
36#include "MachineImpl.h"
37#include "ProgressImpl.h"
38#include "ProgressProxyImpl.h"
39#include "MediumAttachmentImpl.h"
40#include "MediumImpl.h"
41#include "MediumLock.h"
42#include "USBControllerImpl.h"
43#include "HostImpl.h"
44#include "SharedFolderImpl.h"
45#include "GuestOSTypeImpl.h"
46#include "VirtualBoxErrorInfoImpl.h"
47#include "GuestImpl.h"
48#include "StorageControllerImpl.h"
49#include "DisplayImpl.h"
50#include "DisplayUtils.h"
51#include "BandwidthControlImpl.h"
52#include "MachineImplCloneVM.h"
53#include "AutostartDb.h"
54
55// generated header
56#include "VBoxEvents.h"
57
58#ifdef VBOX_WITH_USB
59# include "USBProxyService.h"
60#endif
61
62#include "AutoCaller.h"
63#include "HashedPw.h"
64#include "Performance.h"
65
66#include <iprt/asm.h>
67#include <iprt/path.h>
68#include <iprt/dir.h>
69#include <iprt/env.h>
70#include <iprt/lockvalidator.h>
71#include <iprt/process.h>
72#include <iprt/cpp/utils.h>
73#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
74#include <iprt/sha.h>
75#include <iprt/string.h>
76
77#include <VBox/com/array.h>
78#include <VBox/com/list.h>
79
80#include <VBox/err.h>
81#include <VBox/param.h>
82#include <VBox/settings.h>
83#include <VBox/vmm/ssm.h>
84
85#ifdef VBOX_WITH_GUEST_PROPS
86# include <VBox/HostServices/GuestPropertySvc.h>
87# include <VBox/com/array.h>
88#endif
89
90#include "VBox/com/MultiResult.h"
91
92#include <algorithm>
93
94#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
95# define HOSTSUFF_EXE ".exe"
96#else /* !RT_OS_WINDOWS */
97# define HOSTSUFF_EXE ""
98#endif /* !RT_OS_WINDOWS */
99
100// defines / prototypes
101/////////////////////////////////////////////////////////////////////////////
102
103/////////////////////////////////////////////////////////////////////////////
104// Machine::Data structure
105/////////////////////////////////////////////////////////////////////////////
106
107Machine::Data::Data()
108{
109 mRegistered = FALSE;
110 pMachineConfigFile = NULL;
111 /* Contains hints on what has changed when the user is using the VM (config
112 * changes, running the VM, ...). This is used to decide if a config needs
113 * to be written to disk. */
114 flModifications = 0;
115 /* VM modification usually also trigger setting the current state to
116 * "Modified". Although this is not always the case. An e.g. is the VM
117 * initialization phase or when snapshot related data is changed. The
118 * actually behavior is controlled by the following flag. */
119 m_fAllowStateModification = false;
120 mAccessible = FALSE;
121 /* mUuid is initialized in Machine::init() */
122
123 mMachineState = MachineState_PoweredOff;
124 RTTimeNow(&mLastStateChange);
125
126 mMachineStateDeps = 0;
127 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
128 mMachineStateChangePending = 0;
129
130 mCurrentStateModified = TRUE;
131 mGuestPropertiesModified = FALSE;
132
133 mSession.mPID = NIL_RTPROCESS;
134 mSession.mState = SessionState_Unlocked;
135}
136
137Machine::Data::~Data()
138{
139 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
140 {
141 RTSemEventMultiDestroy(mMachineStateDepsSem);
142 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
143 }
144 if (pMachineConfigFile)
145 {
146 delete pMachineConfigFile;
147 pMachineConfigFile = NULL;
148 }
149}
150
151/////////////////////////////////////////////////////////////////////////////
152// Machine::HWData structure
153/////////////////////////////////////////////////////////////////////////////
154
155Machine::HWData::HWData()
156{
157 /* default values for a newly created machine */
158 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
159 mMemorySize = 128;
160 mCPUCount = 1;
161 mCPUHotPlugEnabled = false;
162 mMemoryBalloonSize = 0;
163 mPageFusionEnabled = false;
164 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 mVideoCaptureFile = "Test.webm";
169 mVideoCaptureWidth = 640;
170 mVideoCaptureHeight = 480;
171 mVideoCaptureEnabled = false;
172
173 mHWVirtExEnabled = true;
174 mHWVirtExNestedPagingEnabled = true;
175#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
176 mHWVirtExLargePagesEnabled = true;
177#else
178 /* Not supported on 32 bits hosts. */
179 mHWVirtExLargePagesEnabled = false;
180#endif
181 mHWVirtExVPIDEnabled = true;
182 mHWVirtExForceEnabled = false;
183#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
184 mHWVirtExExclusive = false;
185#else
186 mHWVirtExExclusive = true;
187#endif
188#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
189 mPAEEnabled = true;
190#else
191 mPAEEnabled = false;
192#endif
193 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
194 mSyntheticCpu = false;
195 mHPETEnabled = false;
196
197 /* default boot order: floppy - DVD - HDD */
198 mBootOrder[0] = DeviceType_Floppy;
199 mBootOrder[1] = DeviceType_DVD;
200 mBootOrder[2] = DeviceType_HardDisk;
201 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
202 mBootOrder[i] = DeviceType_Null;
203
204 mClipboardMode = ClipboardMode_Disabled;
205 mDragAndDropMode = DragAndDropMode_Disabled;
206 mGuestPropertyNotificationPatterns = "";
207
208 mFirmwareType = FirmwareType_BIOS;
209 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
210 mPointingHIDType = PointingHIDType_PS2Mouse;
211 mChipsetType = ChipsetType_PIIX3;
212 mEmulatedUSBWebcamEnabled = FALSE;
213 mEmulatedUSBCardReaderEnabled = FALSE;
214
215 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
216 mCPUAttached[i] = false;
217
218 mIOCacheEnabled = true;
219 mIOCacheSize = 5; /* 5MB */
220
221 /* Maximum CPU execution cap by default. */
222 mCpuExecutionCap = 100;
223}
224
225Machine::HWData::~HWData()
226{
227}
228
229/////////////////////////////////////////////////////////////////////////////
230// Machine::HDData structure
231/////////////////////////////////////////////////////////////////////////////
232
233Machine::MediaData::MediaData()
234{
235}
236
237Machine::MediaData::~MediaData()
238{
239}
240
241/////////////////////////////////////////////////////////////////////////////
242// Machine class
243/////////////////////////////////////////////////////////////////////////////
244
245// constructor / destructor
246/////////////////////////////////////////////////////////////////////////////
247
248Machine::Machine()
249 : mCollectorGuest(NULL),
250 mPeer(NULL),
251 mParent(NULL),
252 mSerialPorts(),
253 mParallelPorts(),
254 uRegistryNeedsSaving(0)
255{}
256
257Machine::~Machine()
258{}
259
260HRESULT Machine::FinalConstruct()
261{
262 LogFlowThisFunc(("\n"));
263 return BaseFinalConstruct();
264}
265
266void Machine::FinalRelease()
267{
268 LogFlowThisFunc(("\n"));
269 uninit();
270 BaseFinalRelease();
271}
272
273/**
274 * Initializes a new machine instance; this init() variant creates a new, empty machine.
275 * This gets called from VirtualBox::CreateMachine().
276 *
277 * @param aParent Associated parent object
278 * @param strConfigFile Local file system path to the VM settings file (can
279 * be relative to the VirtualBox config directory).
280 * @param strName name for the machine
281 * @param llGroups list of groups for the machine
282 * @param aOsType OS Type of this machine or NULL.
283 * @param aId UUID for the new machine.
284 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
285 *
286 * @return Success indicator. if not S_OK, the machine object is invalid
287 */
288HRESULT Machine::init(VirtualBox *aParent,
289 const Utf8Str &strConfigFile,
290 const Utf8Str &strName,
291 const StringsList &llGroups,
292 GuestOSType *aOsType,
293 const Guid &aId,
294 bool fForceOverwrite,
295 bool fDirectoryIncludesUUID)
296{
297 LogFlowThisFuncEnter();
298 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
299
300 /* Enclose the state transition NotReady->InInit->Ready */
301 AutoInitSpan autoInitSpan(this);
302 AssertReturn(autoInitSpan.isOk(), E_FAIL);
303
304 HRESULT rc = initImpl(aParent, strConfigFile);
305 if (FAILED(rc)) return rc;
306
307 rc = tryCreateMachineConfigFile(fForceOverwrite);
308 if (FAILED(rc)) return rc;
309
310 if (SUCCEEDED(rc))
311 {
312 // create an empty machine config
313 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
314
315 rc = initDataAndChildObjects();
316 }
317
318 if (SUCCEEDED(rc))
319 {
320 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
321 mData->mAccessible = TRUE;
322
323 unconst(mData->mUuid) = aId;
324
325 mUserData->s.strName = strName;
326
327 mUserData->s.llGroups = llGroups;
328
329 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
330 // the "name sync" flag determines whether the machine directory gets renamed along
331 // with the machine file; say so if the settings file name is the same as the
332 // settings file parent directory (machine directory)
333 mUserData->s.fNameSync = isInOwnDir();
334
335 // initialize the default snapshots folder
336 rc = COMSETTER(SnapshotFolder)(NULL);
337 AssertComRC(rc);
338
339 if (aOsType)
340 {
341 /* Store OS type */
342 mUserData->s.strOsType = aOsType->id();
343
344 /* Apply BIOS defaults */
345 mBIOSSettings->applyDefaults(aOsType);
346
347 /* Apply network adapters defaults */
348 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
349 mNetworkAdapters[slot]->applyDefaults(aOsType);
350
351 /* Apply serial port defaults */
352 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
353 mSerialPorts[slot]->applyDefaults(aOsType);
354
355 /* Let the OS type select 64-bit ness. */
356 mHWData->mLongMode = aOsType->is64Bit()
357 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
358 }
359
360 /* At this point the changing of the current state modification
361 * flag is allowed. */
362 allowStateModification();
363
364 /* commit all changes made during the initialization */
365 commit();
366 }
367
368 /* Confirm a successful initialization when it's the case */
369 if (SUCCEEDED(rc))
370 {
371 if (mData->mAccessible)
372 autoInitSpan.setSucceeded();
373 else
374 autoInitSpan.setLimited();
375 }
376
377 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
378 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
379 mData->mRegistered,
380 mData->mAccessible,
381 rc));
382
383 LogFlowThisFuncLeave();
384
385 return rc;
386}
387
388/**
389 * Initializes a new instance with data from machine XML (formerly Init_Registered).
390 * Gets called in two modes:
391 *
392 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
393 * UUID is specified and we mark the machine as "registered";
394 *
395 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
396 * and the machine remains unregistered until RegisterMachine() is called.
397 *
398 * @param aParent Associated parent object
399 * @param aConfigFile Local file system path to the VM settings file (can
400 * be relative to the VirtualBox config directory).
401 * @param aId UUID of the machine or NULL (see above).
402 *
403 * @return Success indicator. if not S_OK, the machine object is invalid
404 */
405HRESULT Machine::initFromSettings(VirtualBox *aParent,
406 const Utf8Str &strConfigFile,
407 const Guid *aId)
408{
409 LogFlowThisFuncEnter();
410 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
411
412 /* Enclose the state transition NotReady->InInit->Ready */
413 AutoInitSpan autoInitSpan(this);
414 AssertReturn(autoInitSpan.isOk(), E_FAIL);
415
416 HRESULT rc = initImpl(aParent, strConfigFile);
417 if (FAILED(rc)) return rc;
418
419 if (aId)
420 {
421 // loading a registered VM:
422 unconst(mData->mUuid) = *aId;
423 mData->mRegistered = TRUE;
424 // now load the settings from XML:
425 rc = registeredInit();
426 // this calls initDataAndChildObjects() and loadSettings()
427 }
428 else
429 {
430 // opening an unregistered VM (VirtualBox::OpenMachine()):
431 rc = initDataAndChildObjects();
432
433 if (SUCCEEDED(rc))
434 {
435 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
436 mData->mAccessible = TRUE;
437
438 try
439 {
440 // load and parse machine XML; this will throw on XML or logic errors
441 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
442
443 // reject VM UUID duplicates, they can happen if someone
444 // tries to register an already known VM config again
445 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
446 true /* fPermitInaccessible */,
447 false /* aDoSetError */,
448 NULL) != VBOX_E_OBJECT_NOT_FOUND)
449 {
450 throw setError(E_FAIL,
451 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
452 mData->m_strConfigFile.c_str());
453 }
454
455 // use UUID from machine config
456 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
457
458 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
459 NULL /* puuidRegistry */);
460 if (FAILED(rc)) throw rc;
461
462 /* At this point the changing of the current state modification
463 * flag is allowed. */
464 allowStateModification();
465
466 commit();
467 }
468 catch (HRESULT err)
469 {
470 /* we assume that error info is set by the thrower */
471 rc = err;
472 }
473 catch (...)
474 {
475 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
476 }
477 }
478 }
479
480 /* Confirm a successful initialization when it's the case */
481 if (SUCCEEDED(rc))
482 {
483 if (mData->mAccessible)
484 autoInitSpan.setSucceeded();
485 else
486 {
487 autoInitSpan.setLimited();
488
489 // uninit media from this machine's media registry, or else
490 // reloading the settings will fail
491 mParent->unregisterMachineMedia(getId());
492 }
493 }
494
495 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
496 "rc=%08X\n",
497 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
498 mData->mRegistered, mData->mAccessible, rc));
499
500 LogFlowThisFuncLeave();
501
502 return rc;
503}
504
505/**
506 * Initializes a new instance from a machine config that is already in memory
507 * (import OVF case). Since we are importing, the UUID in the machine
508 * config is ignored and we always generate a fresh one.
509 *
510 * @param strName Name for the new machine; this overrides what is specified in config and is used
511 * for the settings file as well.
512 * @param config Machine configuration loaded and parsed from XML.
513 *
514 * @return Success indicator. if not S_OK, the machine object is invalid
515 */
516HRESULT Machine::init(VirtualBox *aParent,
517 const Utf8Str &strName,
518 const settings::MachineConfigFile &config)
519{
520 LogFlowThisFuncEnter();
521
522 /* Enclose the state transition NotReady->InInit->Ready */
523 AutoInitSpan autoInitSpan(this);
524 AssertReturn(autoInitSpan.isOk(), E_FAIL);
525
526 Utf8Str strConfigFile;
527 aParent->getDefaultMachineFolder(strConfigFile);
528 strConfigFile.append(RTPATH_DELIMITER);
529 strConfigFile.append(strName);
530 strConfigFile.append(RTPATH_DELIMITER);
531 strConfigFile.append(strName);
532 strConfigFile.append(".vbox");
533
534 HRESULT rc = initImpl(aParent, strConfigFile);
535 if (FAILED(rc)) return rc;
536
537 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
538 if (FAILED(rc)) return rc;
539
540 rc = initDataAndChildObjects();
541
542 if (SUCCEEDED(rc))
543 {
544 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
545 mData->mAccessible = TRUE;
546
547 // create empty machine config for instance data
548 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
549
550 // generate fresh UUID, ignore machine config
551 unconst(mData->mUuid).create();
552
553 rc = loadMachineDataFromSettings(config,
554 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
555
556 // override VM name as well, it may be different
557 mUserData->s.strName = strName;
558
559 if (SUCCEEDED(rc))
560 {
561 /* At this point the changing of the current state modification
562 * flag is allowed. */
563 allowStateModification();
564
565 /* commit all changes made during the initialization */
566 commit();
567 }
568 }
569
570 /* Confirm a successful initialization when it's the case */
571 if (SUCCEEDED(rc))
572 {
573 if (mData->mAccessible)
574 autoInitSpan.setSucceeded();
575 else
576 {
577 autoInitSpan.setLimited();
578
579 // uninit media from this machine's media registry, or else
580 // reloading the settings will fail
581 mParent->unregisterMachineMedia(getId());
582 }
583 }
584
585 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
586 "rc=%08X\n",
587 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
588 mData->mRegistered, mData->mAccessible, rc));
589
590 LogFlowThisFuncLeave();
591
592 return rc;
593}
594
595/**
596 * Shared code between the various init() implementations.
597 * @param aParent
598 * @return
599 */
600HRESULT Machine::initImpl(VirtualBox *aParent,
601 const Utf8Str &strConfigFile)
602{
603 LogFlowThisFuncEnter();
604
605 AssertReturn(aParent, E_INVALIDARG);
606 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
607
608 HRESULT rc = S_OK;
609
610 /* share the parent weakly */
611 unconst(mParent) = aParent;
612
613 /* allocate the essential machine data structure (the rest will be
614 * allocated later by initDataAndChildObjects() */
615 mData.allocate();
616
617 /* memorize the config file name (as provided) */
618 mData->m_strConfigFile = strConfigFile;
619
620 /* get the full file name */
621 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
622 if (RT_FAILURE(vrc1))
623 return setError(VBOX_E_FILE_ERROR,
624 tr("Invalid machine settings file name '%s' (%Rrc)"),
625 strConfigFile.c_str(),
626 vrc1);
627
628 LogFlowThisFuncLeave();
629
630 return rc;
631}
632
633/**
634 * Tries to create a machine settings file in the path stored in the machine
635 * instance data. Used when a new machine is created to fail gracefully if
636 * the settings file could not be written (e.g. because machine dir is read-only).
637 * @return
638 */
639HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
640{
641 HRESULT rc = S_OK;
642
643 // when we create a new machine, we must be able to create the settings file
644 RTFILE f = NIL_RTFILE;
645 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
646 if ( RT_SUCCESS(vrc)
647 || vrc == VERR_SHARING_VIOLATION
648 )
649 {
650 if (RT_SUCCESS(vrc))
651 RTFileClose(f);
652 if (!fForceOverwrite)
653 rc = setError(VBOX_E_FILE_ERROR,
654 tr("Machine settings file '%s' already exists"),
655 mData->m_strConfigFileFull.c_str());
656 else
657 {
658 /* try to delete the config file, as otherwise the creation
659 * of a new settings file will fail. */
660 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
661 if (RT_FAILURE(vrc2))
662 rc = setError(VBOX_E_FILE_ERROR,
663 tr("Could not delete the existing settings file '%s' (%Rrc)"),
664 mData->m_strConfigFileFull.c_str(), vrc2);
665 }
666 }
667 else if ( vrc != VERR_FILE_NOT_FOUND
668 && vrc != VERR_PATH_NOT_FOUND
669 )
670 rc = setError(VBOX_E_FILE_ERROR,
671 tr("Invalid machine settings file name '%s' (%Rrc)"),
672 mData->m_strConfigFileFull.c_str(),
673 vrc);
674 return rc;
675}
676
677/**
678 * Initializes the registered machine by loading the settings file.
679 * This method is separated from #init() in order to make it possible to
680 * retry the operation after VirtualBox startup instead of refusing to
681 * startup the whole VirtualBox server in case if the settings file of some
682 * registered VM is invalid or inaccessible.
683 *
684 * @note Must be always called from this object's write lock
685 * (unless called from #init() that doesn't need any locking).
686 * @note Locks the mUSBController method for writing.
687 * @note Subclasses must not call this method.
688 */
689HRESULT Machine::registeredInit()
690{
691 AssertReturn(!isSessionMachine(), E_FAIL);
692 AssertReturn(!isSnapshotMachine(), E_FAIL);
693 AssertReturn(mData->mUuid.isValid(), E_FAIL);
694 AssertReturn(!mData->mAccessible, E_FAIL);
695
696 HRESULT rc = initDataAndChildObjects();
697
698 if (SUCCEEDED(rc))
699 {
700 /* Temporarily reset the registered flag in order to let setters
701 * potentially called from loadSettings() succeed (isMutable() used in
702 * all setters will return FALSE for a Machine instance if mRegistered
703 * is TRUE). */
704 mData->mRegistered = FALSE;
705
706 try
707 {
708 // load and parse machine XML; this will throw on XML or logic errors
709 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
710
711 if (mData->mUuid != mData->pMachineConfigFile->uuid)
712 throw setError(E_FAIL,
713 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
714 mData->pMachineConfigFile->uuid.raw(),
715 mData->m_strConfigFileFull.c_str(),
716 mData->mUuid.toString().c_str(),
717 mParent->settingsFilePath().c_str());
718
719 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
720 NULL /* const Guid *puuidRegistry */);
721 if (FAILED(rc)) throw rc;
722 }
723 catch (HRESULT err)
724 {
725 /* we assume that error info is set by the thrower */
726 rc = err;
727 }
728 catch (...)
729 {
730 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
731 }
732
733 /* Restore the registered flag (even on failure) */
734 mData->mRegistered = TRUE;
735 }
736
737 if (SUCCEEDED(rc))
738 {
739 /* Set mAccessible to TRUE only if we successfully locked and loaded
740 * the settings file */
741 mData->mAccessible = TRUE;
742
743 /* commit all changes made during loading the settings file */
744 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
745 /// @todo r=klaus for some reason the settings loading logic backs up
746 // the settings, and therefore a commit is needed. Should probably be changed.
747 }
748 else
749 {
750 /* If the machine is registered, then, instead of returning a
751 * failure, we mark it as inaccessible and set the result to
752 * success to give it a try later */
753
754 /* fetch the current error info */
755 mData->mAccessError = com::ErrorInfo();
756 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
757 mData->mUuid.raw(),
758 mData->mAccessError.getText().raw()));
759
760 /* rollback all changes */
761 rollback(false /* aNotify */);
762
763 // uninit media from this machine's media registry, or else
764 // reloading the settings will fail
765 mParent->unregisterMachineMedia(getId());
766
767 /* uninitialize the common part to make sure all data is reset to
768 * default (null) values */
769 uninitDataAndChildObjects();
770
771 rc = S_OK;
772 }
773
774 return rc;
775}
776
777/**
778 * Uninitializes the instance.
779 * Called either from FinalRelease() or by the parent when it gets destroyed.
780 *
781 * @note The caller of this method must make sure that this object
782 * a) doesn't have active callers on the current thread and b) is not locked
783 * by the current thread; otherwise uninit() will hang either a) due to
784 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
785 * a dead-lock caused by this thread waiting for all callers on the other
786 * threads are done but preventing them from doing so by holding a lock.
787 */
788void Machine::uninit()
789{
790 LogFlowThisFuncEnter();
791
792 Assert(!isWriteLockOnCurrentThread());
793
794 Assert(!uRegistryNeedsSaving);
795 if (uRegistryNeedsSaving)
796 {
797 AutoCaller autoCaller(this);
798 if (SUCCEEDED(autoCaller.rc()))
799 {
800 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
801 saveSettings(NULL, Machine::SaveS_Force);
802 }
803 }
804
805 /* Enclose the state transition Ready->InUninit->NotReady */
806 AutoUninitSpan autoUninitSpan(this);
807 if (autoUninitSpan.uninitDone())
808 return;
809
810 Assert(!isSnapshotMachine());
811 Assert(!isSessionMachine());
812 Assert(!!mData);
813
814 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
815 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
816
817 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
818
819 if (!mData->mSession.mMachine.isNull())
820 {
821 /* Theoretically, this can only happen if the VirtualBox server has been
822 * terminated while there were clients running that owned open direct
823 * sessions. Since in this case we are definitely called by
824 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
825 * won't happen on the client watcher thread (because it does
826 * VirtualBox::addCaller() for the duration of the
827 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
828 * cannot happen until the VirtualBox caller is released). This is
829 * important, because SessionMachine::uninit() cannot correctly operate
830 * after we return from this method (it expects the Machine instance is
831 * still valid). We'll call it ourselves below.
832 */
833 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
834 (SessionMachine*)mData->mSession.mMachine));
835
836 if (Global::IsOnlineOrTransient(mData->mMachineState))
837 {
838 LogWarningThisFunc(("Setting state to Aborted!\n"));
839 /* set machine state using SessionMachine reimplementation */
840 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
841 }
842
843 /*
844 * Uninitialize SessionMachine using public uninit() to indicate
845 * an unexpected uninitialization.
846 */
847 mData->mSession.mMachine->uninit();
848 /* SessionMachine::uninit() must set mSession.mMachine to null */
849 Assert(mData->mSession.mMachine.isNull());
850 }
851
852 // uninit media from this machine's media registry, if they're still there
853 Guid uuidMachine(getId());
854
855 /* the lock is no more necessary (SessionMachine is uninitialized) */
856 alock.release();
857
858 /* XXX This will fail with
859 * "cannot be closed because it is still attached to 1 virtual machines"
860 * because at this point we did not call uninitDataAndChildObjects() yet
861 * and therefore also removeBackReference() for all these mediums was not called! */
862
863 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
864 mParent->unregisterMachineMedia(uuidMachine);
865
866 // has machine been modified?
867 if (mData->flModifications)
868 {
869 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
870 rollback(false /* aNotify */);
871 }
872
873 if (mData->mAccessible)
874 uninitDataAndChildObjects();
875
876 /* free the essential data structure last */
877 mData.free();
878
879 LogFlowThisFuncLeave();
880}
881
882// IMachine properties
883/////////////////////////////////////////////////////////////////////////////
884
885STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
886{
887 CheckComArgOutPointerValid(aParent);
888
889 AutoLimitedCaller autoCaller(this);
890 if (FAILED(autoCaller.rc())) return autoCaller.rc();
891
892 /* mParent is constant during life time, no need to lock */
893 ComObjPtr<VirtualBox> pVirtualBox(mParent);
894 pVirtualBox.queryInterfaceTo(aParent);
895
896 return S_OK;
897}
898
899STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
900{
901 CheckComArgOutPointerValid(aAccessible);
902
903 AutoLimitedCaller autoCaller(this);
904 if (FAILED(autoCaller.rc())) return autoCaller.rc();
905
906 LogFlowThisFunc(("ENTER\n"));
907
908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
909
910 HRESULT rc = S_OK;
911
912 if (!mData->mAccessible)
913 {
914 /* try to initialize the VM once more if not accessible */
915
916 AutoReinitSpan autoReinitSpan(this);
917 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
918
919#ifdef DEBUG
920 LogFlowThisFunc(("Dumping media backreferences\n"));
921 mParent->dumpAllBackRefs();
922#endif
923
924 if (mData->pMachineConfigFile)
925 {
926 // reset the XML file to force loadSettings() (called from registeredInit())
927 // to parse it again; the file might have changed
928 delete mData->pMachineConfigFile;
929 mData->pMachineConfigFile = NULL;
930 }
931
932 rc = registeredInit();
933
934 if (SUCCEEDED(rc) && mData->mAccessible)
935 {
936 autoReinitSpan.setSucceeded();
937
938 /* make sure interesting parties will notice the accessibility
939 * state change */
940 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
941 mParent->onMachineDataChange(mData->mUuid);
942 }
943 }
944
945 if (SUCCEEDED(rc))
946 *aAccessible = mData->mAccessible;
947
948 LogFlowThisFuncLeave();
949
950 return rc;
951}
952
953STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
954{
955 CheckComArgOutPointerValid(aAccessError);
956
957 AutoLimitedCaller autoCaller(this);
958 if (FAILED(autoCaller.rc())) return autoCaller.rc();
959
960 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
961
962 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
963 {
964 /* return shortly */
965 aAccessError = NULL;
966 return S_OK;
967 }
968
969 HRESULT rc = S_OK;
970
971 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
972 rc = errorInfo.createObject();
973 if (SUCCEEDED(rc))
974 {
975 errorInfo->init(mData->mAccessError.getResultCode(),
976 mData->mAccessError.getInterfaceID().ref(),
977 Utf8Str(mData->mAccessError.getComponent()).c_str(),
978 Utf8Str(mData->mAccessError.getText()));
979 rc = errorInfo.queryInterfaceTo(aAccessError);
980 }
981
982 return rc;
983}
984
985STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
986{
987 CheckComArgOutPointerValid(aName);
988
989 AutoCaller autoCaller(this);
990 if (FAILED(autoCaller.rc())) return autoCaller.rc();
991
992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
993
994 mUserData->s.strName.cloneTo(aName);
995
996 return S_OK;
997}
998
999STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
1000{
1001 CheckComArgStrNotEmptyOrNull(aName);
1002
1003 AutoCaller autoCaller(this);
1004 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1005
1006 // prohibit setting a UUID only as the machine name, or else it can
1007 // never be found by findMachine()
1008 Guid test(aName);
1009
1010 if (test.isValid())
1011 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1012
1013 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1014
1015 HRESULT rc = checkStateDependency(MutableStateDep);
1016 if (FAILED(rc)) return rc;
1017
1018 setModified(IsModified_MachineData);
1019 mUserData.backup();
1020 mUserData->s.strName = aName;
1021
1022 return S_OK;
1023}
1024
1025STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1026{
1027 CheckComArgOutPointerValid(aDescription);
1028
1029 AutoCaller autoCaller(this);
1030 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1031
1032 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1033
1034 mUserData->s.strDescription.cloneTo(aDescription);
1035
1036 return S_OK;
1037}
1038
1039STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1040{
1041 AutoCaller autoCaller(this);
1042 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1043
1044 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1045
1046 // this can be done in principle in any state as it doesn't affect the VM
1047 // significantly, but play safe by not messing around while complex
1048 // activities are going on
1049 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1050 if (FAILED(rc)) return rc;
1051
1052 setModified(IsModified_MachineData);
1053 mUserData.backup();
1054 mUserData->s.strDescription = aDescription;
1055
1056 return S_OK;
1057}
1058
1059STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1060{
1061 CheckComArgOutPointerValid(aId);
1062
1063 AutoLimitedCaller autoCaller(this);
1064 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1065
1066 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1067
1068 mData->mUuid.toUtf16().cloneTo(aId);
1069
1070 return S_OK;
1071}
1072
1073STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1074{
1075 CheckComArgOutSafeArrayPointerValid(aGroups);
1076
1077 AutoCaller autoCaller(this);
1078 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1079
1080 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1081 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1082 size_t i = 0;
1083 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1084 it != mUserData->s.llGroups.end();
1085 ++it, i++)
1086 {
1087 Bstr tmp = *it;
1088 tmp.cloneTo(&groups[i]);
1089 }
1090 groups.detachTo(ComSafeArrayOutArg(aGroups));
1091
1092 return S_OK;
1093}
1094
1095STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1096{
1097 AutoCaller autoCaller(this);
1098 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1099
1100 StringsList llGroups;
1101 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1102 if (FAILED(rc))
1103 return rc;
1104
1105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1106
1107 // changing machine groups is possible while the VM is offline
1108 rc = checkStateDependency(OfflineStateDep);
1109 if (FAILED(rc)) return rc;
1110
1111 setModified(IsModified_MachineData);
1112 mUserData.backup();
1113 mUserData->s.llGroups = llGroups;
1114
1115 return S_OK;
1116}
1117
1118STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1119{
1120 CheckComArgOutPointerValid(aOSTypeId);
1121
1122 AutoCaller autoCaller(this);
1123 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1124
1125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1126
1127 mUserData->s.strOsType.cloneTo(aOSTypeId);
1128
1129 return S_OK;
1130}
1131
1132STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1133{
1134 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1135
1136 AutoCaller autoCaller(this);
1137 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1138
1139 /* look up the object by Id to check it is valid */
1140 ComPtr<IGuestOSType> guestOSType;
1141 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1142 if (FAILED(rc)) return rc;
1143
1144 /* when setting, always use the "etalon" value for consistency -- lookup
1145 * by ID is case-insensitive and the input value may have different case */
1146 Bstr osTypeId;
1147 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1148 if (FAILED(rc)) return rc;
1149
1150 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1151
1152 rc = checkStateDependency(MutableStateDep);
1153 if (FAILED(rc)) return rc;
1154
1155 setModified(IsModified_MachineData);
1156 mUserData.backup();
1157 mUserData->s.strOsType = osTypeId;
1158
1159 return S_OK;
1160}
1161
1162
1163STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1164{
1165 CheckComArgOutPointerValid(aFirmwareType);
1166
1167 AutoCaller autoCaller(this);
1168 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1169
1170 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1171
1172 *aFirmwareType = mHWData->mFirmwareType;
1173
1174 return S_OK;
1175}
1176
1177STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1178{
1179 AutoCaller autoCaller(this);
1180 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1181 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1182
1183 HRESULT rc = checkStateDependency(MutableStateDep);
1184 if (FAILED(rc)) return rc;
1185
1186 setModified(IsModified_MachineData);
1187 mHWData.backup();
1188 mHWData->mFirmwareType = aFirmwareType;
1189
1190 return S_OK;
1191}
1192
1193STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1194{
1195 CheckComArgOutPointerValid(aKeyboardHIDType);
1196
1197 AutoCaller autoCaller(this);
1198 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1199
1200 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1201
1202 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1203
1204 return S_OK;
1205}
1206
1207STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1208{
1209 AutoCaller autoCaller(this);
1210 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1211 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1212
1213 HRESULT rc = checkStateDependency(MutableStateDep);
1214 if (FAILED(rc)) return rc;
1215
1216 setModified(IsModified_MachineData);
1217 mHWData.backup();
1218 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1219
1220 return S_OK;
1221}
1222
1223STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1224{
1225 CheckComArgOutPointerValid(aPointingHIDType);
1226
1227 AutoCaller autoCaller(this);
1228 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1229
1230 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1231
1232 *aPointingHIDType = mHWData->mPointingHIDType;
1233
1234 return S_OK;
1235}
1236
1237STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1238{
1239 AutoCaller autoCaller(this);
1240 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1241 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1242
1243 HRESULT rc = checkStateDependency(MutableStateDep);
1244 if (FAILED(rc)) return rc;
1245
1246 setModified(IsModified_MachineData);
1247 mHWData.backup();
1248 mHWData->mPointingHIDType = aPointingHIDType;
1249
1250 return S_OK;
1251}
1252
1253STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1254{
1255 CheckComArgOutPointerValid(aChipsetType);
1256
1257 AutoCaller autoCaller(this);
1258 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1259
1260 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1261
1262 *aChipsetType = mHWData->mChipsetType;
1263
1264 return S_OK;
1265}
1266
1267STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1268{
1269 AutoCaller autoCaller(this);
1270 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1271 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1272
1273 HRESULT rc = checkStateDependency(MutableStateDep);
1274 if (FAILED(rc)) return rc;
1275
1276 if (aChipsetType != mHWData->mChipsetType)
1277 {
1278 setModified(IsModified_MachineData);
1279 mHWData.backup();
1280 mHWData->mChipsetType = aChipsetType;
1281
1282 // Resize network adapter array, to be finalized on commit/rollback.
1283 // We must not throw away entries yet, otherwise settings are lost
1284 // without a way to roll back.
1285 uint32_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1286 uint32_t oldCount = mNetworkAdapters.size();
1287 if (newCount > oldCount)
1288 {
1289 mNetworkAdapters.resize(newCount);
1290 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1291 {
1292 unconst(mNetworkAdapters[slot]).createObject();
1293 mNetworkAdapters[slot]->init(this, slot);
1294 }
1295 }
1296 }
1297
1298 return S_OK;
1299}
1300
1301STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1302{
1303 CheckComArgOutPointerValid(aHWVersion);
1304
1305 AutoCaller autoCaller(this);
1306 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1307
1308 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1309
1310 mHWData->mHWVersion.cloneTo(aHWVersion);
1311
1312 return S_OK;
1313}
1314
1315STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1316{
1317 /* check known version */
1318 Utf8Str hwVersion = aHWVersion;
1319 if ( hwVersion.compare("1") != 0
1320 && hwVersion.compare("2") != 0)
1321 return setError(E_INVALIDARG,
1322 tr("Invalid hardware version: %ls\n"), aHWVersion);
1323
1324 AutoCaller autoCaller(this);
1325 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1326
1327 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1328
1329 HRESULT rc = checkStateDependency(MutableStateDep);
1330 if (FAILED(rc)) return rc;
1331
1332 setModified(IsModified_MachineData);
1333 mHWData.backup();
1334 mHWData->mHWVersion = hwVersion;
1335
1336 return S_OK;
1337}
1338
1339STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1340{
1341 CheckComArgOutPointerValid(aUUID);
1342
1343 AutoCaller autoCaller(this);
1344 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1345
1346 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1347
1348 if (mHWData->mHardwareUUID.isValid())
1349 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1350 else
1351 mData->mUuid.toUtf16().cloneTo(aUUID);
1352
1353 return S_OK;
1354}
1355
1356STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1357{
1358 Guid hardwareUUID(aUUID);
1359 if (!hardwareUUID.isValid())
1360 return E_INVALIDARG;
1361
1362 AutoCaller autoCaller(this);
1363 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1364
1365 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1366
1367 HRESULT rc = checkStateDependency(MutableStateDep);
1368 if (FAILED(rc)) return rc;
1369
1370 setModified(IsModified_MachineData);
1371 mHWData.backup();
1372 if (hardwareUUID == mData->mUuid)
1373 mHWData->mHardwareUUID.clear();
1374 else
1375 mHWData->mHardwareUUID = hardwareUUID;
1376
1377 return S_OK;
1378}
1379
1380STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1381{
1382 CheckComArgOutPointerValid(memorySize);
1383
1384 AutoCaller autoCaller(this);
1385 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1386
1387 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1388
1389 *memorySize = mHWData->mMemorySize;
1390
1391 return S_OK;
1392}
1393
1394STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1395{
1396 /* check RAM limits */
1397 if ( memorySize < MM_RAM_MIN_IN_MB
1398 || memorySize > MM_RAM_MAX_IN_MB
1399 )
1400 return setError(E_INVALIDARG,
1401 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1402 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1403
1404 AutoCaller autoCaller(this);
1405 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1406
1407 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1408
1409 HRESULT rc = checkStateDependency(MutableStateDep);
1410 if (FAILED(rc)) return rc;
1411
1412 setModified(IsModified_MachineData);
1413 mHWData.backup();
1414 mHWData->mMemorySize = memorySize;
1415
1416 return S_OK;
1417}
1418
1419STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1420{
1421 CheckComArgOutPointerValid(CPUCount);
1422
1423 AutoCaller autoCaller(this);
1424 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1425
1426 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1427
1428 *CPUCount = mHWData->mCPUCount;
1429
1430 return S_OK;
1431}
1432
1433STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1434{
1435 /* check CPU limits */
1436 if ( CPUCount < SchemaDefs::MinCPUCount
1437 || CPUCount > SchemaDefs::MaxCPUCount
1438 )
1439 return setError(E_INVALIDARG,
1440 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1441 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1442
1443 AutoCaller autoCaller(this);
1444 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1445
1446 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1447
1448 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1449 if (mHWData->mCPUHotPlugEnabled)
1450 {
1451 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1452 {
1453 if (mHWData->mCPUAttached[idx])
1454 return setError(E_INVALIDARG,
1455 tr("There is still a CPU attached to socket %lu."
1456 "Detach the CPU before removing the socket"),
1457 CPUCount, idx+1);
1458 }
1459 }
1460
1461 HRESULT rc = checkStateDependency(MutableStateDep);
1462 if (FAILED(rc)) return rc;
1463
1464 setModified(IsModified_MachineData);
1465 mHWData.backup();
1466 mHWData->mCPUCount = CPUCount;
1467
1468 return S_OK;
1469}
1470
1471STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1472{
1473 CheckComArgOutPointerValid(aExecutionCap);
1474
1475 AutoCaller autoCaller(this);
1476 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1477
1478 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1479
1480 *aExecutionCap = mHWData->mCpuExecutionCap;
1481
1482 return S_OK;
1483}
1484
1485STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1486{
1487 HRESULT rc = S_OK;
1488
1489 /* check throttle limits */
1490 if ( aExecutionCap < 1
1491 || aExecutionCap > 100
1492 )
1493 return setError(E_INVALIDARG,
1494 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1495 aExecutionCap, 1, 100);
1496
1497 AutoCaller autoCaller(this);
1498 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1499
1500 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1501
1502 alock.release();
1503 rc = onCPUExecutionCapChange(aExecutionCap);
1504 alock.acquire();
1505 if (FAILED(rc)) return rc;
1506
1507 setModified(IsModified_MachineData);
1508 mHWData.backup();
1509 mHWData->mCpuExecutionCap = aExecutionCap;
1510
1511 /* Save settings if online - todo why is this required?? */
1512 if (Global::IsOnline(mData->mMachineState))
1513 saveSettings(NULL);
1514
1515 return S_OK;
1516}
1517
1518
1519STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *enabled)
1520{
1521 CheckComArgOutPointerValid(enabled);
1522
1523 AutoCaller autoCaller(this);
1524 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1525
1526 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1527
1528 *enabled = mHWData->mCPUHotPlugEnabled;
1529
1530 return S_OK;
1531}
1532
1533STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled)
1534{
1535 HRESULT rc = S_OK;
1536
1537 AutoCaller autoCaller(this);
1538 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1539
1540 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1541
1542 rc = checkStateDependency(MutableStateDep);
1543 if (FAILED(rc)) return rc;
1544
1545 if (mHWData->mCPUHotPlugEnabled != enabled)
1546 {
1547 if (enabled)
1548 {
1549 setModified(IsModified_MachineData);
1550 mHWData.backup();
1551
1552 /* Add the amount of CPUs currently attached */
1553 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1554 {
1555 mHWData->mCPUAttached[i] = true;
1556 }
1557 }
1558 else
1559 {
1560 /*
1561 * We can disable hotplug only if the amount of maximum CPUs is equal
1562 * to the amount of attached CPUs
1563 */
1564 unsigned cCpusAttached = 0;
1565 unsigned iHighestId = 0;
1566
1567 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1568 {
1569 if (mHWData->mCPUAttached[i])
1570 {
1571 cCpusAttached++;
1572 iHighestId = i;
1573 }
1574 }
1575
1576 if ( (cCpusAttached != mHWData->mCPUCount)
1577 || (iHighestId >= mHWData->mCPUCount))
1578 return setError(E_INVALIDARG,
1579 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1580
1581 setModified(IsModified_MachineData);
1582 mHWData.backup();
1583 }
1584 }
1585
1586 mHWData->mCPUHotPlugEnabled = enabled;
1587
1588 return rc;
1589}
1590
1591STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *enabled)
1592{
1593#ifdef VBOX_WITH_USB_CARDREADER
1594 CheckComArgOutPointerValid(enabled);
1595
1596 AutoCaller autoCaller(this);
1597 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1598
1599 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1600
1601 *enabled = mHWData->mEmulatedUSBCardReaderEnabled;
1602
1603 return S_OK;
1604#else
1605 NOREF(enabled);
1606 return E_NOTIMPL;
1607#endif
1608}
1609
1610STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL enabled)
1611{
1612#ifdef VBOX_WITH_USB_CARDREADER
1613 AutoCaller autoCaller(this);
1614 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1615 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1616
1617 HRESULT rc = checkStateDependency(MutableStateDep);
1618 if (FAILED(rc)) return rc;
1619
1620 setModified(IsModified_MachineData);
1621 mHWData.backup();
1622 mHWData->mEmulatedUSBCardReaderEnabled = enabled;
1623
1624 return S_OK;
1625#else
1626 NOREF(enabled);
1627 return E_NOTIMPL;
1628#endif
1629}
1630
1631STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *enabled)
1632{
1633#ifdef VBOX_WITH_USB_VIDEO
1634 CheckComArgOutPointerValid(enabled);
1635
1636 AutoCaller autoCaller(this);
1637 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1638
1639 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1640
1641 *enabled = mHWData->mEmulatedUSBWebcamEnabled;
1642
1643 return S_OK;
1644#else
1645 NOREF(enabled);
1646 return E_NOTIMPL;
1647#endif
1648}
1649
1650STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL enabled)
1651{
1652#ifdef VBOX_WITH_USB_VIDEO
1653 AutoCaller autoCaller(this);
1654 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1655 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1656
1657 HRESULT rc = checkStateDependency(MutableStateDep);
1658 if (FAILED(rc)) return rc;
1659
1660 setModified(IsModified_MachineData);
1661 mHWData.backup();
1662 mHWData->mEmulatedUSBWebcamEnabled = enabled;
1663
1664 return S_OK;
1665#else
1666 NOREF(enabled);
1667 return E_NOTIMPL;
1668#endif
1669}
1670
1671STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *enabled)
1672{
1673 CheckComArgOutPointerValid(enabled);
1674
1675 AutoCaller autoCaller(this);
1676 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1678
1679 *enabled = mHWData->mHPETEnabled;
1680
1681 return S_OK;
1682}
1683
1684STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL enabled)
1685{
1686 HRESULT rc = S_OK;
1687
1688 AutoCaller autoCaller(this);
1689 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1690 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1691
1692 rc = checkStateDependency(MutableStateDep);
1693 if (FAILED(rc)) return rc;
1694
1695 setModified(IsModified_MachineData);
1696 mHWData.backup();
1697
1698 mHWData->mHPETEnabled = enabled;
1699
1700 return rc;
1701}
1702
1703STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
1704{
1705 AutoCaller autoCaller(this);
1706 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1707
1708 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1709
1710 *fEnabled = mHWData->mVideoCaptureEnabled;
1711 return S_OK;
1712}
1713
1714STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1715{
1716 AutoCaller autoCaller(this);
1717 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1718
1719 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1720 mHWData->mVideoCaptureEnabled = fEnabled;
1721 return S_OK;
1722}
1723
1724STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR * apFile)
1725{
1726 AutoCaller autoCaller(this);
1727 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1728
1729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1730 mHWData->mVideoCaptureFile.cloneTo(apFile);
1731 return S_OK;
1732}
1733
1734STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1735{
1736 Utf8Str strFile(aFile);
1737 AutoCaller autoCaller(this);
1738 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1739
1740 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1741 if (strFile.isEmpty())
1742 strFile = "VideoCap.webm";
1743 mHWData->mVideoCaptureFile = strFile;
1744 return S_OK;
1745}
1746
1747
1748STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *ulHorzRes)
1749{
1750 AutoCaller autoCaller(this);
1751 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1752
1753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1754 *ulHorzRes = mHWData->mVideoCaptureWidth;
1755 return S_OK;
1756}
1757
1758STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG ulHorzRes)
1759{
1760 AutoCaller autoCaller(this);
1761 if (FAILED(autoCaller.rc()))
1762 {
1763 LogFlow(("Autolocked failed\n"));
1764 return autoCaller.rc();
1765 }
1766
1767 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1768 mHWData->mVideoCaptureWidth = ulHorzRes;
1769 return S_OK;
1770}
1771
1772STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *ulVertRes)
1773{
1774 AutoCaller autoCaller(this);
1775 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1776
1777 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1778 *ulVertRes = mHWData->mVideoCaptureHeight;
1779 return S_OK;
1780}
1781
1782STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG ulVertRes)
1783{
1784 AutoCaller autoCaller(this);
1785 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1786
1787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1788 mHWData->mVideoCaptureHeight = ulVertRes;
1789 return S_OK;
1790}
1791
1792STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1793{
1794 CheckComArgOutPointerValid(memorySize);
1795
1796 AutoCaller autoCaller(this);
1797 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1798
1799 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1800
1801 *memorySize = mHWData->mVRAMSize;
1802
1803 return S_OK;
1804}
1805
1806STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1807{
1808 /* check VRAM limits */
1809 if (memorySize < SchemaDefs::MinGuestVRAM ||
1810 memorySize > SchemaDefs::MaxGuestVRAM)
1811 return setError(E_INVALIDARG,
1812 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1813 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1814
1815 AutoCaller autoCaller(this);
1816 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1817
1818 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1819
1820 HRESULT rc = checkStateDependency(MutableStateDep);
1821 if (FAILED(rc)) return rc;
1822
1823 setModified(IsModified_MachineData);
1824 mHWData.backup();
1825 mHWData->mVRAMSize = memorySize;
1826
1827 return S_OK;
1828}
1829
1830/** @todo this method should not be public */
1831STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1832{
1833 CheckComArgOutPointerValid(memoryBalloonSize);
1834
1835 AutoCaller autoCaller(this);
1836 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1837
1838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1839
1840 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
1841
1842 return S_OK;
1843}
1844
1845/**
1846 * Set the memory balloon size.
1847 *
1848 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1849 * we have to make sure that we never call IGuest from here.
1850 */
1851STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
1852{
1853 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1854#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1855 /* check limits */
1856 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1857 return setError(E_INVALIDARG,
1858 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1859 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1860
1861 AutoCaller autoCaller(this);
1862 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1863
1864 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1865
1866 setModified(IsModified_MachineData);
1867 mHWData.backup();
1868 mHWData->mMemoryBalloonSize = memoryBalloonSize;
1869
1870 return S_OK;
1871#else
1872 NOREF(memoryBalloonSize);
1873 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1874#endif
1875}
1876
1877STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *enabled)
1878{
1879 CheckComArgOutPointerValid(enabled);
1880
1881 AutoCaller autoCaller(this);
1882 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1883
1884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1885
1886 *enabled = mHWData->mPageFusionEnabled;
1887 return S_OK;
1888}
1889
1890STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL enabled)
1891{
1892#ifdef VBOX_WITH_PAGE_SHARING
1893 AutoCaller autoCaller(this);
1894 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1895
1896 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1897
1898 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1899 setModified(IsModified_MachineData);
1900 mHWData.backup();
1901 mHWData->mPageFusionEnabled = enabled;
1902 return S_OK;
1903#else
1904 NOREF(enabled);
1905 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1906#endif
1907}
1908
1909STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *enabled)
1910{
1911 CheckComArgOutPointerValid(enabled);
1912
1913 AutoCaller autoCaller(this);
1914 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1915
1916 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1917
1918 *enabled = mHWData->mAccelerate3DEnabled;
1919
1920 return S_OK;
1921}
1922
1923STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
1924{
1925 AutoCaller autoCaller(this);
1926 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1927
1928 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1929
1930 HRESULT rc = checkStateDependency(MutableStateDep);
1931 if (FAILED(rc)) return rc;
1932
1933 /** @todo check validity! */
1934
1935 setModified(IsModified_MachineData);
1936 mHWData.backup();
1937 mHWData->mAccelerate3DEnabled = enable;
1938
1939 return S_OK;
1940}
1941
1942
1943STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *enabled)
1944{
1945 CheckComArgOutPointerValid(enabled);
1946
1947 AutoCaller autoCaller(this);
1948 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1949
1950 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1951
1952 *enabled = mHWData->mAccelerate2DVideoEnabled;
1953
1954 return S_OK;
1955}
1956
1957STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
1958{
1959 AutoCaller autoCaller(this);
1960 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1961
1962 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1963
1964 HRESULT rc = checkStateDependency(MutableStateDep);
1965 if (FAILED(rc)) return rc;
1966
1967 /** @todo check validity! */
1968
1969 setModified(IsModified_MachineData);
1970 mHWData.backup();
1971 mHWData->mAccelerate2DVideoEnabled = enable;
1972
1973 return S_OK;
1974}
1975
1976STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
1977{
1978 CheckComArgOutPointerValid(monitorCount);
1979
1980 AutoCaller autoCaller(this);
1981 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1982
1983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1984
1985 *monitorCount = mHWData->mMonitorCount;
1986
1987 return S_OK;
1988}
1989
1990STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
1991{
1992 /* make sure monitor count is a sensible number */
1993 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
1994 return setError(E_INVALIDARG,
1995 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1996 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
1997
1998 AutoCaller autoCaller(this);
1999 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2000
2001 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2002
2003 HRESULT rc = checkStateDependency(MutableStateDep);
2004 if (FAILED(rc)) return rc;
2005
2006 setModified(IsModified_MachineData);
2007 mHWData.backup();
2008 mHWData->mMonitorCount = monitorCount;
2009
2010 return S_OK;
2011}
2012
2013STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2014{
2015 CheckComArgOutPointerValid(biosSettings);
2016
2017 AutoCaller autoCaller(this);
2018 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2019
2020 /* mBIOSSettings is constant during life time, no need to lock */
2021 mBIOSSettings.queryInterfaceTo(biosSettings);
2022
2023 return S_OK;
2024}
2025
2026STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2027{
2028 CheckComArgOutPointerValid(aVal);
2029
2030 AutoCaller autoCaller(this);
2031 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2032
2033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2034
2035 switch (property)
2036 {
2037 case CPUPropertyType_PAE:
2038 *aVal = mHWData->mPAEEnabled;
2039 break;
2040
2041 case CPUPropertyType_Synthetic:
2042 *aVal = mHWData->mSyntheticCpu;
2043 break;
2044
2045 case CPUPropertyType_LongMode:
2046 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2047 *aVal = TRUE;
2048 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2049 *aVal = FALSE;
2050#if HC_ARCH_BITS == 64
2051 else
2052 *aVal = TRUE;
2053#else
2054 else
2055 {
2056 *aVal = FALSE;
2057
2058 ComPtr<IGuestOSType> ptrGuestOSType;
2059 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2060 if (SUCCEEDED(hrc2))
2061 {
2062 BOOL fIs64Bit = FALSE;
2063 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2064 if (SUCCEEDED(hrc2) && fIs64Bit)
2065 {
2066 ComObjPtr<Host> ptrHost = mParent->host();
2067 alock.release();
2068
2069 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal); AssertComRC(hrc2);
2070 if (FAILED(hrc2))
2071 *aVal = FALSE;
2072 }
2073 }
2074 }
2075#endif
2076 break;
2077
2078 default:
2079 return E_INVALIDARG;
2080 }
2081 return S_OK;
2082}
2083
2084STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2085{
2086 AutoCaller autoCaller(this);
2087 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2088
2089 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2090
2091 HRESULT rc = checkStateDependency(MutableStateDep);
2092 if (FAILED(rc)) return rc;
2093
2094 switch (property)
2095 {
2096 case CPUPropertyType_PAE:
2097 setModified(IsModified_MachineData);
2098 mHWData.backup();
2099 mHWData->mPAEEnabled = !!aVal;
2100 break;
2101
2102 case CPUPropertyType_Synthetic:
2103 setModified(IsModified_MachineData);
2104 mHWData.backup();
2105 mHWData->mSyntheticCpu = !!aVal;
2106 break;
2107
2108 case CPUPropertyType_LongMode:
2109 setModified(IsModified_MachineData);
2110 mHWData.backup();
2111 mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2112 break;
2113
2114 default:
2115 return E_INVALIDARG;
2116 }
2117 return S_OK;
2118}
2119
2120STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2121{
2122 CheckComArgOutPointerValid(aValEax);
2123 CheckComArgOutPointerValid(aValEbx);
2124 CheckComArgOutPointerValid(aValEcx);
2125 CheckComArgOutPointerValid(aValEdx);
2126
2127 AutoCaller autoCaller(this);
2128 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2129
2130 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2131
2132 switch(aId)
2133 {
2134 case 0x0:
2135 case 0x1:
2136 case 0x2:
2137 case 0x3:
2138 case 0x4:
2139 case 0x5:
2140 case 0x6:
2141 case 0x7:
2142 case 0x8:
2143 case 0x9:
2144 case 0xA:
2145 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2146 return E_INVALIDARG;
2147
2148 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2149 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2150 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2151 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2152 break;
2153
2154 case 0x80000000:
2155 case 0x80000001:
2156 case 0x80000002:
2157 case 0x80000003:
2158 case 0x80000004:
2159 case 0x80000005:
2160 case 0x80000006:
2161 case 0x80000007:
2162 case 0x80000008:
2163 case 0x80000009:
2164 case 0x8000000A:
2165 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2166 return E_INVALIDARG;
2167
2168 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2169 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2170 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2171 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2172 break;
2173
2174 default:
2175 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2176 }
2177 return S_OK;
2178}
2179
2180STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2181{
2182 AutoCaller autoCaller(this);
2183 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2184
2185 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2186
2187 HRESULT rc = checkStateDependency(MutableStateDep);
2188 if (FAILED(rc)) return rc;
2189
2190 switch(aId)
2191 {
2192 case 0x0:
2193 case 0x1:
2194 case 0x2:
2195 case 0x3:
2196 case 0x4:
2197 case 0x5:
2198 case 0x6:
2199 case 0x7:
2200 case 0x8:
2201 case 0x9:
2202 case 0xA:
2203 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2204 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2205 setModified(IsModified_MachineData);
2206 mHWData.backup();
2207 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2208 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2209 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2210 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2211 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2212 break;
2213
2214 case 0x80000000:
2215 case 0x80000001:
2216 case 0x80000002:
2217 case 0x80000003:
2218 case 0x80000004:
2219 case 0x80000005:
2220 case 0x80000006:
2221 case 0x80000007:
2222 case 0x80000008:
2223 case 0x80000009:
2224 case 0x8000000A:
2225 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2226 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2227 setModified(IsModified_MachineData);
2228 mHWData.backup();
2229 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2230 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2231 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2232 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2233 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2234 break;
2235
2236 default:
2237 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2238 }
2239 return S_OK;
2240}
2241
2242STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2243{
2244 AutoCaller autoCaller(this);
2245 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2246
2247 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2248
2249 HRESULT rc = checkStateDependency(MutableStateDep);
2250 if (FAILED(rc)) return rc;
2251
2252 switch(aId)
2253 {
2254 case 0x0:
2255 case 0x1:
2256 case 0x2:
2257 case 0x3:
2258 case 0x4:
2259 case 0x5:
2260 case 0x6:
2261 case 0x7:
2262 case 0x8:
2263 case 0x9:
2264 case 0xA:
2265 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2266 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2267 setModified(IsModified_MachineData);
2268 mHWData.backup();
2269 /* Invalidate leaf. */
2270 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2271 break;
2272
2273 case 0x80000000:
2274 case 0x80000001:
2275 case 0x80000002:
2276 case 0x80000003:
2277 case 0x80000004:
2278 case 0x80000005:
2279 case 0x80000006:
2280 case 0x80000007:
2281 case 0x80000008:
2282 case 0x80000009:
2283 case 0x8000000A:
2284 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2285 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2286 setModified(IsModified_MachineData);
2287 mHWData.backup();
2288 /* Invalidate leaf. */
2289 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2290 break;
2291
2292 default:
2293 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2294 }
2295 return S_OK;
2296}
2297
2298STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2299{
2300 AutoCaller autoCaller(this);
2301 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2302
2303 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2304
2305 HRESULT rc = checkStateDependency(MutableStateDep);
2306 if (FAILED(rc)) return rc;
2307
2308 setModified(IsModified_MachineData);
2309 mHWData.backup();
2310
2311 /* Invalidate all standard leafs. */
2312 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2313 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2314
2315 /* Invalidate all extended leafs. */
2316 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2317 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2318
2319 return S_OK;
2320}
2321
2322STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2323{
2324 CheckComArgOutPointerValid(aVal);
2325
2326 AutoCaller autoCaller(this);
2327 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2328
2329 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2330
2331 switch(property)
2332 {
2333 case HWVirtExPropertyType_Enabled:
2334 *aVal = mHWData->mHWVirtExEnabled;
2335 break;
2336
2337 case HWVirtExPropertyType_Exclusive:
2338 *aVal = mHWData->mHWVirtExExclusive;
2339 break;
2340
2341 case HWVirtExPropertyType_VPID:
2342 *aVal = mHWData->mHWVirtExVPIDEnabled;
2343 break;
2344
2345 case HWVirtExPropertyType_NestedPaging:
2346 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2347 break;
2348
2349 case HWVirtExPropertyType_LargePages:
2350 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2351#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2352 *aVal = FALSE;
2353#endif
2354 break;
2355
2356 case HWVirtExPropertyType_Force:
2357 *aVal = mHWData->mHWVirtExForceEnabled;
2358 break;
2359
2360 default:
2361 return E_INVALIDARG;
2362 }
2363 return S_OK;
2364}
2365
2366STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2367{
2368 AutoCaller autoCaller(this);
2369 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2370
2371 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2372
2373 HRESULT rc = checkStateDependency(MutableStateDep);
2374 if (FAILED(rc)) return rc;
2375
2376 switch(property)
2377 {
2378 case HWVirtExPropertyType_Enabled:
2379 setModified(IsModified_MachineData);
2380 mHWData.backup();
2381 mHWData->mHWVirtExEnabled = !!aVal;
2382 break;
2383
2384 case HWVirtExPropertyType_Exclusive:
2385 setModified(IsModified_MachineData);
2386 mHWData.backup();
2387 mHWData->mHWVirtExExclusive = !!aVal;
2388 break;
2389
2390 case HWVirtExPropertyType_VPID:
2391 setModified(IsModified_MachineData);
2392 mHWData.backup();
2393 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2394 break;
2395
2396 case HWVirtExPropertyType_NestedPaging:
2397 setModified(IsModified_MachineData);
2398 mHWData.backup();
2399 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2400 break;
2401
2402 case HWVirtExPropertyType_LargePages:
2403 setModified(IsModified_MachineData);
2404 mHWData.backup();
2405 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2406 break;
2407
2408 case HWVirtExPropertyType_Force:
2409 setModified(IsModified_MachineData);
2410 mHWData.backup();
2411 mHWData->mHWVirtExForceEnabled = !!aVal;
2412 break;
2413
2414 default:
2415 return E_INVALIDARG;
2416 }
2417
2418 return S_OK;
2419}
2420
2421STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2422{
2423 CheckComArgOutPointerValid(aSnapshotFolder);
2424
2425 AutoCaller autoCaller(this);
2426 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2427
2428 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2429
2430 Utf8Str strFullSnapshotFolder;
2431 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2432 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2433
2434 return S_OK;
2435}
2436
2437STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2438{
2439 /* @todo (r=dmik):
2440 * 1. Allow to change the name of the snapshot folder containing snapshots
2441 * 2. Rename the folder on disk instead of just changing the property
2442 * value (to be smart and not to leave garbage). Note that it cannot be
2443 * done here because the change may be rolled back. Thus, the right
2444 * place is #saveSettings().
2445 */
2446
2447 AutoCaller autoCaller(this);
2448 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2449
2450 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2451
2452 HRESULT rc = checkStateDependency(MutableStateDep);
2453 if (FAILED(rc)) return rc;
2454
2455 if (!mData->mCurrentSnapshot.isNull())
2456 return setError(E_FAIL,
2457 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2458
2459 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2460
2461 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2462 if (strSnapshotFolder.isEmpty())
2463 strSnapshotFolder = "Snapshots";
2464 int vrc = calculateFullPath(strSnapshotFolder,
2465 strSnapshotFolder);
2466 if (RT_FAILURE(vrc))
2467 return setError(E_FAIL,
2468 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2469 aSnapshotFolder, vrc);
2470
2471 setModified(IsModified_MachineData);
2472 mUserData.backup();
2473
2474 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2475
2476 return S_OK;
2477}
2478
2479STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2480{
2481 CheckComArgOutSafeArrayPointerValid(aAttachments);
2482
2483 AutoCaller autoCaller(this);
2484 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2485
2486 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2487
2488 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2489 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2490
2491 return S_OK;
2492}
2493
2494STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2495{
2496 CheckComArgOutPointerValid(vrdeServer);
2497
2498 AutoCaller autoCaller(this);
2499 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2500
2501 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2502
2503 Assert(!!mVRDEServer);
2504 mVRDEServer.queryInterfaceTo(vrdeServer);
2505
2506 return S_OK;
2507}
2508
2509STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2510{
2511 CheckComArgOutPointerValid(audioAdapter);
2512
2513 AutoCaller autoCaller(this);
2514 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2515
2516 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2517
2518 mAudioAdapter.queryInterfaceTo(audioAdapter);
2519 return S_OK;
2520}
2521
2522STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2523{
2524#ifdef VBOX_WITH_VUSB
2525 CheckComArgOutPointerValid(aUSBController);
2526
2527 AutoCaller autoCaller(this);
2528 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2529
2530 clearError();
2531 MultiResult rc(S_OK);
2532
2533# ifdef VBOX_WITH_USB
2534 rc = mParent->host()->checkUSBProxyService();
2535 if (FAILED(rc)) return rc;
2536# endif
2537
2538 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2539
2540 return rc = mUSBController.queryInterfaceTo(aUSBController);
2541#else
2542 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2543 * extended error info to indicate that USB is simply not available
2544 * (w/o treating it as a failure), for example, as in OSE */
2545 NOREF(aUSBController);
2546 ReturnComNotImplemented();
2547#endif /* VBOX_WITH_VUSB */
2548}
2549
2550STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2551{
2552 CheckComArgOutPointerValid(aFilePath);
2553
2554 AutoLimitedCaller autoCaller(this);
2555 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2556
2557 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2558
2559 mData->m_strConfigFileFull.cloneTo(aFilePath);
2560 return S_OK;
2561}
2562
2563STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2564{
2565 CheckComArgOutPointerValid(aModified);
2566
2567 AutoCaller autoCaller(this);
2568 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2569
2570 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2571
2572 HRESULT rc = checkStateDependency(MutableStateDep);
2573 if (FAILED(rc)) return rc;
2574
2575 if (!mData->pMachineConfigFile->fileExists())
2576 // this is a new machine, and no config file exists yet:
2577 *aModified = TRUE;
2578 else
2579 *aModified = (mData->flModifications != 0);
2580
2581 return S_OK;
2582}
2583
2584STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2585{
2586 CheckComArgOutPointerValid(aSessionState);
2587
2588 AutoCaller autoCaller(this);
2589 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2590
2591 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2592
2593 *aSessionState = mData->mSession.mState;
2594
2595 return S_OK;
2596}
2597
2598STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2599{
2600 CheckComArgOutPointerValid(aSessionType);
2601
2602 AutoCaller autoCaller(this);
2603 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2604
2605 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2606
2607 mData->mSession.mType.cloneTo(aSessionType);
2608
2609 return S_OK;
2610}
2611
2612STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2613{
2614 CheckComArgOutPointerValid(aSessionPID);
2615
2616 AutoCaller autoCaller(this);
2617 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2618
2619 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2620
2621 *aSessionPID = mData->mSession.mPID;
2622
2623 return S_OK;
2624}
2625
2626STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2627{
2628 CheckComArgOutPointerValid(machineState);
2629
2630 AutoCaller autoCaller(this);
2631 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2632
2633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2634
2635 *machineState = mData->mMachineState;
2636
2637 return S_OK;
2638}
2639
2640STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2641{
2642 CheckComArgOutPointerValid(aLastStateChange);
2643
2644 AutoCaller autoCaller(this);
2645 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2646
2647 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2648
2649 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2650
2651 return S_OK;
2652}
2653
2654STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2655{
2656 CheckComArgOutPointerValid(aStateFilePath);
2657
2658 AutoCaller autoCaller(this);
2659 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2660
2661 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2662
2663 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2664
2665 return S_OK;
2666}
2667
2668STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2669{
2670 CheckComArgOutPointerValid(aLogFolder);
2671
2672 AutoCaller autoCaller(this);
2673 AssertComRCReturnRC(autoCaller.rc());
2674
2675 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2676
2677 Utf8Str logFolder;
2678 getLogFolder(logFolder);
2679 logFolder.cloneTo(aLogFolder);
2680
2681 return S_OK;
2682}
2683
2684STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2685{
2686 CheckComArgOutPointerValid(aCurrentSnapshot);
2687
2688 AutoCaller autoCaller(this);
2689 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2690
2691 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2692
2693 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2694
2695 return S_OK;
2696}
2697
2698STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2699{
2700 CheckComArgOutPointerValid(aSnapshotCount);
2701
2702 AutoCaller autoCaller(this);
2703 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2704
2705 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2706
2707 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2708 ? 0
2709 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2710
2711 return S_OK;
2712}
2713
2714STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2715{
2716 CheckComArgOutPointerValid(aCurrentStateModified);
2717
2718 AutoCaller autoCaller(this);
2719 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2720
2721 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2722
2723 /* Note: for machines with no snapshots, we always return FALSE
2724 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2725 * reasons :) */
2726
2727 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2728 ? FALSE
2729 : mData->mCurrentStateModified;
2730
2731 return S_OK;
2732}
2733
2734STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2735{
2736 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2737
2738 AutoCaller autoCaller(this);
2739 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2740
2741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2742
2743 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2744 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2745
2746 return S_OK;
2747}
2748
2749STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2750{
2751 CheckComArgOutPointerValid(aClipboardMode);
2752
2753 AutoCaller autoCaller(this);
2754 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2755
2756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2757
2758 *aClipboardMode = mHWData->mClipboardMode;
2759
2760 return S_OK;
2761}
2762
2763STDMETHODIMP
2764Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2765{
2766 HRESULT rc = S_OK;
2767
2768 AutoCaller autoCaller(this);
2769 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2770
2771 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2772
2773 alock.release();
2774 rc = onClipboardModeChange(aClipboardMode);
2775 alock.acquire();
2776 if (FAILED(rc)) return rc;
2777
2778 setModified(IsModified_MachineData);
2779 mHWData.backup();
2780 mHWData->mClipboardMode = aClipboardMode;
2781
2782 /* Save settings if online - todo why is this required?? */
2783 if (Global::IsOnline(mData->mMachineState))
2784 saveSettings(NULL);
2785
2786 return S_OK;
2787}
2788
2789STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
2790{
2791 CheckComArgOutPointerValid(aDragAndDropMode);
2792
2793 AutoCaller autoCaller(this);
2794 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2795
2796 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2797
2798 *aDragAndDropMode = mHWData->mDragAndDropMode;
2799
2800 return S_OK;
2801}
2802
2803STDMETHODIMP
2804Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
2805{
2806 HRESULT rc = S_OK;
2807
2808 AutoCaller autoCaller(this);
2809 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2810
2811 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2812
2813 alock.release();
2814 rc = onDragAndDropModeChange(aDragAndDropMode);
2815 alock.acquire();
2816 if (FAILED(rc)) return rc;
2817
2818 setModified(IsModified_MachineData);
2819 mHWData.backup();
2820 mHWData->mDragAndDropMode = aDragAndDropMode;
2821
2822 /* Save settings if online - todo why is this required?? */
2823 if (Global::IsOnline(mData->mMachineState))
2824 saveSettings(NULL);
2825
2826 return S_OK;
2827}
2828
2829STDMETHODIMP
2830Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
2831{
2832 CheckComArgOutPointerValid(aPatterns);
2833
2834 AutoCaller autoCaller(this);
2835 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2836
2837 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2838
2839 try
2840 {
2841 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
2842 }
2843 catch (...)
2844 {
2845 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2846 }
2847
2848 return S_OK;
2849}
2850
2851STDMETHODIMP
2852Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
2853{
2854 AutoCaller autoCaller(this);
2855 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2856
2857 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2858
2859 HRESULT rc = checkStateDependency(MutableStateDep);
2860 if (FAILED(rc)) return rc;
2861
2862 setModified(IsModified_MachineData);
2863 mHWData.backup();
2864 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
2865 return rc;
2866}
2867
2868STDMETHODIMP
2869Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
2870{
2871 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
2872
2873 AutoCaller autoCaller(this);
2874 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2875
2876 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2877
2878 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
2879 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
2880
2881 return S_OK;
2882}
2883
2884STDMETHODIMP
2885Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
2886{
2887 CheckComArgOutPointerValid(aEnabled);
2888
2889 AutoCaller autoCaller(this);
2890 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2891
2892 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2893
2894 *aEnabled = mUserData->s.fTeleporterEnabled;
2895
2896 return S_OK;
2897}
2898
2899STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
2900{
2901 AutoCaller autoCaller(this);
2902 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2903
2904 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2905
2906 /* Only allow it to be set to true when PoweredOff or Aborted.
2907 (Clearing it is always permitted.) */
2908 if ( aEnabled
2909 && mData->mRegistered
2910 && ( !isSessionMachine()
2911 || ( mData->mMachineState != MachineState_PoweredOff
2912 && mData->mMachineState != MachineState_Teleported
2913 && mData->mMachineState != MachineState_Aborted
2914 )
2915 )
2916 )
2917 return setError(VBOX_E_INVALID_VM_STATE,
2918 tr("The machine is not powered off (state is %s)"),
2919 Global::stringifyMachineState(mData->mMachineState));
2920
2921 setModified(IsModified_MachineData);
2922 mUserData.backup();
2923 mUserData->s.fTeleporterEnabled = !!aEnabled;
2924
2925 return S_OK;
2926}
2927
2928STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
2929{
2930 CheckComArgOutPointerValid(aPort);
2931
2932 AutoCaller autoCaller(this);
2933 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2934
2935 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2936
2937 *aPort = (ULONG)mUserData->s.uTeleporterPort;
2938
2939 return S_OK;
2940}
2941
2942STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
2943{
2944 if (aPort >= _64K)
2945 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
2946
2947 AutoCaller autoCaller(this);
2948 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2949
2950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2951
2952 HRESULT rc = checkStateDependency(MutableStateDep);
2953 if (FAILED(rc)) return rc;
2954
2955 setModified(IsModified_MachineData);
2956 mUserData.backup();
2957 mUserData->s.uTeleporterPort = (uint32_t)aPort;
2958
2959 return S_OK;
2960}
2961
2962STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
2963{
2964 CheckComArgOutPointerValid(aAddress);
2965
2966 AutoCaller autoCaller(this);
2967 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2968
2969 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2970
2971 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
2972
2973 return S_OK;
2974}
2975
2976STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
2977{
2978 AutoCaller autoCaller(this);
2979 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2980
2981 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2982
2983 HRESULT rc = checkStateDependency(MutableStateDep);
2984 if (FAILED(rc)) return rc;
2985
2986 setModified(IsModified_MachineData);
2987 mUserData.backup();
2988 mUserData->s.strTeleporterAddress = aAddress;
2989
2990 return S_OK;
2991}
2992
2993STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
2994{
2995 CheckComArgOutPointerValid(aPassword);
2996
2997 AutoCaller autoCaller(this);
2998 HRESULT hrc = autoCaller.rc();
2999 if (SUCCEEDED(hrc))
3000 {
3001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3002 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
3003 }
3004
3005 return hrc;
3006}
3007
3008STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
3009{
3010 /*
3011 * Hash the password first.
3012 */
3013 Utf8Str strPassword(aPassword);
3014 if (!strPassword.isEmpty())
3015 {
3016 if (VBoxIsPasswordHashed(&strPassword))
3017 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3018 VBoxHashPassword(&strPassword);
3019 }
3020
3021 /*
3022 * Do the update.
3023 */
3024 AutoCaller autoCaller(this);
3025 HRESULT hrc = autoCaller.rc();
3026 if (SUCCEEDED(hrc))
3027 {
3028 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3029 hrc = checkStateDependency(MutableStateDep);
3030 if (SUCCEEDED(hrc))
3031 {
3032 setModified(IsModified_MachineData);
3033 mUserData.backup();
3034 mUserData->s.strTeleporterPassword = strPassword;
3035 }
3036 }
3037
3038 return hrc;
3039}
3040
3041STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
3042{
3043 CheckComArgOutPointerValid(aState);
3044
3045 AutoCaller autoCaller(this);
3046 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3047
3048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3049
3050 *aState = mUserData->s.enmFaultToleranceState;
3051 return S_OK;
3052}
3053
3054STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
3055{
3056 AutoCaller autoCaller(this);
3057 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3058
3059 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3060
3061 /* @todo deal with running state change. */
3062 HRESULT rc = checkStateDependency(MutableStateDep);
3063 if (FAILED(rc)) return rc;
3064
3065 setModified(IsModified_MachineData);
3066 mUserData.backup();
3067 mUserData->s.enmFaultToleranceState = aState;
3068 return S_OK;
3069}
3070
3071STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3072{
3073 CheckComArgOutPointerValid(aAddress);
3074
3075 AutoCaller autoCaller(this);
3076 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3077
3078 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3079
3080 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3081 return S_OK;
3082}
3083
3084STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3085{
3086 AutoCaller autoCaller(this);
3087 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3088
3089 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3090
3091 /* @todo deal with running state change. */
3092 HRESULT rc = checkStateDependency(MutableStateDep);
3093 if (FAILED(rc)) return rc;
3094
3095 setModified(IsModified_MachineData);
3096 mUserData.backup();
3097 mUserData->s.strFaultToleranceAddress = aAddress;
3098 return S_OK;
3099}
3100
3101STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3102{
3103 CheckComArgOutPointerValid(aPort);
3104
3105 AutoCaller autoCaller(this);
3106 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3107
3108 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3109
3110 *aPort = mUserData->s.uFaultTolerancePort;
3111 return S_OK;
3112}
3113
3114STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3115{
3116 AutoCaller autoCaller(this);
3117 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3118
3119 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3120
3121 /* @todo deal with running state change. */
3122 HRESULT rc = checkStateDependency(MutableStateDep);
3123 if (FAILED(rc)) return rc;
3124
3125 setModified(IsModified_MachineData);
3126 mUserData.backup();
3127 mUserData->s.uFaultTolerancePort = aPort;
3128 return S_OK;
3129}
3130
3131STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3132{
3133 CheckComArgOutPointerValid(aPassword);
3134
3135 AutoCaller autoCaller(this);
3136 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3137
3138 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3139
3140 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3141
3142 return S_OK;
3143}
3144
3145STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3146{
3147 AutoCaller autoCaller(this);
3148 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3149
3150 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3151
3152 /* @todo deal with running state change. */
3153 HRESULT rc = checkStateDependency(MutableStateDep);
3154 if (FAILED(rc)) return rc;
3155
3156 setModified(IsModified_MachineData);
3157 mUserData.backup();
3158 mUserData->s.strFaultTolerancePassword = aPassword;
3159
3160 return S_OK;
3161}
3162
3163STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3164{
3165 CheckComArgOutPointerValid(aInterval);
3166
3167 AutoCaller autoCaller(this);
3168 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3169
3170 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3171
3172 *aInterval = mUserData->s.uFaultToleranceInterval;
3173 return S_OK;
3174}
3175
3176STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3177{
3178 AutoCaller autoCaller(this);
3179 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3180
3181 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3182
3183 /* @todo deal with running state change. */
3184 HRESULT rc = checkStateDependency(MutableStateDep);
3185 if (FAILED(rc)) return rc;
3186
3187 setModified(IsModified_MachineData);
3188 mUserData.backup();
3189 mUserData->s.uFaultToleranceInterval = aInterval;
3190 return S_OK;
3191}
3192
3193STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3194{
3195 CheckComArgOutPointerValid(aEnabled);
3196
3197 AutoCaller autoCaller(this);
3198 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3199
3200 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3201
3202 *aEnabled = mUserData->s.fRTCUseUTC;
3203
3204 return S_OK;
3205}
3206
3207STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3208{
3209 AutoCaller autoCaller(this);
3210 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3211
3212 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3213
3214 /* Only allow it to be set to true when PoweredOff or Aborted.
3215 (Clearing it is always permitted.) */
3216 if ( aEnabled
3217 && mData->mRegistered
3218 && ( !isSessionMachine()
3219 || ( mData->mMachineState != MachineState_PoweredOff
3220 && mData->mMachineState != MachineState_Teleported
3221 && mData->mMachineState != MachineState_Aborted
3222 )
3223 )
3224 )
3225 return setError(VBOX_E_INVALID_VM_STATE,
3226 tr("The machine is not powered off (state is %s)"),
3227 Global::stringifyMachineState(mData->mMachineState));
3228
3229 setModified(IsModified_MachineData);
3230 mUserData.backup();
3231 mUserData->s.fRTCUseUTC = !!aEnabled;
3232
3233 return S_OK;
3234}
3235
3236STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3237{
3238 CheckComArgOutPointerValid(aEnabled);
3239
3240 AutoCaller autoCaller(this);
3241 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3242
3243 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3244
3245 *aEnabled = mHWData->mIOCacheEnabled;
3246
3247 return S_OK;
3248}
3249
3250STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3251{
3252 AutoCaller autoCaller(this);
3253 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3254
3255 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3256
3257 HRESULT rc = checkStateDependency(MutableStateDep);
3258 if (FAILED(rc)) return rc;
3259
3260 setModified(IsModified_MachineData);
3261 mHWData.backup();
3262 mHWData->mIOCacheEnabled = aEnabled;
3263
3264 return S_OK;
3265}
3266
3267STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3268{
3269 CheckComArgOutPointerValid(aIOCacheSize);
3270
3271 AutoCaller autoCaller(this);
3272 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3273
3274 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3275
3276 *aIOCacheSize = mHWData->mIOCacheSize;
3277
3278 return S_OK;
3279}
3280
3281STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3282{
3283 AutoCaller autoCaller(this);
3284 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3285
3286 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3287
3288 HRESULT rc = checkStateDependency(MutableStateDep);
3289 if (FAILED(rc)) return rc;
3290
3291 setModified(IsModified_MachineData);
3292 mHWData.backup();
3293 mHWData->mIOCacheSize = aIOCacheSize;
3294
3295 return S_OK;
3296}
3297
3298
3299/**
3300 * @note Locks objects!
3301 */
3302STDMETHODIMP Machine::LockMachine(ISession *aSession,
3303 LockType_T lockType)
3304{
3305 CheckComArgNotNull(aSession);
3306
3307 AutoCaller autoCaller(this);
3308 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3309
3310 /* check the session state */
3311 SessionState_T state;
3312 HRESULT rc = aSession->COMGETTER(State)(&state);
3313 if (FAILED(rc)) return rc;
3314
3315 if (state != SessionState_Unlocked)
3316 return setError(VBOX_E_INVALID_OBJECT_STATE,
3317 tr("The given session is busy"));
3318
3319 // get the client's IInternalSessionControl interface
3320 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3321 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3322 E_INVALIDARG);
3323
3324 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3325
3326 if (!mData->mRegistered)
3327 return setError(E_UNEXPECTED,
3328 tr("The machine '%s' is not registered"),
3329 mUserData->s.strName.c_str());
3330
3331 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3332
3333 SessionState_T oldState = mData->mSession.mState;
3334 /* Hack: in case the session is closing and there is a progress object
3335 * which allows waiting for the session to be closed, take the opportunity
3336 * and do a limited wait (max. 1 second). This helps a lot when the system
3337 * is busy and thus session closing can take a little while. */
3338 if ( mData->mSession.mState == SessionState_Unlocking
3339 && mData->mSession.mProgress)
3340 {
3341 alock.release();
3342 mData->mSession.mProgress->WaitForCompletion(1000);
3343 alock.acquire();
3344 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3345 }
3346
3347 // try again now
3348 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3349 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3350 )
3351 {
3352 // OK, share the session... we are now dealing with three processes:
3353 // 1) VBoxSVC (where this code runs);
3354 // 2) process C: the caller's client process (who wants a shared session);
3355 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3356
3357 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3358 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3359 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3360 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3361 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3362
3363 /*
3364 * Release the lock before calling the client process. It's safe here
3365 * since the only thing to do after we get the lock again is to add
3366 * the remote control to the list (which doesn't directly influence
3367 * anything).
3368 */
3369 alock.release();
3370
3371 // get the console of the session holding the write lock (this is a remote call)
3372 ComPtr<IConsole> pConsoleW;
3373 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3374 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3375 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3376 if (FAILED(rc))
3377 // the failure may occur w/o any error info (from RPC), so provide one
3378 return setError(VBOX_E_VM_ERROR,
3379 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3380
3381 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3382
3383 // share the session machine and W's console with the caller's session
3384 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3385 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3386 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3387
3388 if (FAILED(rc))
3389 // the failure may occur w/o any error info (from RPC), so provide one
3390 return setError(VBOX_E_VM_ERROR,
3391 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3392 alock.acquire();
3393
3394 // need to revalidate the state after acquiring the lock again
3395 if (mData->mSession.mState != SessionState_Locked)
3396 {
3397 pSessionControl->Uninitialize();
3398 return setError(VBOX_E_INVALID_SESSION_STATE,
3399 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3400 mUserData->s.strName.c_str());
3401 }
3402
3403 // add the caller's session to the list
3404 mData->mSession.mRemoteControls.push_back(pSessionControl);
3405 }
3406 else if ( mData->mSession.mState == SessionState_Locked
3407 || mData->mSession.mState == SessionState_Unlocking
3408 )
3409 {
3410 // sharing not permitted, or machine still unlocking:
3411 return setError(VBOX_E_INVALID_OBJECT_STATE,
3412 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3413 mUserData->s.strName.c_str());
3414 }
3415 else
3416 {
3417 // machine is not locked: then write-lock the machine (create the session machine)
3418
3419 // must not be busy
3420 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3421
3422 // get the caller's session PID
3423 RTPROCESS pid = NIL_RTPROCESS;
3424 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3425 pSessionControl->GetPID((ULONG*)&pid);
3426 Assert(pid != NIL_RTPROCESS);
3427
3428 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3429
3430 if (fLaunchingVMProcess)
3431 {
3432 // this machine is awaiting for a spawning session to be opened:
3433 // then the calling process must be the one that got started by
3434 // LaunchVMProcess()
3435
3436 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3437 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3438
3439 if (mData->mSession.mPID != pid)
3440 return setError(E_ACCESSDENIED,
3441 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3442 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3443 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3444 }
3445
3446 // create the mutable SessionMachine from the current machine
3447 ComObjPtr<SessionMachine> sessionMachine;
3448 sessionMachine.createObject();
3449 rc = sessionMachine->init(this);
3450 AssertComRC(rc);
3451
3452 /* NOTE: doing return from this function after this point but
3453 * before the end is forbidden since it may call SessionMachine::uninit()
3454 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3455 * lock while still holding the Machine lock in alock so that a deadlock
3456 * is possible due to the wrong lock order. */
3457
3458 if (SUCCEEDED(rc))
3459 {
3460 /*
3461 * Set the session state to Spawning to protect against subsequent
3462 * attempts to open a session and to unregister the machine after
3463 * we release the lock.
3464 */
3465 SessionState_T origState = mData->mSession.mState;
3466 mData->mSession.mState = SessionState_Spawning;
3467
3468 /*
3469 * Release the lock before calling the client process -- it will call
3470 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3471 * because the state is Spawning, so that LaunchVMProcess() and
3472 * LockMachine() calls will fail. This method, called before we
3473 * acquire the lock again, will fail because of the wrong PID.
3474 *
3475 * Note that mData->mSession.mRemoteControls accessed outside
3476 * the lock may not be modified when state is Spawning, so it's safe.
3477 */
3478 alock.release();
3479
3480 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3481 rc = pSessionControl->AssignMachine(sessionMachine, lockType);
3482 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3483
3484 /* The failure may occur w/o any error info (from RPC), so provide one */
3485 if (FAILED(rc))
3486 setError(VBOX_E_VM_ERROR,
3487 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3488
3489 if ( SUCCEEDED(rc)
3490 && fLaunchingVMProcess
3491 )
3492 {
3493 /* complete the remote session initialization */
3494
3495 /* get the console from the direct session */
3496 ComPtr<IConsole> console;
3497 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3498 ComAssertComRC(rc);
3499
3500 if (SUCCEEDED(rc) && !console)
3501 {
3502 ComAssert(!!console);
3503 rc = E_FAIL;
3504 }
3505
3506 /* assign machine & console to the remote session */
3507 if (SUCCEEDED(rc))
3508 {
3509 /*
3510 * after LaunchVMProcess(), the first and the only
3511 * entry in remoteControls is that remote session
3512 */
3513 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3514 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3515 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3516
3517 /* The failure may occur w/o any error info (from RPC), so provide one */
3518 if (FAILED(rc))
3519 setError(VBOX_E_VM_ERROR,
3520 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3521 }
3522
3523 if (FAILED(rc))
3524 pSessionControl->Uninitialize();
3525 }
3526
3527 /* acquire the lock again */
3528 alock.acquire();
3529
3530 /* Restore the session state */
3531 mData->mSession.mState = origState;
3532 }
3533
3534 // finalize spawning anyway (this is why we don't return on errors above)
3535 if (fLaunchingVMProcess)
3536 {
3537 /* Note that the progress object is finalized later */
3538 /** @todo Consider checking mData->mSession.mProgress for cancellation
3539 * around here. */
3540
3541 /* We don't reset mSession.mPID here because it is necessary for
3542 * SessionMachine::uninit() to reap the child process later. */
3543
3544 if (FAILED(rc))
3545 {
3546 /* Close the remote session, remove the remote control from the list
3547 * and reset session state to Closed (@note keep the code in sync
3548 * with the relevant part in openSession()). */
3549
3550 Assert(mData->mSession.mRemoteControls.size() == 1);
3551 if (mData->mSession.mRemoteControls.size() == 1)
3552 {
3553 ErrorInfoKeeper eik;
3554 mData->mSession.mRemoteControls.front()->Uninitialize();
3555 }
3556
3557 mData->mSession.mRemoteControls.clear();
3558 mData->mSession.mState = SessionState_Unlocked;
3559 }
3560 }
3561 else
3562 {
3563 /* memorize PID of the directly opened session */
3564 if (SUCCEEDED(rc))
3565 mData->mSession.mPID = pid;
3566 }
3567
3568 if (SUCCEEDED(rc))
3569 {
3570 /* memorize the direct session control and cache IUnknown for it */
3571 mData->mSession.mDirectControl = pSessionControl;
3572 mData->mSession.mState = SessionState_Locked;
3573 /* associate the SessionMachine with this Machine */
3574 mData->mSession.mMachine = sessionMachine;
3575
3576 /* request an IUnknown pointer early from the remote party for later
3577 * identity checks (it will be internally cached within mDirectControl
3578 * at least on XPCOM) */
3579 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3580 NOREF(unk);
3581 }
3582
3583 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3584 * would break the lock order */
3585 alock.release();
3586
3587 /* uninitialize the created session machine on failure */
3588 if (FAILED(rc))
3589 sessionMachine->uninit();
3590
3591 }
3592
3593 if (SUCCEEDED(rc))
3594 {
3595 /*
3596 * tell the client watcher thread to update the set of
3597 * machines that have open sessions
3598 */
3599 mParent->updateClientWatcher();
3600
3601 if (oldState != SessionState_Locked)
3602 /* fire an event */
3603 mParent->onSessionStateChange(getId(), SessionState_Locked);
3604 }
3605
3606 return rc;
3607}
3608
3609/**
3610 * @note Locks objects!
3611 */
3612STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3613 IN_BSTR aFrontend,
3614 IN_BSTR aEnvironment,
3615 IProgress **aProgress)
3616{
3617 CheckComArgStr(aFrontend);
3618 Utf8Str strFrontend(aFrontend);
3619 Utf8Str strEnvironment(aEnvironment);
3620 /* "emergencystop" doesn't need the session, so skip the checks/interface
3621 * retrieval. This code doesn't quite fit in here, but introducing a
3622 * special API method would be even more effort, and would require explicit
3623 * support by every API client. It's better to hide the feature a bit. */
3624 if (strFrontend != "emergencystop")
3625 CheckComArgNotNull(aSession);
3626 CheckComArgOutPointerValid(aProgress);
3627
3628 AutoCaller autoCaller(this);
3629 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3630
3631 HRESULT rc = S_OK;
3632 if (strFrontend.isEmpty())
3633 {
3634 Bstr bstrFrontend;
3635 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3636 if (FAILED(rc))
3637 return rc;
3638 strFrontend = bstrFrontend;
3639 if (strFrontend.isEmpty())
3640 {
3641 ComPtr<ISystemProperties> systemProperties;
3642 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3643 if (FAILED(rc))
3644 return rc;
3645 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3646 if (FAILED(rc))
3647 return rc;
3648 strFrontend = bstrFrontend;
3649 }
3650 /* paranoia - emergencystop is not a valid default */
3651 if (strFrontend == "emergencystop")
3652 strFrontend = Utf8Str::Empty;
3653 }
3654
3655 if (strFrontend != "emergencystop")
3656 {
3657 /* check the session state */
3658 SessionState_T state;
3659 rc = aSession->COMGETTER(State)(&state);
3660 if (FAILED(rc))
3661 return rc;
3662
3663 if (state != SessionState_Unlocked)
3664 return setError(VBOX_E_INVALID_OBJECT_STATE,
3665 tr("The given session is busy"));
3666
3667 /* get the IInternalSessionControl interface */
3668 ComPtr<IInternalSessionControl> control(aSession);
3669 ComAssertMsgRet(!control.isNull(),
3670 ("No IInternalSessionControl interface"),
3671 E_INVALIDARG);
3672
3673 /* get the teleporter enable state for the progress object init. */
3674 BOOL fTeleporterEnabled;
3675 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3676 if (FAILED(rc))
3677 return rc;
3678
3679 /* create a progress object */
3680 ComObjPtr<ProgressProxy> progress;
3681 progress.createObject();
3682 rc = progress->init(mParent,
3683 static_cast<IMachine*>(this),
3684 Bstr(tr("Starting VM")).raw(),
3685 TRUE /* aCancelable */,
3686 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3687 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3688 2 /* uFirstOperationWeight */,
3689 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3690
3691 if (SUCCEEDED(rc))
3692 {
3693 rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
3694 if (SUCCEEDED(rc))
3695 {
3696 progress.queryInterfaceTo(aProgress);
3697
3698 /* signal the client watcher thread */
3699 mParent->updateClientWatcher();
3700
3701 /* fire an event */
3702 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3703 }
3704 }
3705 }
3706 else
3707 {
3708 /* no progress object - either instant success or failure */
3709 *aProgress = NULL;
3710
3711 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3712
3713 if (mData->mSession.mState != SessionState_Locked)
3714 return setError(VBOX_E_INVALID_OBJECT_STATE,
3715 tr("The machine '%s' is not locked by a session"),
3716 mUserData->s.strName.c_str());
3717
3718 /* must have a VM process associated - do not kill normal API clients
3719 * with an open session */
3720 if (!Global::IsOnline(mData->mMachineState))
3721 return setError(VBOX_E_INVALID_OBJECT_STATE,
3722 tr("The machine '%s' does not have a VM process"),
3723 mUserData->s.strName.c_str());
3724
3725 /* forcibly terminate the VM process */
3726 if (mData->mSession.mPID != NIL_RTPROCESS)
3727 RTProcTerminate(mData->mSession.mPID);
3728
3729 /* signal the client watcher thread, as most likely the client has
3730 * been terminated */
3731 mParent->updateClientWatcher();
3732 }
3733
3734 return rc;
3735}
3736
3737STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3738{
3739 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3740 return setError(E_INVALIDARG,
3741 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3742 aPosition, SchemaDefs::MaxBootPosition);
3743
3744 if (aDevice == DeviceType_USB)
3745 return setError(E_NOTIMPL,
3746 tr("Booting from USB device is currently not supported"));
3747
3748 AutoCaller autoCaller(this);
3749 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3750
3751 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3752
3753 HRESULT rc = checkStateDependency(MutableStateDep);
3754 if (FAILED(rc)) return rc;
3755
3756 setModified(IsModified_MachineData);
3757 mHWData.backup();
3758 mHWData->mBootOrder[aPosition - 1] = aDevice;
3759
3760 return S_OK;
3761}
3762
3763STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3764{
3765 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3766 return setError(E_INVALIDARG,
3767 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3768 aPosition, SchemaDefs::MaxBootPosition);
3769
3770 AutoCaller autoCaller(this);
3771 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3772
3773 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3774
3775 *aDevice = mHWData->mBootOrder[aPosition - 1];
3776
3777 return S_OK;
3778}
3779
3780STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
3781 LONG aControllerPort,
3782 LONG aDevice,
3783 DeviceType_T aType,
3784 IMedium *aMedium)
3785{
3786 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3787 aControllerName, aControllerPort, aDevice, aType, aMedium));
3788
3789 CheckComArgStrNotEmptyOrNull(aControllerName);
3790
3791 AutoCaller autoCaller(this);
3792 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3793
3794 // request the host lock first, since might be calling Host methods for getting host drives;
3795 // next, protect the media tree all the while we're in here, as well as our member variables
3796 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
3797 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3798
3799 HRESULT rc = checkStateDependency(MutableStateDep);
3800 if (FAILED(rc)) return rc;
3801
3802 /// @todo NEWMEDIA implicit machine registration
3803 if (!mData->mRegistered)
3804 return setError(VBOX_E_INVALID_OBJECT_STATE,
3805 tr("Cannot attach storage devices to an unregistered machine"));
3806
3807 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3808
3809 /* Check for an existing controller. */
3810 ComObjPtr<StorageController> ctl;
3811 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
3812 if (FAILED(rc)) return rc;
3813
3814 StorageControllerType_T ctrlType;
3815 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3816 if (FAILED(rc))
3817 return setError(E_FAIL,
3818 tr("Could not get type of controller '%ls'"),
3819 aControllerName);
3820
3821 bool fSilent = false;
3822 Utf8Str strReconfig;
3823
3824 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3825 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3826 if (FAILED(rc))
3827 return rc;
3828 if ( mData->mMachineState == MachineState_Paused
3829 && strReconfig == "1")
3830 fSilent = true;
3831
3832 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3833 bool fHotplug = false;
3834 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3835 fHotplug = true;
3836
3837 if (fHotplug && !isControllerHotplugCapable(ctrlType))
3838 return setError(VBOX_E_INVALID_VM_STATE,
3839 tr("Controller '%ls' does not support hotplugging"),
3840 aControllerName);
3841
3842 // check that the port and device are not out of range
3843 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
3844 if (FAILED(rc)) return rc;
3845
3846 /* check if the device slot is already busy */
3847 MediumAttachment *pAttachTemp;
3848 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
3849 aControllerName,
3850 aControllerPort,
3851 aDevice)))
3852 {
3853 Medium *pMedium = pAttachTemp->getMedium();
3854 if (pMedium)
3855 {
3856 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3857 return setError(VBOX_E_OBJECT_IN_USE,
3858 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3859 pMedium->getLocationFull().c_str(),
3860 aControllerPort,
3861 aDevice,
3862 aControllerName);
3863 }
3864 else
3865 return setError(VBOX_E_OBJECT_IN_USE,
3866 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3867 aControllerPort, aDevice, aControllerName);
3868 }
3869
3870 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
3871 if (aMedium && medium.isNull())
3872 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3873
3874 AutoCaller mediumCaller(medium);
3875 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3876
3877 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3878
3879 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
3880 && !medium.isNull()
3881 )
3882 return setError(VBOX_E_OBJECT_IN_USE,
3883 tr("Medium '%s' is already attached to this virtual machine"),
3884 medium->getLocationFull().c_str());
3885
3886 if (!medium.isNull())
3887 {
3888 MediumType_T mtype = medium->getType();
3889 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3890 // For DVDs it's not written to the config file, so needs no global config
3891 // version bump. For floppies it's a new attribute "type", which is ignored
3892 // by older VirtualBox version, so needs no global config version bump either.
3893 // For hard disks this type is not accepted.
3894 if (mtype == MediumType_MultiAttach)
3895 {
3896 // This type is new with VirtualBox 4.0 and therefore requires settings
3897 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3898 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3899 // two reasons: The medium type is a property of the media registry tree, which
3900 // can reside in the global config file (for pre-4.0 media); we would therefore
3901 // possibly need to bump the global config version. We don't want to do that though
3902 // because that might make downgrading to pre-4.0 impossible.
3903 // As a result, we can only use these two new types if the medium is NOT in the
3904 // global registry:
3905 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
3906 if ( medium->isInRegistry(uuidGlobalRegistry)
3907 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3908 )
3909 return setError(VBOX_E_INVALID_OBJECT_STATE,
3910 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3911 "to machines that were created with VirtualBox 4.0 or later"),
3912 medium->getLocationFull().c_str());
3913 }
3914 }
3915
3916 bool fIndirect = false;
3917 if (!medium.isNull())
3918 fIndirect = medium->isReadOnly();
3919 bool associate = true;
3920
3921 do
3922 {
3923 if ( aType == DeviceType_HardDisk
3924 && mMediaData.isBackedUp())
3925 {
3926 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3927
3928 /* check if the medium was attached to the VM before we started
3929 * changing attachments in which case the attachment just needs to
3930 * be restored */
3931 if ((pAttachTemp = findAttachment(oldAtts, medium)))
3932 {
3933 AssertReturn(!fIndirect, E_FAIL);
3934
3935 /* see if it's the same bus/channel/device */
3936 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
3937 {
3938 /* the simplest case: restore the whole attachment
3939 * and return, nothing else to do */
3940 mMediaData->mAttachments.push_back(pAttachTemp);
3941 return S_OK;
3942 }
3943
3944 /* bus/channel/device differ; we need a new attachment object,
3945 * but don't try to associate it again */
3946 associate = false;
3947 break;
3948 }
3949 }
3950
3951 /* go further only if the attachment is to be indirect */
3952 if (!fIndirect)
3953 break;
3954
3955 /* perform the so called smart attachment logic for indirect
3956 * attachments. Note that smart attachment is only applicable to base
3957 * hard disks. */
3958
3959 if (medium->getParent().isNull())
3960 {
3961 /* first, investigate the backup copy of the current hard disk
3962 * attachments to make it possible to re-attach existing diffs to
3963 * another device slot w/o losing their contents */
3964 if (mMediaData.isBackedUp())
3965 {
3966 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3967
3968 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3969 uint32_t foundLevel = 0;
3970
3971 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
3972 it != oldAtts.end();
3973 ++it)
3974 {
3975 uint32_t level = 0;
3976 MediumAttachment *pAttach = *it;
3977 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3978 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3979 if (pMedium.isNull())
3980 continue;
3981
3982 if (pMedium->getBase(&level) == medium)
3983 {
3984 /* skip the hard disk if its currently attached (we
3985 * cannot attach the same hard disk twice) */
3986 if (findAttachment(mMediaData->mAttachments,
3987 pMedium))
3988 continue;
3989
3990 /* matched device, channel and bus (i.e. attached to the
3991 * same place) will win and immediately stop the search;
3992 * otherwise the attachment that has the youngest
3993 * descendant of medium will be used
3994 */
3995 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
3996 {
3997 /* the simplest case: restore the whole attachment
3998 * and return, nothing else to do */
3999 mMediaData->mAttachments.push_back(*it);
4000 return S_OK;
4001 }
4002 else if ( foundIt == oldAtts.end()
4003 || level > foundLevel /* prefer younger */
4004 )
4005 {
4006 foundIt = it;
4007 foundLevel = level;
4008 }
4009 }
4010 }
4011
4012 if (foundIt != oldAtts.end())
4013 {
4014 /* use the previously attached hard disk */
4015 medium = (*foundIt)->getMedium();
4016 mediumCaller.attach(medium);
4017 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4018 mediumLock.attach(medium);
4019 /* not implicit, doesn't require association with this VM */
4020 fIndirect = false;
4021 associate = false;
4022 /* go right to the MediumAttachment creation */
4023 break;
4024 }
4025 }
4026
4027 /* must give up the medium lock and medium tree lock as below we
4028 * go over snapshots, which needs a lock with higher lock order. */
4029 mediumLock.release();
4030 treeLock.release();
4031
4032 /* then, search through snapshots for the best diff in the given
4033 * hard disk's chain to base the new diff on */
4034
4035 ComObjPtr<Medium> base;
4036 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4037 while (snap)
4038 {
4039 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4040
4041 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
4042
4043 MediumAttachment *pAttachFound = NULL;
4044 uint32_t foundLevel = 0;
4045
4046 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
4047 it != snapAtts.end();
4048 ++it)
4049 {
4050 MediumAttachment *pAttach = *it;
4051 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4052 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4053 if (pMedium.isNull())
4054 continue;
4055
4056 uint32_t level = 0;
4057 if (pMedium->getBase(&level) == medium)
4058 {
4059 /* matched device, channel and bus (i.e. attached to the
4060 * same place) will win and immediately stop the search;
4061 * otherwise the attachment that has the youngest
4062 * descendant of medium will be used
4063 */
4064 if ( pAttach->getDevice() == aDevice
4065 && pAttach->getPort() == aControllerPort
4066 && pAttach->getControllerName() == aControllerName
4067 )
4068 {
4069 pAttachFound = pAttach;
4070 break;
4071 }
4072 else if ( !pAttachFound
4073 || level > foundLevel /* prefer younger */
4074 )
4075 {
4076 pAttachFound = pAttach;
4077 foundLevel = level;
4078 }
4079 }
4080 }
4081
4082 if (pAttachFound)
4083 {
4084 base = pAttachFound->getMedium();
4085 break;
4086 }
4087
4088 snap = snap->getParent();
4089 }
4090
4091 /* re-lock medium tree and the medium, as we need it below */
4092 treeLock.acquire();
4093 mediumLock.acquire();
4094
4095 /* found a suitable diff, use it as a base */
4096 if (!base.isNull())
4097 {
4098 medium = base;
4099 mediumCaller.attach(medium);
4100 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4101 mediumLock.attach(medium);
4102 }
4103 }
4104
4105 Utf8Str strFullSnapshotFolder;
4106 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4107
4108 ComObjPtr<Medium> diff;
4109 diff.createObject();
4110 // store this diff in the same registry as the parent
4111 Guid uuidRegistryParent;
4112 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4113 {
4114 // parent image has no registry: this can happen if we're attaching a new immutable
4115 // image that has not yet been attached (medium then points to the base and we're
4116 // creating the diff image for the immutable, and the parent is not yet registered);
4117 // put the parent in the machine registry then
4118 mediumLock.release();
4119 treeLock.release();
4120 alock.release();
4121 addMediumToRegistry(medium);
4122 alock.acquire();
4123 treeLock.acquire();
4124 mediumLock.acquire();
4125 medium->getFirstRegistryMachineId(uuidRegistryParent);
4126 }
4127 rc = diff->init(mParent,
4128 medium->getPreferredDiffFormat(),
4129 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4130 uuidRegistryParent);
4131 if (FAILED(rc)) return rc;
4132
4133 /* Apply the normal locking logic to the entire chain. */
4134 MediumLockList *pMediumLockList(new MediumLockList());
4135 mediumLock.release();
4136 treeLock.release();
4137 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4138 true /* fMediumLockWrite */,
4139 medium,
4140 *pMediumLockList);
4141 treeLock.acquire();
4142 mediumLock.acquire();
4143 if (SUCCEEDED(rc))
4144 {
4145 mediumLock.release();
4146 treeLock.release();
4147 rc = pMediumLockList->Lock();
4148 treeLock.acquire();
4149 mediumLock.acquire();
4150 if (FAILED(rc))
4151 setError(rc,
4152 tr("Could not lock medium when creating diff '%s'"),
4153 diff->getLocationFull().c_str());
4154 else
4155 {
4156 /* will release the lock before the potentially lengthy
4157 * operation, so protect with the special state */
4158 MachineState_T oldState = mData->mMachineState;
4159 setMachineState(MachineState_SettingUp);
4160
4161 mediumLock.release();
4162 treeLock.release();
4163 alock.release();
4164
4165 rc = medium->createDiffStorage(diff,
4166 MediumVariant_Standard,
4167 pMediumLockList,
4168 NULL /* aProgress */,
4169 true /* aWait */);
4170
4171 alock.acquire();
4172 treeLock.acquire();
4173 mediumLock.acquire();
4174
4175 setMachineState(oldState);
4176 }
4177 }
4178
4179 /* Unlock the media and free the associated memory. */
4180 delete pMediumLockList;
4181
4182 if (FAILED(rc)) return rc;
4183
4184 /* use the created diff for the actual attachment */
4185 medium = diff;
4186 mediumCaller.attach(medium);
4187 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4188 mediumLock.attach(medium);
4189 }
4190 while (0);
4191
4192 ComObjPtr<MediumAttachment> attachment;
4193 attachment.createObject();
4194 rc = attachment->init(this,
4195 medium,
4196 aControllerName,
4197 aControllerPort,
4198 aDevice,
4199 aType,
4200 fIndirect,
4201 false /* fPassthrough */,
4202 false /* fTempEject */,
4203 false /* fNonRotational */,
4204 false /* fDiscard */,
4205 Utf8Str::Empty);
4206 if (FAILED(rc)) return rc;
4207
4208 if (associate && !medium.isNull())
4209 {
4210 // as the last step, associate the medium to the VM
4211 rc = medium->addBackReference(mData->mUuid);
4212 // here we can fail because of Deleting, or being in process of creating a Diff
4213 if (FAILED(rc)) return rc;
4214
4215 mediumLock.release();
4216 treeLock.release();
4217 alock.release();
4218 addMediumToRegistry(medium);
4219 alock.acquire();
4220 treeLock.acquire();
4221 mediumLock.acquire();
4222 }
4223
4224 /* success: finally remember the attachment */
4225 setModified(IsModified_Storage);
4226 mMediaData.backup();
4227 mMediaData->mAttachments.push_back(attachment);
4228
4229 mediumLock.release();
4230 treeLock.release();
4231 alock.release();
4232
4233 if (fHotplug || fSilent)
4234 {
4235 MediumLockList *pMediumLockList(new MediumLockList());
4236
4237 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4238 true /* fMediumLockWrite */,
4239 NULL,
4240 *pMediumLockList);
4241 alock.acquire();
4242 if (FAILED(rc))
4243 delete pMediumLockList;
4244 else
4245 {
4246 mData->mSession.mLockedMedia.Unlock();
4247 alock.release();
4248 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4249 mData->mSession.mLockedMedia.Lock();
4250 alock.acquire();
4251 }
4252 alock.release();
4253
4254 if (SUCCEEDED(rc))
4255 {
4256 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4257 /* Remove lock list in case of error. */
4258 if (FAILED(rc))
4259 {
4260 mData->mSession.mLockedMedia.Unlock();
4261 mData->mSession.mLockedMedia.Remove(attachment);
4262 mData->mSession.mLockedMedia.Lock();
4263 }
4264 }
4265 }
4266
4267 mParent->saveModifiedRegistries();
4268
4269 return rc;
4270}
4271
4272STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4273 LONG aDevice)
4274{
4275 CheckComArgStrNotEmptyOrNull(aControllerName);
4276
4277 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4278 aControllerName, aControllerPort, aDevice));
4279
4280 AutoCaller autoCaller(this);
4281 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4282
4283 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4284
4285 HRESULT rc = checkStateDependency(MutableStateDep);
4286 if (FAILED(rc)) return rc;
4287
4288 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4289
4290 /* Check for an existing controller. */
4291 ComObjPtr<StorageController> ctl;
4292 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4293 if (FAILED(rc)) return rc;
4294
4295 StorageControllerType_T ctrlType;
4296 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4297 if (FAILED(rc))
4298 return setError(E_FAIL,
4299 tr("Could not get type of controller '%ls'"),
4300 aControllerName);
4301
4302 bool fSilent = false;
4303 Utf8Str strReconfig;
4304
4305 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4306 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4307 if (FAILED(rc))
4308 return rc;
4309 if ( mData->mMachineState == MachineState_Paused
4310 && strReconfig == "1")
4311 fSilent = true;
4312
4313 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4314 bool fHotplug = false;
4315 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4316 fHotplug = true;
4317
4318 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4319 return setError(VBOX_E_INVALID_VM_STATE,
4320 tr("Controller '%ls' does not support hotplugging"),
4321 aControllerName);
4322
4323 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4324 aControllerName,
4325 aControllerPort,
4326 aDevice);
4327 if (!pAttach)
4328 return setError(VBOX_E_OBJECT_NOT_FOUND,
4329 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4330 aDevice, aControllerPort, aControllerName);
4331
4332 /*
4333 * The VM has to detach the device before we delete any implicit diffs.
4334 * If this fails we can roll back without loosing data.
4335 */
4336 if (fHotplug || fSilent)
4337 {
4338 alock.release();
4339 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4340 alock.acquire();
4341 }
4342 if (FAILED(rc)) return rc;
4343
4344 /* If we are here everything went well and we can delete the implicit now. */
4345 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4346
4347 alock.release();
4348
4349 mParent->saveModifiedRegistries();
4350
4351 return rc;
4352}
4353
4354STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4355 LONG aDevice, BOOL aPassthrough)
4356{
4357 CheckComArgStrNotEmptyOrNull(aControllerName);
4358
4359 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4360 aControllerName, aControllerPort, aDevice, aPassthrough));
4361
4362 AutoCaller autoCaller(this);
4363 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4364
4365 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4366
4367 HRESULT rc = checkStateDependency(MutableStateDep);
4368 if (FAILED(rc)) return rc;
4369
4370 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4371
4372 if (Global::IsOnlineOrTransient(mData->mMachineState))
4373 return setError(VBOX_E_INVALID_VM_STATE,
4374 tr("Invalid machine state: %s"),
4375 Global::stringifyMachineState(mData->mMachineState));
4376
4377 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4378 aControllerName,
4379 aControllerPort,
4380 aDevice);
4381 if (!pAttach)
4382 return setError(VBOX_E_OBJECT_NOT_FOUND,
4383 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4384 aDevice, aControllerPort, aControllerName);
4385
4386
4387 setModified(IsModified_Storage);
4388 mMediaData.backup();
4389
4390 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4391
4392 if (pAttach->getType() != DeviceType_DVD)
4393 return setError(E_INVALIDARG,
4394 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4395 aDevice, aControllerPort, aControllerName);
4396 pAttach->updatePassthrough(!!aPassthrough);
4397
4398 return S_OK;
4399}
4400
4401STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4402 LONG aDevice, BOOL aTemporaryEject)
4403{
4404 CheckComArgStrNotEmptyOrNull(aControllerName);
4405
4406 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4407 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4408
4409 AutoCaller autoCaller(this);
4410 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4411
4412 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4413
4414 HRESULT rc = checkStateDependency(MutableStateDep);
4415 if (FAILED(rc)) return rc;
4416
4417 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4418 aControllerName,
4419 aControllerPort,
4420 aDevice);
4421 if (!pAttach)
4422 return setError(VBOX_E_OBJECT_NOT_FOUND,
4423 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4424 aDevice, aControllerPort, aControllerName);
4425
4426
4427 setModified(IsModified_Storage);
4428 mMediaData.backup();
4429
4430 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4431
4432 if (pAttach->getType() != DeviceType_DVD)
4433 return setError(E_INVALIDARG,
4434 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4435 aDevice, aControllerPort, aControllerName);
4436 pAttach->updateTempEject(!!aTemporaryEject);
4437
4438 return S_OK;
4439}
4440
4441STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4442 LONG aDevice, BOOL aNonRotational)
4443{
4444 CheckComArgStrNotEmptyOrNull(aControllerName);
4445
4446 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4447 aControllerName, aControllerPort, aDevice, aNonRotational));
4448
4449 AutoCaller autoCaller(this);
4450 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4451
4452 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4453
4454 HRESULT rc = checkStateDependency(MutableStateDep);
4455 if (FAILED(rc)) return rc;
4456
4457 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4458
4459 if (Global::IsOnlineOrTransient(mData->mMachineState))
4460 return setError(VBOX_E_INVALID_VM_STATE,
4461 tr("Invalid machine state: %s"),
4462 Global::stringifyMachineState(mData->mMachineState));
4463
4464 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4465 aControllerName,
4466 aControllerPort,
4467 aDevice);
4468 if (!pAttach)
4469 return setError(VBOX_E_OBJECT_NOT_FOUND,
4470 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4471 aDevice, aControllerPort, aControllerName);
4472
4473
4474 setModified(IsModified_Storage);
4475 mMediaData.backup();
4476
4477 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4478
4479 if (pAttach->getType() != DeviceType_HardDisk)
4480 return setError(E_INVALIDARG,
4481 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"),
4482 aDevice, aControllerPort, aControllerName);
4483 pAttach->updateNonRotational(!!aNonRotational);
4484
4485 return S_OK;
4486}
4487
4488STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4489 LONG aDevice, BOOL aDiscard)
4490{
4491 CheckComArgStrNotEmptyOrNull(aControllerName);
4492
4493 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4494 aControllerName, aControllerPort, aDevice, aDiscard));
4495
4496 AutoCaller autoCaller(this);
4497 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4498
4499 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4500
4501 HRESULT rc = checkStateDependency(MutableStateDep);
4502 if (FAILED(rc)) return rc;
4503
4504 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4505
4506 if (Global::IsOnlineOrTransient(mData->mMachineState))
4507 return setError(VBOX_E_INVALID_VM_STATE,
4508 tr("Invalid machine state: %s"),
4509 Global::stringifyMachineState(mData->mMachineState));
4510
4511 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4512 aControllerName,
4513 aControllerPort,
4514 aDevice);
4515 if (!pAttach)
4516 return setError(VBOX_E_OBJECT_NOT_FOUND,
4517 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4518 aDevice, aControllerPort, aControllerName);
4519
4520
4521 setModified(IsModified_Storage);
4522 mMediaData.backup();
4523
4524 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4525
4526 if (pAttach->getType() != DeviceType_HardDisk)
4527 return setError(E_INVALIDARG,
4528 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"),
4529 aDevice, aControllerPort, aControllerName);
4530 pAttach->updateDiscard(!!aDiscard);
4531
4532 return S_OK;
4533}
4534
4535STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4536 LONG aDevice)
4537{
4538 int rc = S_OK;
4539 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4540 aControllerName, aControllerPort, aDevice));
4541
4542 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4543
4544 return rc;
4545}
4546
4547STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4548 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4549{
4550 CheckComArgStrNotEmptyOrNull(aControllerName);
4551
4552 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4553 aControllerName, aControllerPort, aDevice));
4554
4555 AutoCaller autoCaller(this);
4556 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4557
4558 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4559
4560 HRESULT rc = checkStateDependency(MutableStateDep);
4561 if (FAILED(rc)) return rc;
4562
4563 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4564
4565 if (Global::IsOnlineOrTransient(mData->mMachineState))
4566 return setError(VBOX_E_INVALID_VM_STATE,
4567 tr("Invalid machine state: %s"),
4568 Global::stringifyMachineState(mData->mMachineState));
4569
4570 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4571 aControllerName,
4572 aControllerPort,
4573 aDevice);
4574 if (!pAttach)
4575 return setError(VBOX_E_OBJECT_NOT_FOUND,
4576 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4577 aDevice, aControllerPort, aControllerName);
4578
4579
4580 setModified(IsModified_Storage);
4581 mMediaData.backup();
4582
4583 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4584 if (aBandwidthGroup && group.isNull())
4585 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4586
4587 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4588
4589 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4590 if (strBandwidthGroupOld.isNotEmpty())
4591 {
4592 /* Get the bandwidth group object and release it - this must not fail. */
4593 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4594 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4595 Assert(SUCCEEDED(rc));
4596
4597 pBandwidthGroupOld->release();
4598 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4599 }
4600
4601 if (!group.isNull())
4602 {
4603 group->reference();
4604 pAttach->updateBandwidthGroup(group->getName());
4605 }
4606
4607 return S_OK;
4608}
4609
4610STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4611 LONG aControllerPort,
4612 LONG aDevice,
4613 DeviceType_T aType)
4614{
4615 HRESULT rc = S_OK;
4616
4617 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4618 aControllerName, aControllerPort, aDevice, aType));
4619
4620 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4621
4622 return rc;
4623}
4624
4625
4626
4627STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4628 LONG aControllerPort,
4629 LONG aDevice,
4630 BOOL aForce)
4631{
4632 int rc = S_OK;
4633 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4634 aControllerName, aControllerPort, aForce));
4635
4636 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4637
4638 return rc;
4639}
4640
4641STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4642 LONG aControllerPort,
4643 LONG aDevice,
4644 IMedium *aMedium,
4645 BOOL aForce)
4646{
4647 int rc = S_OK;
4648 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4649 aControllerName, aControllerPort, aDevice, aForce));
4650
4651 CheckComArgStrNotEmptyOrNull(aControllerName);
4652
4653 AutoCaller autoCaller(this);
4654 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4655
4656 // request the host lock first, since might be calling Host methods for getting host drives;
4657 // next, protect the media tree all the while we're in here, as well as our member variables
4658 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4659 this->lockHandle(),
4660 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4661
4662 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4663 aControllerName,
4664 aControllerPort,
4665 aDevice);
4666 if (pAttach.isNull())
4667 return setError(VBOX_E_OBJECT_NOT_FOUND,
4668 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4669 aDevice, aControllerPort, aControllerName);
4670
4671 /* Remember previously mounted medium. The medium before taking the
4672 * backup is not necessarily the same thing. */
4673 ComObjPtr<Medium> oldmedium;
4674 oldmedium = pAttach->getMedium();
4675
4676 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4677 if (aMedium && pMedium.isNull())
4678 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4679
4680 AutoCaller mediumCaller(pMedium);
4681 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4682
4683 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4684 if (pMedium)
4685 {
4686 DeviceType_T mediumType = pAttach->getType();
4687 switch (mediumType)
4688 {
4689 case DeviceType_DVD:
4690 case DeviceType_Floppy:
4691 break;
4692
4693 default:
4694 return setError(VBOX_E_INVALID_OBJECT_STATE,
4695 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
4696 aControllerPort,
4697 aDevice,
4698 aControllerName);
4699 }
4700 }
4701
4702 setModified(IsModified_Storage);
4703 mMediaData.backup();
4704
4705 {
4706 // The backup operation makes the pAttach reference point to the
4707 // old settings. Re-get the correct reference.
4708 pAttach = findAttachment(mMediaData->mAttachments,
4709 aControllerName,
4710 aControllerPort,
4711 aDevice);
4712 if (!oldmedium.isNull())
4713 oldmedium->removeBackReference(mData->mUuid);
4714 if (!pMedium.isNull())
4715 {
4716 pMedium->addBackReference(mData->mUuid);
4717
4718 mediumLock.release();
4719 multiLock.release();
4720 addMediumToRegistry(pMedium);
4721 multiLock.acquire();
4722 mediumLock.acquire();
4723 }
4724
4725 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4726 pAttach->updateMedium(pMedium);
4727 }
4728
4729 setModified(IsModified_Storage);
4730
4731 mediumLock.release();
4732 multiLock.release();
4733 rc = onMediumChange(pAttach, aForce);
4734 multiLock.acquire();
4735 mediumLock.acquire();
4736
4737 /* On error roll back this change only. */
4738 if (FAILED(rc))
4739 {
4740 if (!pMedium.isNull())
4741 pMedium->removeBackReference(mData->mUuid);
4742 pAttach = findAttachment(mMediaData->mAttachments,
4743 aControllerName,
4744 aControllerPort,
4745 aDevice);
4746 /* If the attachment is gone in the meantime, bail out. */
4747 if (pAttach.isNull())
4748 return rc;
4749 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4750 if (!oldmedium.isNull())
4751 oldmedium->addBackReference(mData->mUuid);
4752 pAttach->updateMedium(oldmedium);
4753 }
4754
4755 mediumLock.release();
4756 multiLock.release();
4757
4758 mParent->saveModifiedRegistries();
4759
4760 return rc;
4761}
4762
4763STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
4764 LONG aControllerPort,
4765 LONG aDevice,
4766 IMedium **aMedium)
4767{
4768 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4769 aControllerName, aControllerPort, aDevice));
4770
4771 CheckComArgStrNotEmptyOrNull(aControllerName);
4772 CheckComArgOutPointerValid(aMedium);
4773
4774 AutoCaller autoCaller(this);
4775 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4776
4777 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4778
4779 *aMedium = NULL;
4780
4781 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4782 aControllerName,
4783 aControllerPort,
4784 aDevice);
4785 if (pAttach.isNull())
4786 return setError(VBOX_E_OBJECT_NOT_FOUND,
4787 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4788 aDevice, aControllerPort, aControllerName);
4789
4790 pAttach->getMedium().queryInterfaceTo(aMedium);
4791
4792 return S_OK;
4793}
4794
4795STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
4796{
4797 CheckComArgOutPointerValid(port);
4798 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
4799
4800 AutoCaller autoCaller(this);
4801 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4802
4803 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4804
4805 mSerialPorts[slot].queryInterfaceTo(port);
4806
4807 return S_OK;
4808}
4809
4810STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
4811{
4812 CheckComArgOutPointerValid(port);
4813 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
4814
4815 AutoCaller autoCaller(this);
4816 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4817
4818 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4819
4820 mParallelPorts[slot].queryInterfaceTo(port);
4821
4822 return S_OK;
4823}
4824
4825STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
4826{
4827 CheckComArgOutPointerValid(adapter);
4828 /* Do not assert if slot is out of range, just return the advertised
4829 status. testdriver/vbox.py triggers this in logVmInfo. */
4830 if (slot >= mNetworkAdapters.size())
4831 return setError(E_INVALIDARG,
4832 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4833 slot, mNetworkAdapters.size());
4834
4835 AutoCaller autoCaller(this);
4836 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4837
4838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4839
4840 mNetworkAdapters[slot].queryInterfaceTo(adapter);
4841
4842 return S_OK;
4843}
4844
4845STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
4846{
4847 CheckComArgOutSafeArrayPointerValid(aKeys);
4848
4849 AutoCaller autoCaller(this);
4850 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4851
4852 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4853
4854 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4855 int i = 0;
4856 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4857 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4858 ++it, ++i)
4859 {
4860 const Utf8Str &strKey = it->first;
4861 strKey.cloneTo(&saKeys[i]);
4862 }
4863 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
4864
4865 return S_OK;
4866 }
4867
4868 /**
4869 * @note Locks this object for reading.
4870 */
4871STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
4872 BSTR *aValue)
4873{
4874 CheckComArgStrNotEmptyOrNull(aKey);
4875 CheckComArgOutPointerValid(aValue);
4876
4877 AutoCaller autoCaller(this);
4878 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4879
4880 /* start with nothing found */
4881 Bstr bstrResult("");
4882
4883 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4884
4885 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
4886 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4887 // found:
4888 bstrResult = it->second; // source is a Utf8Str
4889
4890 /* return the result to caller (may be empty) */
4891 bstrResult.cloneTo(aValue);
4892
4893 return S_OK;
4894}
4895
4896 /**
4897 * @note Locks mParent for writing + this object for writing.
4898 */
4899STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
4900{
4901 CheckComArgStrNotEmptyOrNull(aKey);
4902
4903 AutoCaller autoCaller(this);
4904 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4905
4906 Utf8Str strKey(aKey);
4907 Utf8Str strValue(aValue);
4908 Utf8Str strOldValue; // empty
4909
4910 // locking note: we only hold the read lock briefly to look up the old value,
4911 // then release it and call the onExtraCanChange callbacks. There is a small
4912 // chance of a race insofar as the callback might be called twice if two callers
4913 // change the same key at the same time, but that's a much better solution
4914 // than the deadlock we had here before. The actual changing of the extradata
4915 // is then performed under the write lock and race-free.
4916
4917 // look up the old value first; if nothing has changed then we need not do anything
4918 {
4919 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4920 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
4921 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4922 strOldValue = it->second;
4923 }
4924
4925 bool fChanged;
4926 if ((fChanged = (strOldValue != strValue)))
4927 {
4928 // ask for permission from all listeners outside the locks;
4929 // onExtraDataCanChange() only briefly requests the VirtualBox
4930 // lock to copy the list of callbacks to invoke
4931 Bstr error;
4932 Bstr bstrValue(aValue);
4933
4934 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
4935 {
4936 const char *sep = error.isEmpty() ? "" : ": ";
4937 CBSTR err = error.raw();
4938 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4939 sep, err));
4940 return setError(E_ACCESSDENIED,
4941 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
4942 aKey,
4943 bstrValue.raw(),
4944 sep,
4945 err);
4946 }
4947
4948 // data is changing and change not vetoed: then write it out under the lock
4949 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4950
4951 if (isSnapshotMachine())
4952 {
4953 HRESULT rc = checkStateDependency(MutableStateDep);
4954 if (FAILED(rc)) return rc;
4955 }
4956
4957 if (strValue.isEmpty())
4958 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
4959 else
4960 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
4961 // creates a new key if needed
4962
4963 bool fNeedsGlobalSaveSettings = false;
4964 saveSettings(&fNeedsGlobalSaveSettings);
4965
4966 if (fNeedsGlobalSaveSettings)
4967 {
4968 // save the global settings; for that we should hold only the VirtualBox lock
4969 alock.release();
4970 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4971 mParent->saveSettings();
4972 }
4973 }
4974
4975 // fire notification outside the lock
4976 if (fChanged)
4977 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
4978
4979 return S_OK;
4980}
4981
4982STDMETHODIMP Machine::SaveSettings()
4983{
4984 AutoCaller autoCaller(this);
4985 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4986
4987 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4988
4989 /* when there was auto-conversion, we want to save the file even if
4990 * the VM is saved */
4991 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
4992 if (FAILED(rc)) return rc;
4993
4994 /* the settings file path may never be null */
4995 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4996
4997 /* save all VM data excluding snapshots */
4998 bool fNeedsGlobalSaveSettings = false;
4999 rc = saveSettings(&fNeedsGlobalSaveSettings);
5000 mlock.release();
5001
5002 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5003 {
5004 // save the global settings; for that we should hold only the VirtualBox lock
5005 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5006 rc = mParent->saveSettings();
5007 }
5008
5009 return rc;
5010}
5011
5012STDMETHODIMP Machine::DiscardSettings()
5013{
5014 AutoCaller autoCaller(this);
5015 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5016
5017 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5018
5019 HRESULT rc = checkStateDependency(MutableStateDep);
5020 if (FAILED(rc)) return rc;
5021
5022 /*
5023 * during this rollback, the session will be notified if data has
5024 * been actually changed
5025 */
5026 rollback(true /* aNotify */);
5027
5028 return S_OK;
5029}
5030
5031/** @note Locks objects! */
5032STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
5033 ComSafeArrayOut(IMedium*, aMedia))
5034{
5035 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5036 AutoLimitedCaller autoCaller(this);
5037 AssertComRCReturnRC(autoCaller.rc());
5038
5039 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5040
5041 Guid id(getId());
5042
5043 if (mData->mSession.mState != SessionState_Unlocked)
5044 return setError(VBOX_E_INVALID_OBJECT_STATE,
5045 tr("Cannot unregister the machine '%s' while it is locked"),
5046 mUserData->s.strName.c_str());
5047
5048 // wait for state dependents to drop to zero
5049 ensureNoStateDependencies();
5050
5051 if (!mData->mAccessible)
5052 {
5053 // inaccessible maschines can only be unregistered; uninitialize ourselves
5054 // here because currently there may be no unregistered that are inaccessible
5055 // (this state combination is not supported). Note releasing the caller and
5056 // leaving the lock before calling uninit()
5057 alock.release();
5058 autoCaller.release();
5059
5060 uninit();
5061
5062 mParent->unregisterMachine(this, id);
5063 // calls VirtualBox::saveSettings()
5064
5065 return S_OK;
5066 }
5067
5068 HRESULT rc = S_OK;
5069
5070 // discard saved state
5071 if (mData->mMachineState == MachineState_Saved)
5072 {
5073 // add the saved state file to the list of files the caller should delete
5074 Assert(!mSSData->strStateFilePath.isEmpty());
5075 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5076
5077 mSSData->strStateFilePath.setNull();
5078
5079 // unconditionally set the machine state to powered off, we now
5080 // know no session has locked the machine
5081 mData->mMachineState = MachineState_PoweredOff;
5082 }
5083
5084 size_t cSnapshots = 0;
5085 if (mData->mFirstSnapshot)
5086 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5087 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
5088 // fail now before we start detaching media
5089 return setError(VBOX_E_INVALID_OBJECT_STATE,
5090 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5091 mUserData->s.strName.c_str(), cSnapshots);
5092
5093 // This list collects the medium objects from all medium attachments
5094 // which we will detach from the machine and its snapshots, in a specific
5095 // order which allows for closing all media without getting "media in use"
5096 // errors, simply by going through the list from the front to the back:
5097 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5098 // and must be closed before the parent media from the snapshots, or closing the parents
5099 // will fail because they still have children);
5100 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5101 // the root ("first") snapshot of the machine.
5102 MediaList llMedia;
5103
5104 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5105 && mMediaData->mAttachments.size()
5106 )
5107 {
5108 // we have media attachments: detach them all and add the Medium objects to our list
5109 if (cleanupMode != CleanupMode_UnregisterOnly)
5110 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5111 else
5112 return setError(VBOX_E_INVALID_OBJECT_STATE,
5113 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5114 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5115 }
5116
5117 if (cSnapshots)
5118 {
5119 // autoCleanup must be true here, or we would have failed above
5120
5121 // add the media from the medium attachments of the snapshots to llMedia
5122 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5123 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5124 // into the children first
5125
5126 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5127 MachineState_T oldState = mData->mMachineState;
5128 mData->mMachineState = MachineState_DeletingSnapshot;
5129
5130 // make a copy of the first snapshot so the refcount does not drop to 0
5131 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5132 // because of the AutoCaller voodoo)
5133 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5134
5135 // GO!
5136 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5137
5138 mData->mMachineState = oldState;
5139 }
5140
5141 if (FAILED(rc))
5142 {
5143 rollbackMedia();
5144 return rc;
5145 }
5146
5147 // commit all the media changes made above
5148 commitMedia();
5149
5150 mData->mRegistered = false;
5151
5152 // machine lock no longer needed
5153 alock.release();
5154
5155 // return media to caller
5156 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5157 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5158
5159 mParent->unregisterMachine(this, id);
5160 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5161
5162 return S_OK;
5163}
5164
5165struct Machine::DeleteTask
5166{
5167 ComObjPtr<Machine> pMachine;
5168 RTCList<ComPtr<IMedium> > llMediums;
5169 StringsList llFilesToDelete;
5170 ComObjPtr<Progress> pProgress;
5171};
5172
5173STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5174{
5175 LogFlowFuncEnter();
5176
5177 AutoCaller autoCaller(this);
5178 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5179
5180 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5181
5182 HRESULT rc = checkStateDependency(MutableStateDep);
5183 if (FAILED(rc)) return rc;
5184
5185 if (mData->mRegistered)
5186 return setError(VBOX_E_INVALID_VM_STATE,
5187 tr("Cannot delete settings of a registered machine"));
5188
5189 DeleteTask *pTask = new DeleteTask;
5190 pTask->pMachine = this;
5191 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5192
5193 // collect files to delete
5194 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5195
5196 for (size_t i = 0; i < sfaMedia.size(); ++i)
5197 {
5198 IMedium *pIMedium(sfaMedia[i]);
5199 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5200 if (pMedium.isNull())
5201 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5202 SafeArray<BSTR> ids;
5203 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5204 if (FAILED(rc)) return rc;
5205 /* At this point the medium should not have any back references
5206 * anymore. If it has it is attached to another VM and *must* not
5207 * deleted. */
5208 if (ids.size() < 1)
5209 pTask->llMediums.append(pMedium);
5210 }
5211 if (mData->pMachineConfigFile->fileExists())
5212 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5213
5214 pTask->pProgress.createObject();
5215 pTask->pProgress->init(getVirtualBox(),
5216 static_cast<IMachine*>(this) /* aInitiator */,
5217 Bstr(tr("Deleting files")).raw(),
5218 true /* fCancellable */,
5219 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5220 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5221
5222 int vrc = RTThreadCreate(NULL,
5223 Machine::deleteThread,
5224 (void*)pTask,
5225 0,
5226 RTTHREADTYPE_MAIN_WORKER,
5227 0,
5228 "MachineDelete");
5229
5230 pTask->pProgress.queryInterfaceTo(aProgress);
5231
5232 if (RT_FAILURE(vrc))
5233 {
5234 delete pTask;
5235 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5236 }
5237
5238 LogFlowFuncLeave();
5239
5240 return S_OK;
5241}
5242
5243/**
5244 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5245 * calls Machine::deleteTaskWorker() on the actual machine object.
5246 * @param Thread
5247 * @param pvUser
5248 * @return
5249 */
5250/*static*/
5251DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5252{
5253 LogFlowFuncEnter();
5254
5255 DeleteTask *pTask = (DeleteTask*)pvUser;
5256 Assert(pTask);
5257 Assert(pTask->pMachine);
5258 Assert(pTask->pProgress);
5259
5260 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5261 pTask->pProgress->notifyComplete(rc);
5262
5263 delete pTask;
5264
5265 LogFlowFuncLeave();
5266
5267 NOREF(Thread);
5268
5269 return VINF_SUCCESS;
5270}
5271
5272/**
5273 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5274 * @param task
5275 * @return
5276 */
5277HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5278{
5279 AutoCaller autoCaller(this);
5280 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5281
5282 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5283
5284 HRESULT rc = S_OK;
5285
5286 try
5287 {
5288 ULONG uLogHistoryCount = 3;
5289 ComPtr<ISystemProperties> systemProperties;
5290 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5291 if (FAILED(rc)) throw rc;
5292
5293 if (!systemProperties.isNull())
5294 {
5295 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5296 if (FAILED(rc)) throw rc;
5297 }
5298
5299 MachineState_T oldState = mData->mMachineState;
5300 setMachineState(MachineState_SettingUp);
5301 alock.release();
5302 for (size_t i = 0; i < task.llMediums.size(); ++i)
5303 {
5304 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5305 {
5306 AutoCaller mac(pMedium);
5307 if (FAILED(mac.rc())) throw mac.rc();
5308 Utf8Str strLocation = pMedium->getLocationFull();
5309 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5310 if (FAILED(rc)) throw rc;
5311 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5312 }
5313 ComPtr<IProgress> pProgress2;
5314 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5315 if (FAILED(rc)) throw rc;
5316 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5317 if (FAILED(rc)) throw rc;
5318 /* Check the result of the asynchrony process. */
5319 LONG iRc;
5320 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5321 if (FAILED(rc)) throw rc;
5322 /* If the thread of the progress object has an error, then
5323 * retrieve the error info from there, or it'll be lost. */
5324 if (FAILED(iRc))
5325 throw setError(ProgressErrorInfo(pProgress2));
5326 }
5327 setMachineState(oldState);
5328 alock.acquire();
5329
5330 // delete the files pushed on the task list by Machine::Delete()
5331 // (this includes saved states of the machine and snapshots and
5332 // medium storage files from the IMedium list passed in, and the
5333 // machine XML file)
5334 StringsList::const_iterator it = task.llFilesToDelete.begin();
5335 while (it != task.llFilesToDelete.end())
5336 {
5337 const Utf8Str &strFile = *it;
5338 LogFunc(("Deleting file %s\n", strFile.c_str()));
5339 int vrc = RTFileDelete(strFile.c_str());
5340 if (RT_FAILURE(vrc))
5341 throw setError(VBOX_E_IPRT_ERROR,
5342 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5343
5344 ++it;
5345 if (it == task.llFilesToDelete.end())
5346 {
5347 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5348 if (FAILED(rc)) throw rc;
5349 break;
5350 }
5351
5352 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5353 if (FAILED(rc)) throw rc;
5354 }
5355
5356 /* delete the settings only when the file actually exists */
5357 if (mData->pMachineConfigFile->fileExists())
5358 {
5359 /* Delete any backup or uncommitted XML files. Ignore failures.
5360 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5361 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5362 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5363 RTFileDelete(otherXml.c_str());
5364 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5365 RTFileDelete(otherXml.c_str());
5366
5367 /* delete the Logs folder, nothing important should be left
5368 * there (we don't check for errors because the user might have
5369 * some private files there that we don't want to delete) */
5370 Utf8Str logFolder;
5371 getLogFolder(logFolder);
5372 Assert(logFolder.length());
5373 if (RTDirExists(logFolder.c_str()))
5374 {
5375 /* Delete all VBox.log[.N] files from the Logs folder
5376 * (this must be in sync with the rotation logic in
5377 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5378 * files that may have been created by the GUI. */
5379 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5380 logFolder.c_str(), RTPATH_DELIMITER);
5381 RTFileDelete(log.c_str());
5382 log = Utf8StrFmt("%s%cVBox.png",
5383 logFolder.c_str(), RTPATH_DELIMITER);
5384 RTFileDelete(log.c_str());
5385 for (int i = uLogHistoryCount; i > 0; i--)
5386 {
5387 log = Utf8StrFmt("%s%cVBox.log.%d",
5388 logFolder.c_str(), RTPATH_DELIMITER, i);
5389 RTFileDelete(log.c_str());
5390 log = Utf8StrFmt("%s%cVBox.png.%d",
5391 logFolder.c_str(), RTPATH_DELIMITER, i);
5392 RTFileDelete(log.c_str());
5393 }
5394
5395 RTDirRemove(logFolder.c_str());
5396 }
5397
5398 /* delete the Snapshots folder, nothing important should be left
5399 * there (we don't check for errors because the user might have
5400 * some private files there that we don't want to delete) */
5401 Utf8Str strFullSnapshotFolder;
5402 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5403 Assert(!strFullSnapshotFolder.isEmpty());
5404 if (RTDirExists(strFullSnapshotFolder.c_str()))
5405 RTDirRemove(strFullSnapshotFolder.c_str());
5406
5407 // delete the directory that contains the settings file, but only
5408 // if it matches the VM name
5409 Utf8Str settingsDir;
5410 if (isInOwnDir(&settingsDir))
5411 RTDirRemove(settingsDir.c_str());
5412 }
5413
5414 alock.release();
5415
5416 mParent->saveModifiedRegistries();
5417 }
5418 catch (HRESULT aRC) { rc = aRC; }
5419
5420 return rc;
5421}
5422
5423STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5424{
5425 CheckComArgOutPointerValid(aSnapshot);
5426
5427 AutoCaller autoCaller(this);
5428 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5429
5430 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5431
5432 ComObjPtr<Snapshot> pSnapshot;
5433 HRESULT rc;
5434
5435 if (!aNameOrId || !*aNameOrId)
5436 // null case (caller wants root snapshot): findSnapshotById() handles this
5437 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5438 else
5439 {
5440 Guid uuid(aNameOrId);
5441 if (uuid.isValid())
5442 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5443 else
5444 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5445 }
5446 pSnapshot.queryInterfaceTo(aSnapshot);
5447
5448 return rc;
5449}
5450
5451STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5452{
5453 CheckComArgStrNotEmptyOrNull(aName);
5454 CheckComArgStrNotEmptyOrNull(aHostPath);
5455
5456 AutoCaller autoCaller(this);
5457 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5458
5459 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5460
5461 HRESULT rc = checkStateDependency(MutableStateDep);
5462 if (FAILED(rc)) return rc;
5463
5464 Utf8Str strName(aName);
5465
5466 ComObjPtr<SharedFolder> sharedFolder;
5467 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5468 if (SUCCEEDED(rc))
5469 return setError(VBOX_E_OBJECT_IN_USE,
5470 tr("Shared folder named '%s' already exists"),
5471 strName.c_str());
5472
5473 sharedFolder.createObject();
5474 rc = sharedFolder->init(getMachine(),
5475 strName,
5476 aHostPath,
5477 !!aWritable,
5478 !!aAutoMount,
5479 true /* fFailOnError */);
5480 if (FAILED(rc)) return rc;
5481
5482 setModified(IsModified_SharedFolders);
5483 mHWData.backup();
5484 mHWData->mSharedFolders.push_back(sharedFolder);
5485
5486 /* inform the direct session if any */
5487 alock.release();
5488 onSharedFolderChange();
5489
5490 return S_OK;
5491}
5492
5493STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5494{
5495 CheckComArgStrNotEmptyOrNull(aName);
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 ComObjPtr<SharedFolder> sharedFolder;
5506 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5507 if (FAILED(rc)) return rc;
5508
5509 setModified(IsModified_SharedFolders);
5510 mHWData.backup();
5511 mHWData->mSharedFolders.remove(sharedFolder);
5512
5513 /* inform the direct session if any */
5514 alock.release();
5515 onSharedFolderChange();
5516
5517 return S_OK;
5518}
5519
5520STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5521{
5522 CheckComArgOutPointerValid(aCanShow);
5523
5524 /* start with No */
5525 *aCanShow = FALSE;
5526
5527 AutoCaller autoCaller(this);
5528 AssertComRCReturnRC(autoCaller.rc());
5529
5530 ComPtr<IInternalSessionControl> directControl;
5531 {
5532 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5533
5534 if (mData->mSession.mState != SessionState_Locked)
5535 return setError(VBOX_E_INVALID_VM_STATE,
5536 tr("Machine is not locked for session (session state: %s)"),
5537 Global::stringifySessionState(mData->mSession.mState));
5538
5539 directControl = mData->mSession.mDirectControl;
5540 }
5541
5542 /* ignore calls made after #OnSessionEnd() is called */
5543 if (!directControl)
5544 return S_OK;
5545
5546 LONG64 dummy;
5547 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5548}
5549
5550STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5551{
5552 CheckComArgOutPointerValid(aWinId);
5553
5554 AutoCaller autoCaller(this);
5555 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5556
5557 ComPtr<IInternalSessionControl> directControl;
5558 {
5559 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5560
5561 if (mData->mSession.mState != SessionState_Locked)
5562 return setError(E_FAIL,
5563 tr("Machine is not locked for session (session state: %s)"),
5564 Global::stringifySessionState(mData->mSession.mState));
5565
5566 directControl = mData->mSession.mDirectControl;
5567 }
5568
5569 /* ignore calls made after #OnSessionEnd() is called */
5570 if (!directControl)
5571 return S_OK;
5572
5573 BOOL dummy;
5574 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5575}
5576
5577#ifdef VBOX_WITH_GUEST_PROPS
5578/**
5579 * Look up a guest property in VBoxSVC's internal structures.
5580 */
5581HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5582 BSTR *aValue,
5583 LONG64 *aTimestamp,
5584 BSTR *aFlags) const
5585{
5586 using namespace guestProp;
5587
5588 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5589 Utf8Str strName(aName);
5590 HWData::GuestPropertyMap::const_iterator it =
5591 mHWData->mGuestProperties.find(strName);
5592
5593 if (it != mHWData->mGuestProperties.end())
5594 {
5595 char szFlags[MAX_FLAGS_LEN + 1];
5596 it->second.strValue.cloneTo(aValue);
5597 *aTimestamp = it->second.mTimestamp;
5598 writeFlags(it->second.mFlags, szFlags);
5599 Bstr(szFlags).cloneTo(aFlags);
5600 }
5601
5602 return S_OK;
5603}
5604
5605/**
5606 * Query the VM that a guest property belongs to for the property.
5607 * @returns E_ACCESSDENIED if the VM process is not available or not
5608 * currently handling queries and the lookup should then be done in
5609 * VBoxSVC.
5610 */
5611HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5612 BSTR *aValue,
5613 LONG64 *aTimestamp,
5614 BSTR *aFlags) const
5615{
5616 HRESULT rc;
5617 ComPtr<IInternalSessionControl> directControl;
5618 directControl = mData->mSession.mDirectControl;
5619
5620 /* fail if we were called after #OnSessionEnd() is called. This is a
5621 * silly race condition. */
5622
5623 if (!directControl)
5624 rc = E_ACCESSDENIED;
5625 else
5626 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5627 false /* isSetter */,
5628 aValue, aTimestamp, aFlags);
5629 return rc;
5630}
5631#endif // VBOX_WITH_GUEST_PROPS
5632
5633STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5634 BSTR *aValue,
5635 LONG64 *aTimestamp,
5636 BSTR *aFlags)
5637{
5638#ifndef VBOX_WITH_GUEST_PROPS
5639 ReturnComNotImplemented();
5640#else // VBOX_WITH_GUEST_PROPS
5641 CheckComArgStrNotEmptyOrNull(aName);
5642 CheckComArgOutPointerValid(aValue);
5643 CheckComArgOutPointerValid(aTimestamp);
5644 CheckComArgOutPointerValid(aFlags);
5645
5646 AutoCaller autoCaller(this);
5647 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5648
5649 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5650 if (rc == E_ACCESSDENIED)
5651 /* The VM is not running or the service is not (yet) accessible */
5652 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5653 return rc;
5654#endif // VBOX_WITH_GUEST_PROPS
5655}
5656
5657STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5658{
5659 LONG64 dummyTimestamp;
5660 Bstr dummyFlags;
5661 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5662}
5663
5664STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5665{
5666 Bstr dummyValue;
5667 Bstr dummyFlags;
5668 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5669}
5670
5671#ifdef VBOX_WITH_GUEST_PROPS
5672/**
5673 * Set a guest property in VBoxSVC's internal structures.
5674 */
5675HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5676 IN_BSTR aFlags)
5677{
5678 using namespace guestProp;
5679
5680 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5681 HRESULT rc = S_OK;
5682 HWData::GuestProperty property;
5683 property.mFlags = NILFLAG;
5684
5685 rc = checkStateDependency(MutableStateDep);
5686 if (FAILED(rc)) return rc;
5687
5688 try
5689 {
5690 Utf8Str utf8Name(aName);
5691 Utf8Str utf8Flags(aFlags);
5692 uint32_t fFlags = NILFLAG;
5693 if ( (aFlags != NULL)
5694 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags))
5695 )
5696 return setError(E_INVALIDARG,
5697 tr("Invalid guest property flag values: '%ls'"),
5698 aFlags);
5699
5700 HWData::GuestPropertyMap::iterator it =
5701 mHWData->mGuestProperties.find(utf8Name);
5702
5703 if (it == mHWData->mGuestProperties.end())
5704 {
5705 setModified(IsModified_MachineData);
5706 mHWData.backupEx();
5707
5708 RTTIMESPEC time;
5709 HWData::GuestProperty prop;
5710 prop.strValue = aValue;
5711 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5712 prop.mFlags = fFlags;
5713
5714 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
5715 }
5716 else
5717 {
5718 if (it->second.mFlags & (RDONLYHOST))
5719 {
5720 rc = setError(E_ACCESSDENIED,
5721 tr("The property '%ls' cannot be changed by the host"),
5722 aName);
5723 }
5724 else
5725 {
5726 setModified(IsModified_MachineData);
5727 mHWData.backupEx();
5728
5729 /* The backupEx() operation invalidates our iterator,
5730 * so get a new one. */
5731 it = mHWData->mGuestProperties.find(utf8Name);
5732 Assert(it != mHWData->mGuestProperties.end());
5733
5734 if (RT_VALID_PTR(aValue) && *(aValue) != '\0')
5735 {
5736 RTTIMESPEC time;
5737 it->second.strValue = aValue;
5738 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5739 if (aFlags != NULL)
5740 it->second.mFlags = fFlags;
5741 }
5742 else
5743 {
5744 mHWData->mGuestProperties.erase(it);
5745 }
5746 }
5747 }
5748
5749 if ( SUCCEEDED(rc)
5750 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5751 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5752 RTSTR_MAX,
5753 utf8Name.c_str(),
5754 RTSTR_MAX,
5755 NULL)
5756 )
5757 )
5758 {
5759 alock.release();
5760
5761 mParent->onGuestPropertyChange(mData->mUuid, aName,
5762 aValue ? aValue : Bstr("").raw(),
5763 aFlags ? aFlags : Bstr("").raw());
5764 }
5765 }
5766 catch (std::bad_alloc &)
5767 {
5768 rc = E_OUTOFMEMORY;
5769 }
5770
5771 return rc;
5772}
5773
5774/**
5775 * Set a property on the VM that that property belongs to.
5776 * @returns E_ACCESSDENIED if the VM process is not available or not
5777 * currently handling queries and the setting should then be done in
5778 * VBoxSVC.
5779 */
5780HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
5781 IN_BSTR aFlags)
5782{
5783 HRESULT rc;
5784
5785 try
5786 {
5787 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5788
5789 BSTR dummy = NULL; /* will not be changed (setter) */
5790 LONG64 dummy64;
5791 if (!directControl)
5792 rc = E_ACCESSDENIED;
5793 else
5794 /** @todo Fix when adding DeleteGuestProperty(),
5795 see defect. */
5796 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
5797 true /* isSetter */,
5798 &dummy, &dummy64, &dummy);
5799 }
5800 catch (std::bad_alloc &)
5801 {
5802 rc = E_OUTOFMEMORY;
5803 }
5804
5805 return rc;
5806}
5807#endif // VBOX_WITH_GUEST_PROPS
5808
5809STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
5810 IN_BSTR aFlags)
5811{
5812#ifndef VBOX_WITH_GUEST_PROPS
5813 ReturnComNotImplemented();
5814#else // VBOX_WITH_GUEST_PROPS
5815 CheckComArgStrNotEmptyOrNull(aName);
5816 CheckComArgMaybeNull(aFlags);
5817 CheckComArgMaybeNull(aValue);
5818
5819 AutoCaller autoCaller(this);
5820 if (FAILED(autoCaller.rc()))
5821 return autoCaller.rc();
5822
5823 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
5824 if (rc == E_ACCESSDENIED)
5825 /* The VM is not running or the service is not (yet) accessible */
5826 rc = setGuestPropertyToService(aName, aValue, aFlags);
5827 return rc;
5828#endif // VBOX_WITH_GUEST_PROPS
5829}
5830
5831STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
5832{
5833 return SetGuestProperty(aName, aValue, NULL);
5834}
5835
5836STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
5837{
5838 return SetGuestProperty(aName, NULL, NULL);
5839}
5840
5841#ifdef VBOX_WITH_GUEST_PROPS
5842/**
5843 * Enumerate the guest properties in VBoxSVC's internal structures.
5844 */
5845HRESULT Machine::enumerateGuestPropertiesInService
5846 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5847 ComSafeArrayOut(BSTR, aValues),
5848 ComSafeArrayOut(LONG64, aTimestamps),
5849 ComSafeArrayOut(BSTR, aFlags))
5850{
5851 using namespace guestProp;
5852
5853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5854 Utf8Str strPatterns(aPatterns);
5855
5856 HWData::GuestPropertyMap propMap;
5857
5858 /*
5859 * Look for matching patterns and build up a list.
5860 */
5861 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5862 while (it != mHWData->mGuestProperties.end())
5863 {
5864 if ( strPatterns.isEmpty()
5865 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5866 RTSTR_MAX,
5867 it->first.c_str(),
5868 RTSTR_MAX,
5869 NULL)
5870 )
5871 {
5872 propMap.insert(*it);
5873 }
5874
5875 it++;
5876 }
5877
5878 alock.release();
5879
5880 /*
5881 * And build up the arrays for returning the property information.
5882 */
5883 size_t cEntries = propMap.size();
5884 SafeArray<BSTR> names(cEntries);
5885 SafeArray<BSTR> values(cEntries);
5886 SafeArray<LONG64> timestamps(cEntries);
5887 SafeArray<BSTR> flags(cEntries);
5888 size_t iProp = 0;
5889
5890 it = propMap.begin();
5891 while (it != propMap.end())
5892 {
5893 char szFlags[MAX_FLAGS_LEN + 1];
5894 it->first.cloneTo(&names[iProp]);
5895 it->second.strValue.cloneTo(&values[iProp]);
5896 timestamps[iProp] = it->second.mTimestamp;
5897 writeFlags(it->second.mFlags, szFlags);
5898 Bstr(szFlags).cloneTo(&flags[iProp++]);
5899 it++;
5900 }
5901 names.detachTo(ComSafeArrayOutArg(aNames));
5902 values.detachTo(ComSafeArrayOutArg(aValues));
5903 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
5904 flags.detachTo(ComSafeArrayOutArg(aFlags));
5905 return S_OK;
5906}
5907
5908/**
5909 * Enumerate the properties managed by a VM.
5910 * @returns E_ACCESSDENIED if the VM process is not available or not
5911 * currently handling queries and the setting should then be done in
5912 * VBoxSVC.
5913 */
5914HRESULT Machine::enumerateGuestPropertiesOnVM
5915 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5916 ComSafeArrayOut(BSTR, aValues),
5917 ComSafeArrayOut(LONG64, aTimestamps),
5918 ComSafeArrayOut(BSTR, aFlags))
5919{
5920 HRESULT rc;
5921 ComPtr<IInternalSessionControl> directControl;
5922 directControl = mData->mSession.mDirectControl;
5923
5924 if (!directControl)
5925 rc = E_ACCESSDENIED;
5926 else
5927 rc = directControl->EnumerateGuestProperties
5928 (aPatterns, ComSafeArrayOutArg(aNames),
5929 ComSafeArrayOutArg(aValues),
5930 ComSafeArrayOutArg(aTimestamps),
5931 ComSafeArrayOutArg(aFlags));
5932 return rc;
5933}
5934#endif // VBOX_WITH_GUEST_PROPS
5935
5936STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
5937 ComSafeArrayOut(BSTR, aNames),
5938 ComSafeArrayOut(BSTR, aValues),
5939 ComSafeArrayOut(LONG64, aTimestamps),
5940 ComSafeArrayOut(BSTR, aFlags))
5941{
5942#ifndef VBOX_WITH_GUEST_PROPS
5943 ReturnComNotImplemented();
5944#else // VBOX_WITH_GUEST_PROPS
5945 CheckComArgMaybeNull(aPatterns);
5946 CheckComArgOutSafeArrayPointerValid(aNames);
5947 CheckComArgOutSafeArrayPointerValid(aValues);
5948 CheckComArgOutSafeArrayPointerValid(aTimestamps);
5949 CheckComArgOutSafeArrayPointerValid(aFlags);
5950
5951 AutoCaller autoCaller(this);
5952 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5953
5954 HRESULT rc = enumerateGuestPropertiesOnVM
5955 (aPatterns, ComSafeArrayOutArg(aNames),
5956 ComSafeArrayOutArg(aValues),
5957 ComSafeArrayOutArg(aTimestamps),
5958 ComSafeArrayOutArg(aFlags));
5959 if (rc == E_ACCESSDENIED)
5960 /* The VM is not running or the service is not (yet) accessible */
5961 rc = enumerateGuestPropertiesInService
5962 (aPatterns, ComSafeArrayOutArg(aNames),
5963 ComSafeArrayOutArg(aValues),
5964 ComSafeArrayOutArg(aTimestamps),
5965 ComSafeArrayOutArg(aFlags));
5966 return rc;
5967#endif // VBOX_WITH_GUEST_PROPS
5968}
5969
5970STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
5971 ComSafeArrayOut(IMediumAttachment*, aAttachments))
5972{
5973 MediaData::AttachmentList atts;
5974
5975 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
5976 if (FAILED(rc)) return rc;
5977
5978 SafeIfaceArray<IMediumAttachment> attachments(atts);
5979 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
5980
5981 return S_OK;
5982}
5983
5984STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
5985 LONG aControllerPort,
5986 LONG aDevice,
5987 IMediumAttachment **aAttachment)
5988{
5989 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5990 aControllerName, aControllerPort, aDevice));
5991
5992 CheckComArgStrNotEmptyOrNull(aControllerName);
5993 CheckComArgOutPointerValid(aAttachment);
5994
5995 AutoCaller autoCaller(this);
5996 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5997
5998 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5999
6000 *aAttachment = NULL;
6001
6002 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
6003 aControllerName,
6004 aControllerPort,
6005 aDevice);
6006 if (pAttach.isNull())
6007 return setError(VBOX_E_OBJECT_NOT_FOUND,
6008 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
6009 aDevice, aControllerPort, aControllerName);
6010
6011 pAttach.queryInterfaceTo(aAttachment);
6012
6013 return S_OK;
6014}
6015
6016STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
6017 StorageBus_T aConnectionType,
6018 IStorageController **controller)
6019{
6020 CheckComArgStrNotEmptyOrNull(aName);
6021
6022 if ( (aConnectionType <= StorageBus_Null)
6023 || (aConnectionType > StorageBus_SAS))
6024 return setError(E_INVALIDARG,
6025 tr("Invalid connection type: %d"),
6026 aConnectionType);
6027
6028 AutoCaller autoCaller(this);
6029 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6030
6031 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6032
6033 HRESULT rc = checkStateDependency(MutableStateDep);
6034 if (FAILED(rc)) return rc;
6035
6036 /* try to find one with the name first. */
6037 ComObjPtr<StorageController> ctrl;
6038
6039 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
6040 if (SUCCEEDED(rc))
6041 return setError(VBOX_E_OBJECT_IN_USE,
6042 tr("Storage controller named '%ls' already exists"),
6043 aName);
6044
6045 ctrl.createObject();
6046
6047 /* get a new instance number for the storage controller */
6048 ULONG ulInstance = 0;
6049 bool fBootable = true;
6050 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6051 it != mStorageControllers->end();
6052 ++it)
6053 {
6054 if ((*it)->getStorageBus() == aConnectionType)
6055 {
6056 ULONG ulCurInst = (*it)->getInstance();
6057
6058 if (ulCurInst >= ulInstance)
6059 ulInstance = ulCurInst + 1;
6060
6061 /* Only one controller of each type can be marked as bootable. */
6062 if ((*it)->getBootable())
6063 fBootable = false;
6064 }
6065 }
6066
6067 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6068 if (FAILED(rc)) return rc;
6069
6070 setModified(IsModified_Storage);
6071 mStorageControllers.backup();
6072 mStorageControllers->push_back(ctrl);
6073
6074 ctrl.queryInterfaceTo(controller);
6075
6076 /* inform the direct session if any */
6077 alock.release();
6078 onStorageControllerChange();
6079
6080 return S_OK;
6081}
6082
6083STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6084 IStorageController **aStorageController)
6085{
6086 CheckComArgStrNotEmptyOrNull(aName);
6087
6088 AutoCaller autoCaller(this);
6089 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6090
6091 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6092
6093 ComObjPtr<StorageController> ctrl;
6094
6095 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6096 if (SUCCEEDED(rc))
6097 ctrl.queryInterfaceTo(aStorageController);
6098
6099 return rc;
6100}
6101
6102STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6103 IStorageController **aStorageController)
6104{
6105 AutoCaller autoCaller(this);
6106 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6107
6108 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6109
6110 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6111 it != mStorageControllers->end();
6112 ++it)
6113 {
6114 if ((*it)->getInstance() == aInstance)
6115 {
6116 (*it).queryInterfaceTo(aStorageController);
6117 return S_OK;
6118 }
6119 }
6120
6121 return setError(VBOX_E_OBJECT_NOT_FOUND,
6122 tr("Could not find a storage controller with instance number '%lu'"),
6123 aInstance);
6124}
6125
6126STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6127{
6128 AutoCaller autoCaller(this);
6129 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6130
6131 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6132
6133 HRESULT rc = checkStateDependency(MutableStateDep);
6134 if (FAILED(rc)) return rc;
6135
6136 ComObjPtr<StorageController> ctrl;
6137
6138 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6139 if (SUCCEEDED(rc))
6140 {
6141 /* Ensure that only one controller of each type is marked as bootable. */
6142 if (fBootable == TRUE)
6143 {
6144 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6145 it != mStorageControllers->end();
6146 ++it)
6147 {
6148 ComObjPtr<StorageController> aCtrl = (*it);
6149
6150 if ( (aCtrl->getName() != Utf8Str(aName))
6151 && aCtrl->getBootable() == TRUE
6152 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6153 && aCtrl->getControllerType() == ctrl->getControllerType())
6154 {
6155 aCtrl->setBootable(FALSE);
6156 break;
6157 }
6158 }
6159 }
6160
6161 if (SUCCEEDED(rc))
6162 {
6163 ctrl->setBootable(fBootable);
6164 setModified(IsModified_Storage);
6165 }
6166 }
6167
6168 if (SUCCEEDED(rc))
6169 {
6170 /* inform the direct session if any */
6171 alock.release();
6172 onStorageControllerChange();
6173 }
6174
6175 return rc;
6176}
6177
6178STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6179{
6180 CheckComArgStrNotEmptyOrNull(aName);
6181
6182 AutoCaller autoCaller(this);
6183 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6184
6185 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6186
6187 HRESULT rc = checkStateDependency(MutableStateDep);
6188 if (FAILED(rc)) return rc;
6189
6190 ComObjPtr<StorageController> ctrl;
6191 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6192 if (FAILED(rc)) return rc;
6193
6194 {
6195 /* find all attached devices to the appropriate storage controller and detach them all */
6196 // make a temporary list because detachDevice invalidates iterators into
6197 // mMediaData->mAttachments
6198 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6199
6200 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6201 it != llAttachments2.end();
6202 ++it)
6203 {
6204 MediumAttachment *pAttachTemp = *it;
6205
6206 AutoCaller localAutoCaller(pAttachTemp);
6207 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6208
6209 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6210
6211 if (pAttachTemp->getControllerName() == aName)
6212 {
6213 rc = detachDevice(pAttachTemp, alock, NULL);
6214 if (FAILED(rc)) return rc;
6215 }
6216 }
6217 }
6218
6219 /* We can remove it now. */
6220 setModified(IsModified_Storage);
6221 mStorageControllers.backup();
6222
6223 ctrl->unshare();
6224
6225 mStorageControllers->remove(ctrl);
6226
6227 /* inform the direct session if any */
6228 alock.release();
6229 onStorageControllerChange();
6230
6231 return S_OK;
6232}
6233
6234STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6235 ULONG *puOriginX,
6236 ULONG *puOriginY,
6237 ULONG *puWidth,
6238 ULONG *puHeight,
6239 BOOL *pfEnabled)
6240{
6241 LogFlowThisFunc(("\n"));
6242
6243 CheckComArgNotNull(puOriginX);
6244 CheckComArgNotNull(puOriginY);
6245 CheckComArgNotNull(puWidth);
6246 CheckComArgNotNull(puHeight);
6247 CheckComArgNotNull(pfEnabled);
6248
6249 uint32_t u32OriginX= 0;
6250 uint32_t u32OriginY= 0;
6251 uint32_t u32Width = 0;
6252 uint32_t u32Height = 0;
6253 uint16_t u16Flags = 0;
6254
6255 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6256 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6257 if (RT_FAILURE(vrc))
6258 {
6259#ifdef RT_OS_WINDOWS
6260 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6261 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6262 * So just assign fEnable to TRUE again.
6263 * The right fix would be to change GUI API wrappers to make sure that parameters
6264 * are changed only if API succeeds.
6265 */
6266 *pfEnabled = TRUE;
6267#endif
6268 return setError(VBOX_E_IPRT_ERROR,
6269 tr("Saved guest size is not available (%Rrc)"),
6270 vrc);
6271 }
6272
6273 *puOriginX = u32OriginX;
6274 *puOriginY = u32OriginY;
6275 *puWidth = u32Width;
6276 *puHeight = u32Height;
6277 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6278
6279 return S_OK;
6280}
6281
6282STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6283{
6284 LogFlowThisFunc(("\n"));
6285
6286 CheckComArgNotNull(aSize);
6287 CheckComArgNotNull(aWidth);
6288 CheckComArgNotNull(aHeight);
6289
6290 if (aScreenId != 0)
6291 return E_NOTIMPL;
6292
6293 AutoCaller autoCaller(this);
6294 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6295
6296 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6297
6298 uint8_t *pu8Data = NULL;
6299 uint32_t cbData = 0;
6300 uint32_t u32Width = 0;
6301 uint32_t u32Height = 0;
6302
6303 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6304
6305 if (RT_FAILURE(vrc))
6306 return setError(VBOX_E_IPRT_ERROR,
6307 tr("Saved screenshot data is not available (%Rrc)"),
6308 vrc);
6309
6310 *aSize = cbData;
6311 *aWidth = u32Width;
6312 *aHeight = u32Height;
6313
6314 freeSavedDisplayScreenshot(pu8Data);
6315
6316 return S_OK;
6317}
6318
6319STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6320{
6321 LogFlowThisFunc(("\n"));
6322
6323 CheckComArgNotNull(aWidth);
6324 CheckComArgNotNull(aHeight);
6325 CheckComArgOutSafeArrayPointerValid(aData);
6326
6327 if (aScreenId != 0)
6328 return E_NOTIMPL;
6329
6330 AutoCaller autoCaller(this);
6331 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6332
6333 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6334
6335 uint8_t *pu8Data = NULL;
6336 uint32_t cbData = 0;
6337 uint32_t u32Width = 0;
6338 uint32_t u32Height = 0;
6339
6340 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6341
6342 if (RT_FAILURE(vrc))
6343 return setError(VBOX_E_IPRT_ERROR,
6344 tr("Saved screenshot data is not available (%Rrc)"),
6345 vrc);
6346
6347 *aWidth = u32Width;
6348 *aHeight = u32Height;
6349
6350 com::SafeArray<BYTE> bitmap(cbData);
6351 /* Convert pixels to format expected by the API caller. */
6352 if (aBGR)
6353 {
6354 /* [0] B, [1] G, [2] R, [3] A. */
6355 for (unsigned i = 0; i < cbData; i += 4)
6356 {
6357 bitmap[i] = pu8Data[i];
6358 bitmap[i + 1] = pu8Data[i + 1];
6359 bitmap[i + 2] = pu8Data[i + 2];
6360 bitmap[i + 3] = 0xff;
6361 }
6362 }
6363 else
6364 {
6365 /* [0] R, [1] G, [2] B, [3] A. */
6366 for (unsigned i = 0; i < cbData; i += 4)
6367 {
6368 bitmap[i] = pu8Data[i + 2];
6369 bitmap[i + 1] = pu8Data[i + 1];
6370 bitmap[i + 2] = pu8Data[i];
6371 bitmap[i + 3] = 0xff;
6372 }
6373 }
6374 bitmap.detachTo(ComSafeArrayOutArg(aData));
6375
6376 freeSavedDisplayScreenshot(pu8Data);
6377
6378 return S_OK;
6379}
6380
6381
6382STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6383{
6384 LogFlowThisFunc(("\n"));
6385
6386 CheckComArgNotNull(aWidth);
6387 CheckComArgNotNull(aHeight);
6388 CheckComArgOutSafeArrayPointerValid(aData);
6389
6390 if (aScreenId != 0)
6391 return E_NOTIMPL;
6392
6393 AutoCaller autoCaller(this);
6394 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6395
6396 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6397
6398 uint8_t *pu8Data = NULL;
6399 uint32_t cbData = 0;
6400 uint32_t u32Width = 0;
6401 uint32_t u32Height = 0;
6402
6403 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6404
6405 if (RT_FAILURE(vrc))
6406 return setError(VBOX_E_IPRT_ERROR,
6407 tr("Saved screenshot data is not available (%Rrc)"),
6408 vrc);
6409
6410 *aWidth = u32Width;
6411 *aHeight = u32Height;
6412
6413 HRESULT rc = S_OK;
6414 uint8_t *pu8PNG = NULL;
6415 uint32_t cbPNG = 0;
6416 uint32_t cxPNG = 0;
6417 uint32_t cyPNG = 0;
6418
6419 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6420
6421 if (RT_SUCCESS(vrc))
6422 {
6423 com::SafeArray<BYTE> screenData(cbPNG);
6424 screenData.initFrom(pu8PNG, cbPNG);
6425 if (pu8PNG)
6426 RTMemFree(pu8PNG);
6427 screenData.detachTo(ComSafeArrayOutArg(aData));
6428 }
6429 else
6430 {
6431 if (pu8PNG)
6432 RTMemFree(pu8PNG);
6433 return setError(VBOX_E_IPRT_ERROR,
6434 tr("Could not convert screenshot to PNG (%Rrc)"),
6435 vrc);
6436 }
6437
6438 freeSavedDisplayScreenshot(pu8Data);
6439
6440 return rc;
6441}
6442
6443STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6444{
6445 LogFlowThisFunc(("\n"));
6446
6447 CheckComArgNotNull(aSize);
6448 CheckComArgNotNull(aWidth);
6449 CheckComArgNotNull(aHeight);
6450
6451 if (aScreenId != 0)
6452 return E_NOTIMPL;
6453
6454 AutoCaller autoCaller(this);
6455 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6456
6457 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6458
6459 uint8_t *pu8Data = NULL;
6460 uint32_t cbData = 0;
6461 uint32_t u32Width = 0;
6462 uint32_t u32Height = 0;
6463
6464 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6465
6466 if (RT_FAILURE(vrc))
6467 return setError(VBOX_E_IPRT_ERROR,
6468 tr("Saved screenshot data is not available (%Rrc)"),
6469 vrc);
6470
6471 *aSize = cbData;
6472 *aWidth = u32Width;
6473 *aHeight = u32Height;
6474
6475 freeSavedDisplayScreenshot(pu8Data);
6476
6477 return S_OK;
6478}
6479
6480STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6481{
6482 LogFlowThisFunc(("\n"));
6483
6484 CheckComArgNotNull(aWidth);
6485 CheckComArgNotNull(aHeight);
6486 CheckComArgOutSafeArrayPointerValid(aData);
6487
6488 if (aScreenId != 0)
6489 return E_NOTIMPL;
6490
6491 AutoCaller autoCaller(this);
6492 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6493
6494 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6495
6496 uint8_t *pu8Data = NULL;
6497 uint32_t cbData = 0;
6498 uint32_t u32Width = 0;
6499 uint32_t u32Height = 0;
6500
6501 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6502
6503 if (RT_FAILURE(vrc))
6504 return setError(VBOX_E_IPRT_ERROR,
6505 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6506 vrc);
6507
6508 *aWidth = u32Width;
6509 *aHeight = u32Height;
6510
6511 com::SafeArray<BYTE> png(cbData);
6512 png.initFrom(pu8Data, cbData);
6513 png.detachTo(ComSafeArrayOutArg(aData));
6514
6515 freeSavedDisplayScreenshot(pu8Data);
6516
6517 return S_OK;
6518}
6519
6520STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6521{
6522 HRESULT rc = S_OK;
6523 LogFlowThisFunc(("\n"));
6524
6525 AutoCaller autoCaller(this);
6526 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6527
6528 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6529
6530 if (!mHWData->mCPUHotPlugEnabled)
6531 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6532
6533 if (aCpu >= mHWData->mCPUCount)
6534 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6535
6536 if (mHWData->mCPUAttached[aCpu])
6537 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6538
6539 alock.release();
6540 rc = onCPUChange(aCpu, false);
6541 alock.acquire();
6542 if (FAILED(rc)) return rc;
6543
6544 setModified(IsModified_MachineData);
6545 mHWData.backup();
6546 mHWData->mCPUAttached[aCpu] = true;
6547
6548 /* Save settings if online */
6549 if (Global::IsOnline(mData->mMachineState))
6550 saveSettings(NULL);
6551
6552 return S_OK;
6553}
6554
6555STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6556{
6557 HRESULT rc = S_OK;
6558 LogFlowThisFunc(("\n"));
6559
6560 AutoCaller autoCaller(this);
6561 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6562
6563 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6564
6565 if (!mHWData->mCPUHotPlugEnabled)
6566 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6567
6568 if (aCpu >= SchemaDefs::MaxCPUCount)
6569 return setError(E_INVALIDARG,
6570 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6571 SchemaDefs::MaxCPUCount);
6572
6573 if (!mHWData->mCPUAttached[aCpu])
6574 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6575
6576 /* CPU 0 can't be detached */
6577 if (aCpu == 0)
6578 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6579
6580 alock.release();
6581 rc = onCPUChange(aCpu, true);
6582 alock.acquire();
6583 if (FAILED(rc)) return rc;
6584
6585 setModified(IsModified_MachineData);
6586 mHWData.backup();
6587 mHWData->mCPUAttached[aCpu] = false;
6588
6589 /* Save settings if online */
6590 if (Global::IsOnline(mData->mMachineState))
6591 saveSettings(NULL);
6592
6593 return S_OK;
6594}
6595
6596STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6597{
6598 LogFlowThisFunc(("\n"));
6599
6600 CheckComArgNotNull(aCpuAttached);
6601
6602 *aCpuAttached = false;
6603
6604 AutoCaller autoCaller(this);
6605 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6606
6607 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6608
6609 /* If hotplug is enabled the CPU is always enabled. */
6610 if (!mHWData->mCPUHotPlugEnabled)
6611 {
6612 if (aCpu < mHWData->mCPUCount)
6613 *aCpuAttached = true;
6614 }
6615 else
6616 {
6617 if (aCpu < SchemaDefs::MaxCPUCount)
6618 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6619 }
6620
6621 return S_OK;
6622}
6623
6624STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6625{
6626 CheckComArgOutPointerValid(aName);
6627
6628 AutoCaller autoCaller(this);
6629 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6630
6631 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6632
6633 Utf8Str log = queryLogFilename(aIdx);
6634 if (!RTFileExists(log.c_str()))
6635 log.setNull();
6636 log.cloneTo(aName);
6637
6638 return S_OK;
6639}
6640
6641STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6642{
6643 LogFlowThisFunc(("\n"));
6644 CheckComArgOutSafeArrayPointerValid(aData);
6645 if (aSize < 0)
6646 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6647
6648 AutoCaller autoCaller(this);
6649 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6650
6651 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6652
6653 HRESULT rc = S_OK;
6654 Utf8Str log = queryLogFilename(aIdx);
6655
6656 /* do not unnecessarily hold the lock while doing something which does
6657 * not need the lock and potentially takes a long time. */
6658 alock.release();
6659
6660 /* Limit the chunk size to 32K for now, as that gives better performance
6661 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6662 * One byte expands to approx. 25 bytes of breathtaking XML. */
6663 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6664 com::SafeArray<BYTE> logData(cbData);
6665
6666 RTFILE LogFile;
6667 int vrc = RTFileOpen(&LogFile, log.c_str(),
6668 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6669 if (RT_SUCCESS(vrc))
6670 {
6671 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6672 if (RT_SUCCESS(vrc))
6673 logData.resize(cbData);
6674 else
6675 rc = setError(VBOX_E_IPRT_ERROR,
6676 tr("Could not read log file '%s' (%Rrc)"),
6677 log.c_str(), vrc);
6678 RTFileClose(LogFile);
6679 }
6680 else
6681 rc = setError(VBOX_E_IPRT_ERROR,
6682 tr("Could not open log file '%s' (%Rrc)"),
6683 log.c_str(), vrc);
6684
6685 if (FAILED(rc))
6686 logData.resize(0);
6687 logData.detachTo(ComSafeArrayOutArg(aData));
6688
6689 return rc;
6690}
6691
6692
6693/**
6694 * Currently this method doesn't attach device to the running VM,
6695 * just makes sure it's plugged on next VM start.
6696 */
6697STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6698{
6699 AutoCaller autoCaller(this);
6700 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6701
6702 // lock scope
6703 {
6704 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6705
6706 HRESULT rc = checkStateDependency(MutableStateDep);
6707 if (FAILED(rc)) return rc;
6708
6709 ChipsetType_T aChipset = ChipsetType_PIIX3;
6710 COMGETTER(ChipsetType)(&aChipset);
6711
6712 if (aChipset != ChipsetType_ICH9)
6713 {
6714 return setError(E_INVALIDARG,
6715 tr("Host PCI attachment only supported with ICH9 chipset"));
6716 }
6717
6718 // check if device with this host PCI address already attached
6719 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6720 it != mHWData->mPCIDeviceAssignments.end();
6721 ++it)
6722 {
6723 LONG iHostAddress = -1;
6724 ComPtr<PCIDeviceAttachment> pAttach;
6725 pAttach = *it;
6726 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6727 if (iHostAddress == hostAddress)
6728 return setError(E_INVALIDARG,
6729 tr("Device with host PCI address already attached to this VM"));
6730 }
6731
6732 ComObjPtr<PCIDeviceAttachment> pda;
6733 char name[32];
6734
6735 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6736 Bstr bname(name);
6737 pda.createObject();
6738 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
6739 setModified(IsModified_MachineData);
6740 mHWData.backup();
6741 mHWData->mPCIDeviceAssignments.push_back(pda);
6742 }
6743
6744 return S_OK;
6745}
6746
6747/**
6748 * Currently this method doesn't detach device from the running VM,
6749 * just makes sure it's not plugged on next VM start.
6750 */
6751STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
6752{
6753 AutoCaller autoCaller(this);
6754 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6755
6756 ComObjPtr<PCIDeviceAttachment> pAttach;
6757 bool fRemoved = false;
6758 HRESULT rc;
6759
6760 // lock scope
6761 {
6762 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6763
6764 rc = checkStateDependency(MutableStateDep);
6765 if (FAILED(rc)) return rc;
6766
6767 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6768 it != mHWData->mPCIDeviceAssignments.end();
6769 ++it)
6770 {
6771 LONG iHostAddress = -1;
6772 pAttach = *it;
6773 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6774 if (iHostAddress != -1 && iHostAddress == hostAddress)
6775 {
6776 setModified(IsModified_MachineData);
6777 mHWData.backup();
6778 mHWData->mPCIDeviceAssignments.remove(pAttach);
6779 fRemoved = true;
6780 break;
6781 }
6782 }
6783 }
6784
6785
6786 /* Fire event outside of the lock */
6787 if (fRemoved)
6788 {
6789 Assert(!pAttach.isNull());
6790 ComPtr<IEventSource> es;
6791 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6792 Assert(SUCCEEDED(rc));
6793 Bstr mid;
6794 rc = this->COMGETTER(Id)(mid.asOutParam());
6795 Assert(SUCCEEDED(rc));
6796 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6797 }
6798
6799 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6800 tr("No host PCI device %08x attached"),
6801 hostAddress
6802 );
6803}
6804
6805STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
6806{
6807 CheckComArgOutSafeArrayPointerValid(aAssignments);
6808
6809 AutoCaller autoCaller(this);
6810 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6811
6812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6813
6814 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
6815 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
6816
6817 return S_OK;
6818}
6819
6820STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
6821{
6822 CheckComArgOutPointerValid(aBandwidthControl);
6823
6824 AutoCaller autoCaller(this);
6825 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6826
6827 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
6828
6829 return S_OK;
6830}
6831
6832STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
6833{
6834 CheckComArgOutPointerValid(pfEnabled);
6835 AutoCaller autoCaller(this);
6836 HRESULT hrc = autoCaller.rc();
6837 if (SUCCEEDED(hrc))
6838 {
6839 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6840 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
6841 }
6842 return hrc;
6843}
6844
6845STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
6846{
6847 AutoCaller autoCaller(this);
6848 HRESULT hrc = autoCaller.rc();
6849 if (SUCCEEDED(hrc))
6850 {
6851 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6852 hrc = checkStateDependency(MutableStateDep);
6853 if (SUCCEEDED(hrc))
6854 {
6855 hrc = mHWData.backupEx();
6856 if (SUCCEEDED(hrc))
6857 {
6858 setModified(IsModified_MachineData);
6859 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
6860 }
6861 }
6862 }
6863 return hrc;
6864}
6865
6866STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
6867{
6868 CheckComArgOutPointerValid(pbstrConfig);
6869 AutoCaller autoCaller(this);
6870 HRESULT hrc = autoCaller.rc();
6871 if (SUCCEEDED(hrc))
6872 {
6873 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6874 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
6875 }
6876 return hrc;
6877}
6878
6879STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
6880{
6881 CheckComArgStr(bstrConfig);
6882 AutoCaller autoCaller(this);
6883 HRESULT hrc = autoCaller.rc();
6884 if (SUCCEEDED(hrc))
6885 {
6886 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6887 hrc = checkStateDependency(MutableStateDep);
6888 if (SUCCEEDED(hrc))
6889 {
6890 hrc = mHWData.backupEx();
6891 if (SUCCEEDED(hrc))
6892 {
6893 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
6894 if (SUCCEEDED(hrc))
6895 setModified(IsModified_MachineData);
6896 }
6897 }
6898 }
6899 return hrc;
6900
6901}
6902
6903STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
6904{
6905 CheckComArgOutPointerValid(pfAllow);
6906 AutoCaller autoCaller(this);
6907 HRESULT hrc = autoCaller.rc();
6908 if (SUCCEEDED(hrc))
6909 {
6910 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6911 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
6912 }
6913 return hrc;
6914}
6915
6916STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
6917{
6918 AutoCaller autoCaller(this);
6919 HRESULT hrc = autoCaller.rc();
6920 if (SUCCEEDED(hrc))
6921 {
6922 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6923 hrc = checkStateDependency(MutableStateDep);
6924 if (SUCCEEDED(hrc))
6925 {
6926 hrc = mHWData.backupEx();
6927 if (SUCCEEDED(hrc))
6928 {
6929 setModified(IsModified_MachineData);
6930 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
6931 }
6932 }
6933 }
6934 return hrc;
6935}
6936
6937STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
6938{
6939 CheckComArgOutPointerValid(pfEnabled);
6940 AutoCaller autoCaller(this);
6941 HRESULT hrc = autoCaller.rc();
6942 if (SUCCEEDED(hrc))
6943 {
6944 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6945 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
6946 }
6947 return hrc;
6948}
6949
6950STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
6951{
6952 AutoCaller autoCaller(this);
6953 HRESULT hrc = autoCaller.rc();
6954 if (SUCCEEDED(hrc))
6955 {
6956 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6957 hrc = checkStateDependency(MutableStateDep);
6958 if ( SUCCEEDED(hrc)
6959 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
6960 {
6961 AutostartDb *autostartDb = mParent->getAutostartDb();
6962 int vrc;
6963
6964 if (fEnabled)
6965 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6966 else
6967 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6968
6969 if (RT_SUCCESS(vrc))
6970 {
6971 hrc = mHWData.backupEx();
6972 if (SUCCEEDED(hrc))
6973 {
6974 setModified(IsModified_MachineData);
6975 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
6976 }
6977 }
6978 else if (vrc == VERR_NOT_SUPPORTED)
6979 hrc = setError(VBOX_E_NOT_SUPPORTED,
6980 tr("The VM autostart feature is not supported on this platform"));
6981 else if (vrc == VERR_PATH_NOT_FOUND)
6982 hrc = setError(E_FAIL,
6983 tr("The path to the autostart database is not set"));
6984 else
6985 hrc = setError(E_UNEXPECTED,
6986 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6987 fEnabled ? "Adding" : "Removing",
6988 mUserData->s.strName.c_str(), vrc);
6989 }
6990 }
6991 return hrc;
6992}
6993
6994STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
6995{
6996 CheckComArgOutPointerValid(puDelay);
6997 AutoCaller autoCaller(this);
6998 HRESULT hrc = autoCaller.rc();
6999 if (SUCCEEDED(hrc))
7000 {
7001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7002 *puDelay = mHWData->mAutostart.uAutostartDelay;
7003 }
7004 return hrc;
7005}
7006
7007STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
7008{
7009 AutoCaller autoCaller(this);
7010 HRESULT hrc = autoCaller.rc();
7011 if (SUCCEEDED(hrc))
7012 {
7013 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7014 hrc = checkStateDependency(MutableStateDep);
7015 if (SUCCEEDED(hrc))
7016 {
7017 hrc = mHWData.backupEx();
7018 if (SUCCEEDED(hrc))
7019 {
7020 setModified(IsModified_MachineData);
7021 mHWData->mAutostart.uAutostartDelay = uDelay;
7022 }
7023 }
7024 }
7025 return hrc;
7026}
7027
7028STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
7029{
7030 CheckComArgOutPointerValid(penmAutostopType);
7031 AutoCaller autoCaller(this);
7032 HRESULT hrc = autoCaller.rc();
7033 if (SUCCEEDED(hrc))
7034 {
7035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7036 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
7037 }
7038 return hrc;
7039}
7040
7041STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
7042{
7043 AutoCaller autoCaller(this);
7044 HRESULT hrc = autoCaller.rc();
7045 if (SUCCEEDED(hrc))
7046 {
7047 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7048 hrc = checkStateDependency(MutableStateDep);
7049 if ( SUCCEEDED(hrc)
7050 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
7051 {
7052 AutostartDb *autostartDb = mParent->getAutostartDb();
7053 int vrc;
7054
7055 if (enmAutostopType != AutostopType_Disabled)
7056 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7057 else
7058 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7059
7060 if (RT_SUCCESS(vrc))
7061 {
7062 hrc = mHWData.backupEx();
7063 if (SUCCEEDED(hrc))
7064 {
7065 setModified(IsModified_MachineData);
7066 mHWData->mAutostart.enmAutostopType = enmAutostopType;
7067 }
7068 }
7069 else if (vrc == VERR_NOT_SUPPORTED)
7070 hrc = setError(VBOX_E_NOT_SUPPORTED,
7071 tr("The VM autostop feature is not supported on this platform"));
7072 else if (vrc == VERR_PATH_NOT_FOUND)
7073 hrc = setError(E_FAIL,
7074 tr("The path to the autostart database is not set"));
7075 else
7076 hrc = setError(E_UNEXPECTED,
7077 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7078 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7079 mUserData->s.strName.c_str(), vrc);
7080 }
7081 }
7082 return hrc;
7083}
7084
7085STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7086{
7087 CheckComArgOutPointerValid(aDefaultFrontend);
7088 AutoCaller autoCaller(this);
7089 HRESULT hrc = autoCaller.rc();
7090 if (SUCCEEDED(hrc))
7091 {
7092 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7093 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7094 }
7095 return hrc;
7096}
7097
7098STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7099{
7100 CheckComArgStr(aDefaultFrontend);
7101 AutoCaller autoCaller(this);
7102 HRESULT hrc = autoCaller.rc();
7103 if (SUCCEEDED(hrc))
7104 {
7105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7106 hrc = checkStateDependency(MutableOrSavedStateDep);
7107 if (SUCCEEDED(hrc))
7108 {
7109 hrc = mHWData.backupEx();
7110 if (SUCCEEDED(hrc))
7111 {
7112 setModified(IsModified_MachineData);
7113 mHWData->mDefaultFrontend = aDefaultFrontend;
7114 }
7115 }
7116 }
7117 return hrc;
7118}
7119
7120
7121STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7122{
7123 LogFlowFuncEnter();
7124
7125 CheckComArgNotNull(pTarget);
7126 CheckComArgOutPointerValid(pProgress);
7127
7128 /* Convert the options. */
7129 RTCList<CloneOptions_T> optList;
7130 if (options != NULL)
7131 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7132
7133 if (optList.contains(CloneOptions_Link))
7134 {
7135 if (!isSnapshotMachine())
7136 return setError(E_INVALIDARG,
7137 tr("Linked clone can only be created from a snapshot"));
7138 if (mode != CloneMode_MachineState)
7139 return setError(E_INVALIDARG,
7140 tr("Linked clone can only be created for a single machine state"));
7141 }
7142 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7143
7144 AutoCaller autoCaller(this);
7145 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7146
7147
7148 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7149
7150 HRESULT rc = pWorker->start(pProgress);
7151
7152 LogFlowFuncLeave();
7153
7154 return rc;
7155}
7156
7157// public methods for internal purposes
7158/////////////////////////////////////////////////////////////////////////////
7159
7160/**
7161 * Adds the given IsModified_* flag to the dirty flags of the machine.
7162 * This must be called either during loadSettings or under the machine write lock.
7163 * @param fl
7164 */
7165void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7166{
7167 mData->flModifications |= fl;
7168 if (fAllowStateModification && isStateModificationAllowed())
7169 mData->mCurrentStateModified = true;
7170}
7171
7172/**
7173 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7174 * care of the write locking.
7175 *
7176 * @param fModifications The flag to add.
7177 */
7178void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7179{
7180 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7181 setModified(fModification, fAllowStateModification);
7182}
7183
7184/**
7185 * Saves the registry entry of this machine to the given configuration node.
7186 *
7187 * @param aEntryNode Node to save the registry entry to.
7188 *
7189 * @note locks this object for reading.
7190 */
7191HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7192{
7193 AutoLimitedCaller autoCaller(this);
7194 AssertComRCReturnRC(autoCaller.rc());
7195
7196 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7197
7198 data.uuid = mData->mUuid;
7199 data.strSettingsFile = mData->m_strConfigFile;
7200
7201 return S_OK;
7202}
7203
7204/**
7205 * Calculates the absolute path of the given path taking the directory of the
7206 * machine settings file as the current directory.
7207 *
7208 * @param aPath Path to calculate the absolute path for.
7209 * @param aResult Where to put the result (used only on success, can be the
7210 * same Utf8Str instance as passed in @a aPath).
7211 * @return IPRT result.
7212 *
7213 * @note Locks this object for reading.
7214 */
7215int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7216{
7217 AutoCaller autoCaller(this);
7218 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7219
7220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7221
7222 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7223
7224 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7225
7226 strSettingsDir.stripFilename();
7227 char folder[RTPATH_MAX];
7228 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7229 if (RT_SUCCESS(vrc))
7230 aResult = folder;
7231
7232 return vrc;
7233}
7234
7235/**
7236 * Copies strSource to strTarget, making it relative to the machine folder
7237 * if it is a subdirectory thereof, or simply copying it otherwise.
7238 *
7239 * @param strSource Path to evaluate and copy.
7240 * @param strTarget Buffer to receive target path.
7241 *
7242 * @note Locks this object for reading.
7243 */
7244void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7245 Utf8Str &strTarget)
7246{
7247 AutoCaller autoCaller(this);
7248 AssertComRCReturn(autoCaller.rc(), (void)0);
7249
7250 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7251
7252 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7253 // use strTarget as a temporary buffer to hold the machine settings dir
7254 strTarget = mData->m_strConfigFileFull;
7255 strTarget.stripFilename();
7256 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7257 {
7258 // is relative: then append what's left
7259 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7260 // for empty paths (only possible for subdirs) use "." to avoid
7261 // triggering default settings for not present config attributes.
7262 if (strTarget.isEmpty())
7263 strTarget = ".";
7264 }
7265 else
7266 // is not relative: then overwrite
7267 strTarget = strSource;
7268}
7269
7270/**
7271 * Returns the full path to the machine's log folder in the
7272 * \a aLogFolder argument.
7273 */
7274void Machine::getLogFolder(Utf8Str &aLogFolder)
7275{
7276 AutoCaller autoCaller(this);
7277 AssertComRCReturnVoid(autoCaller.rc());
7278
7279 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7280
7281 char szTmp[RTPATH_MAX];
7282 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7283 if (RT_SUCCESS(vrc))
7284 {
7285 if (szTmp[0] && !mUserData.isNull())
7286 {
7287 char szTmp2[RTPATH_MAX];
7288 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7289 if (RT_SUCCESS(vrc))
7290 aLogFolder = BstrFmt("%s%c%s",
7291 szTmp2,
7292 RTPATH_DELIMITER,
7293 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7294 }
7295 else
7296 vrc = VERR_PATH_IS_RELATIVE;
7297 }
7298
7299 if (RT_FAILURE(vrc))
7300 {
7301 // fallback if VBOX_USER_LOGHOME is not set or invalid
7302 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7303 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7304 aLogFolder.append(RTPATH_DELIMITER);
7305 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7306 }
7307}
7308
7309/**
7310 * Returns the full path to the machine's log file for an given index.
7311 */
7312Utf8Str Machine::queryLogFilename(ULONG idx)
7313{
7314 Utf8Str logFolder;
7315 getLogFolder(logFolder);
7316 Assert(logFolder.length());
7317 Utf8Str log;
7318 if (idx == 0)
7319 log = Utf8StrFmt("%s%cVBox.log",
7320 logFolder.c_str(), RTPATH_DELIMITER);
7321 else
7322 log = Utf8StrFmt("%s%cVBox.log.%d",
7323 logFolder.c_str(), RTPATH_DELIMITER, idx);
7324 return log;
7325}
7326
7327/**
7328 * Composes a unique saved state filename based on the current system time. The filename is
7329 * granular to the second so this will work so long as no more than one snapshot is taken on
7330 * a machine per second.
7331 *
7332 * Before version 4.1, we used this formula for saved state files:
7333 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7334 * which no longer works because saved state files can now be shared between the saved state of the
7335 * "saved" machine and an online snapshot, and the following would cause problems:
7336 * 1) save machine
7337 * 2) create online snapshot from that machine state --> reusing saved state file
7338 * 3) save machine again --> filename would be reused, breaking the online snapshot
7339 *
7340 * So instead we now use a timestamp.
7341 *
7342 * @param str
7343 */
7344void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7345{
7346 AutoCaller autoCaller(this);
7347 AssertComRCReturnVoid(autoCaller.rc());
7348
7349 {
7350 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7351 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7352 }
7353
7354 RTTIMESPEC ts;
7355 RTTimeNow(&ts);
7356 RTTIME time;
7357 RTTimeExplode(&time, &ts);
7358
7359 strStateFilePath += RTPATH_DELIMITER;
7360 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7361 time.i32Year, time.u8Month, time.u8MonthDay,
7362 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7363}
7364
7365/**
7366 * @note Locks this object for writing, calls the client process
7367 * (inside the lock).
7368 */
7369HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7370 const Utf8Str &strFrontend,
7371 const Utf8Str &strEnvironment,
7372 ProgressProxy *aProgress)
7373{
7374 LogFlowThisFuncEnter();
7375
7376 AssertReturn(aControl, E_FAIL);
7377 AssertReturn(aProgress, E_FAIL);
7378
7379 AutoCaller autoCaller(this);
7380 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7381
7382 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7383
7384 if (!mData->mRegistered)
7385 return setError(E_UNEXPECTED,
7386 tr("The machine '%s' is not registered"),
7387 mUserData->s.strName.c_str());
7388
7389 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7390
7391 if ( mData->mSession.mState == SessionState_Locked
7392 || mData->mSession.mState == SessionState_Spawning
7393 || mData->mSession.mState == SessionState_Unlocking)
7394 return setError(VBOX_E_INVALID_OBJECT_STATE,
7395 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7396 mUserData->s.strName.c_str());
7397
7398 /* may not be busy */
7399 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7400
7401 /* get the path to the executable */
7402 char szPath[RTPATH_MAX];
7403 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7404 size_t sz = strlen(szPath);
7405 szPath[sz++] = RTPATH_DELIMITER;
7406 szPath[sz] = 0;
7407 char *cmd = szPath + sz;
7408 sz = RTPATH_MAX - sz;
7409
7410 int vrc = VINF_SUCCESS;
7411 RTPROCESS pid = NIL_RTPROCESS;
7412
7413 RTENV env = RTENV_DEFAULT;
7414
7415 if (!strEnvironment.isEmpty())
7416 {
7417 char *newEnvStr = NULL;
7418
7419 do
7420 {
7421 /* clone the current environment */
7422 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7423 AssertRCBreakStmt(vrc2, vrc = vrc2);
7424
7425 newEnvStr = RTStrDup(strEnvironment.c_str());
7426 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7427
7428 /* put new variables to the environment
7429 * (ignore empty variable names here since RTEnv API
7430 * intentionally doesn't do that) */
7431 char *var = newEnvStr;
7432 for (char *p = newEnvStr; *p; ++p)
7433 {
7434 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7435 {
7436 *p = '\0';
7437 if (*var)
7438 {
7439 char *val = strchr(var, '=');
7440 if (val)
7441 {
7442 *val++ = '\0';
7443 vrc2 = RTEnvSetEx(env, var, val);
7444 }
7445 else
7446 vrc2 = RTEnvUnsetEx(env, var);
7447 if (RT_FAILURE(vrc2))
7448 break;
7449 }
7450 var = p + 1;
7451 }
7452 }
7453 if (RT_SUCCESS(vrc2) && *var)
7454 vrc2 = RTEnvPutEx(env, var);
7455
7456 AssertRCBreakStmt(vrc2, vrc = vrc2);
7457 }
7458 while (0);
7459
7460 if (newEnvStr != NULL)
7461 RTStrFree(newEnvStr);
7462 }
7463
7464 /* Qt is default */
7465#ifdef VBOX_WITH_QTGUI
7466 if (strFrontend == "gui" || strFrontend == "GUI/Qt" || strFrontend == "")
7467 {
7468# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7469 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7470# else
7471 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7472# endif
7473 Assert(sz >= sizeof(VirtualBox_exe));
7474 strcpy(cmd, VirtualBox_exe);
7475
7476 Utf8Str idStr = mData->mUuid.toString();
7477 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7478 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7479 }
7480#else /* !VBOX_WITH_QTGUI */
7481 if (0)
7482 ;
7483#endif /* VBOX_WITH_QTGUI */
7484
7485 else
7486
7487#ifdef VBOX_WITH_VBOXSDL
7488 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7489 {
7490 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7491 Assert(sz >= sizeof(VBoxSDL_exe));
7492 strcpy(cmd, VBoxSDL_exe);
7493
7494 Utf8Str idStr = mData->mUuid.toString();
7495 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7496 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7497 }
7498#else /* !VBOX_WITH_VBOXSDL */
7499 if (0)
7500 ;
7501#endif /* !VBOX_WITH_VBOXSDL */
7502
7503 else
7504
7505#ifdef VBOX_WITH_HEADLESS
7506 if ( strFrontend == "headless"
7507 || strFrontend == "capture"
7508 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7509 )
7510 {
7511 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7512 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7513 * and a VM works even if the server has not been installed.
7514 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7515 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7516 * differently in 4.0 and 3.x.
7517 */
7518 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7519 Assert(sz >= sizeof(VBoxHeadless_exe));
7520 strcpy(cmd, VBoxHeadless_exe);
7521
7522 Utf8Str idStr = mData->mUuid.toString();
7523 /* Leave space for "--capture" arg. */
7524 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7525 "--startvm", idStr.c_str(),
7526 "--vrde", "config",
7527 0, /* For "--capture". */
7528 0 };
7529 if (strFrontend == "capture")
7530 {
7531 unsigned pos = RT_ELEMENTS(args) - 2;
7532 args[pos] = "--capture";
7533 }
7534 vrc = RTProcCreate(szPath, args, env,
7535#ifdef RT_OS_WINDOWS
7536 RTPROC_FLAGS_NO_WINDOW
7537#else
7538 0
7539#endif
7540 , &pid);
7541 }
7542#else /* !VBOX_WITH_HEADLESS */
7543 if (0)
7544 ;
7545#endif /* !VBOX_WITH_HEADLESS */
7546 else
7547 {
7548 RTEnvDestroy(env);
7549 return setError(E_INVALIDARG,
7550 tr("Invalid frontend name: '%s'"),
7551 strFrontend.c_str());
7552 }
7553
7554 RTEnvDestroy(env);
7555
7556 if (RT_FAILURE(vrc))
7557 return setError(VBOX_E_IPRT_ERROR,
7558 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7559 mUserData->s.strName.c_str(), vrc);
7560
7561 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7562
7563 /*
7564 * Note that we don't release the lock here before calling the client,
7565 * because it doesn't need to call us back if called with a NULL argument.
7566 * Releasing the lock here is dangerous because we didn't prepare the
7567 * launch data yet, but the client we've just started may happen to be
7568 * too fast and call openSession() that will fail (because of PID, etc.),
7569 * so that the Machine will never get out of the Spawning session state.
7570 */
7571
7572 /* inform the session that it will be a remote one */
7573 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7574 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
7575 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7576
7577 if (FAILED(rc))
7578 {
7579 /* restore the session state */
7580 mData->mSession.mState = SessionState_Unlocked;
7581 /* The failure may occur w/o any error info (from RPC), so provide one */
7582 return setError(VBOX_E_VM_ERROR,
7583 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7584 }
7585
7586 /* attach launch data to the machine */
7587 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7588 mData->mSession.mRemoteControls.push_back(aControl);
7589 mData->mSession.mProgress = aProgress;
7590 mData->mSession.mPID = pid;
7591 mData->mSession.mState = SessionState_Spawning;
7592 mData->mSession.mType = strFrontend;
7593
7594 LogFlowThisFuncLeave();
7595 return S_OK;
7596}
7597
7598/**
7599 * Returns @c true if the given machine has an open direct session and returns
7600 * the session machine instance and additional session data (on some platforms)
7601 * if so.
7602 *
7603 * Note that when the method returns @c false, the arguments remain unchanged.
7604 *
7605 * @param aMachine Session machine object.
7606 * @param aControl Direct session control object (optional).
7607 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7608 *
7609 * @note locks this object for reading.
7610 */
7611#if defined(RT_OS_WINDOWS)
7612bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7613 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7614 HANDLE *aIPCSem /*= NULL*/,
7615 bool aAllowClosing /*= false*/)
7616#elif defined(RT_OS_OS2)
7617bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7618 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7619 HMTX *aIPCSem /*= NULL*/,
7620 bool aAllowClosing /*= false*/)
7621#else
7622bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7623 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7624 bool aAllowClosing /*= false*/)
7625#endif
7626{
7627 AutoLimitedCaller autoCaller(this);
7628 AssertComRCReturn(autoCaller.rc(), false);
7629
7630 /* just return false for inaccessible machines */
7631 if (autoCaller.state() != Ready)
7632 return false;
7633
7634 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7635
7636 if ( mData->mSession.mState == SessionState_Locked
7637 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7638 )
7639 {
7640 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7641
7642 aMachine = mData->mSession.mMachine;
7643
7644 if (aControl != NULL)
7645 *aControl = mData->mSession.mDirectControl;
7646
7647#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7648 /* Additional session data */
7649 if (aIPCSem != NULL)
7650 *aIPCSem = aMachine->mIPCSem;
7651#endif
7652 return true;
7653 }
7654
7655 return false;
7656}
7657
7658/**
7659 * Returns @c true if the given machine has an spawning direct session and
7660 * returns and additional session data (on some platforms) if so.
7661 *
7662 * Note that when the method returns @c false, the arguments remain unchanged.
7663 *
7664 * @param aPID PID of the spawned direct session process.
7665 *
7666 * @note locks this object for reading.
7667 */
7668#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7669bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7670#else
7671bool Machine::isSessionSpawning()
7672#endif
7673{
7674 AutoLimitedCaller autoCaller(this);
7675 AssertComRCReturn(autoCaller.rc(), false);
7676
7677 /* just return false for inaccessible machines */
7678 if (autoCaller.state() != Ready)
7679 return false;
7680
7681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7682
7683 if (mData->mSession.mState == SessionState_Spawning)
7684 {
7685#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7686 /* Additional session data */
7687 if (aPID != NULL)
7688 {
7689 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
7690 *aPID = mData->mSession.mPID;
7691 }
7692#endif
7693 return true;
7694 }
7695
7696 return false;
7697}
7698
7699/**
7700 * Called from the client watcher thread to check for unexpected client process
7701 * death during Session_Spawning state (e.g. before it successfully opened a
7702 * direct session).
7703 *
7704 * On Win32 and on OS/2, this method is called only when we've got the
7705 * direct client's process termination notification, so it always returns @c
7706 * true.
7707 *
7708 * On other platforms, this method returns @c true if the client process is
7709 * terminated and @c false if it's still alive.
7710 *
7711 * @note Locks this object for writing.
7712 */
7713bool Machine::checkForSpawnFailure()
7714{
7715 AutoCaller autoCaller(this);
7716 if (!autoCaller.isOk())
7717 {
7718 /* nothing to do */
7719 LogFlowThisFunc(("Already uninitialized!\n"));
7720 return true;
7721 }
7722
7723 /* VirtualBox::addProcessToReap() needs a write lock */
7724 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
7725
7726 if (mData->mSession.mState != SessionState_Spawning)
7727 {
7728 /* nothing to do */
7729 LogFlowThisFunc(("Not spawning any more!\n"));
7730 return true;
7731 }
7732
7733 HRESULT rc = S_OK;
7734
7735#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7736
7737 /* the process was already unexpectedly terminated, we just need to set an
7738 * error and finalize session spawning */
7739 rc = setError(E_FAIL,
7740 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
7741 getName().c_str());
7742#else
7743
7744 /* PID not yet initialized, skip check. */
7745 if (mData->mSession.mPID == NIL_RTPROCESS)
7746 return false;
7747
7748 RTPROCSTATUS status;
7749 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
7750 &status);
7751
7752 if (vrc != VERR_PROCESS_RUNNING)
7753 {
7754 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7755 rc = setError(E_FAIL,
7756 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7757 getName().c_str(), status.iStatus);
7758 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7759 rc = setError(E_FAIL,
7760 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7761 getName().c_str(), status.iStatus);
7762 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7763 rc = setError(E_FAIL,
7764 tr("The virtual machine '%s' has terminated abnormally"),
7765 getName().c_str(), status.iStatus);
7766 else
7767 rc = setError(E_FAIL,
7768 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7769 getName().c_str(), rc);
7770 }
7771
7772#endif
7773
7774 if (FAILED(rc))
7775 {
7776 /* Close the remote session, remove the remote control from the list
7777 * and reset session state to Closed (@note keep the code in sync with
7778 * the relevant part in checkForSpawnFailure()). */
7779
7780 Assert(mData->mSession.mRemoteControls.size() == 1);
7781 if (mData->mSession.mRemoteControls.size() == 1)
7782 {
7783 ErrorInfoKeeper eik;
7784 mData->mSession.mRemoteControls.front()->Uninitialize();
7785 }
7786
7787 mData->mSession.mRemoteControls.clear();
7788 mData->mSession.mState = SessionState_Unlocked;
7789
7790 /* finalize the progress after setting the state */
7791 if (!mData->mSession.mProgress.isNull())
7792 {
7793 mData->mSession.mProgress->notifyComplete(rc);
7794 mData->mSession.mProgress.setNull();
7795 }
7796
7797 mParent->addProcessToReap(mData->mSession.mPID);
7798 mData->mSession.mPID = NIL_RTPROCESS;
7799
7800 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7801 return true;
7802 }
7803
7804 return false;
7805}
7806
7807/**
7808 * Checks whether the machine can be registered. If so, commits and saves
7809 * all settings.
7810 *
7811 * @note Must be called from mParent's write lock. Locks this object and
7812 * children for writing.
7813 */
7814HRESULT Machine::prepareRegister()
7815{
7816 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7817
7818 AutoLimitedCaller autoCaller(this);
7819 AssertComRCReturnRC(autoCaller.rc());
7820
7821 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7822
7823 /* wait for state dependents to drop to zero */
7824 ensureNoStateDependencies();
7825
7826 if (!mData->mAccessible)
7827 return setError(VBOX_E_INVALID_OBJECT_STATE,
7828 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7829 mUserData->s.strName.c_str(),
7830 mData->mUuid.toString().c_str());
7831
7832 AssertReturn(autoCaller.state() == Ready, E_FAIL);
7833
7834 if (mData->mRegistered)
7835 return setError(VBOX_E_INVALID_OBJECT_STATE,
7836 tr("The machine '%s' with UUID {%s} is already registered"),
7837 mUserData->s.strName.c_str(),
7838 mData->mUuid.toString().c_str());
7839
7840 HRESULT rc = S_OK;
7841
7842 // Ensure the settings are saved. If we are going to be registered and
7843 // no config file exists yet, create it by calling saveSettings() too.
7844 if ( (mData->flModifications)
7845 || (!mData->pMachineConfigFile->fileExists())
7846 )
7847 {
7848 rc = saveSettings(NULL);
7849 // no need to check whether VirtualBox.xml needs saving too since
7850 // we can't have a machine XML file rename pending
7851 if (FAILED(rc)) return rc;
7852 }
7853
7854 /* more config checking goes here */
7855
7856 if (SUCCEEDED(rc))
7857 {
7858 /* we may have had implicit modifications we want to fix on success */
7859 commit();
7860
7861 mData->mRegistered = true;
7862 }
7863 else
7864 {
7865 /* we may have had implicit modifications we want to cancel on failure*/
7866 rollback(false /* aNotify */);
7867 }
7868
7869 return rc;
7870}
7871
7872/**
7873 * Increases the number of objects dependent on the machine state or on the
7874 * registered state. Guarantees that these two states will not change at least
7875 * until #releaseStateDependency() is called.
7876 *
7877 * Depending on the @a aDepType value, additional state checks may be made.
7878 * These checks will set extended error info on failure. See
7879 * #checkStateDependency() for more info.
7880 *
7881 * If this method returns a failure, the dependency is not added and the caller
7882 * is not allowed to rely on any particular machine state or registration state
7883 * value and may return the failed result code to the upper level.
7884 *
7885 * @param aDepType Dependency type to add.
7886 * @param aState Current machine state (NULL if not interested).
7887 * @param aRegistered Current registered state (NULL if not interested).
7888 *
7889 * @note Locks this object for writing.
7890 */
7891HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7892 MachineState_T *aState /* = NULL */,
7893 BOOL *aRegistered /* = NULL */)
7894{
7895 AutoCaller autoCaller(this);
7896 AssertComRCReturnRC(autoCaller.rc());
7897
7898 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7899
7900 HRESULT rc = checkStateDependency(aDepType);
7901 if (FAILED(rc)) return rc;
7902
7903 {
7904 if (mData->mMachineStateChangePending != 0)
7905 {
7906 /* ensureNoStateDependencies() is waiting for state dependencies to
7907 * drop to zero so don't add more. It may make sense to wait a bit
7908 * and retry before reporting an error (since the pending state
7909 * transition should be really quick) but let's just assert for
7910 * now to see if it ever happens on practice. */
7911
7912 AssertFailed();
7913
7914 return setError(E_ACCESSDENIED,
7915 tr("Machine state change is in progress. Please retry the operation later."));
7916 }
7917
7918 ++mData->mMachineStateDeps;
7919 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7920 }
7921
7922 if (aState)
7923 *aState = mData->mMachineState;
7924 if (aRegistered)
7925 *aRegistered = mData->mRegistered;
7926
7927 return S_OK;
7928}
7929
7930/**
7931 * Decreases the number of objects dependent on the machine state.
7932 * Must always complete the #addStateDependency() call after the state
7933 * dependency is no more necessary.
7934 */
7935void Machine::releaseStateDependency()
7936{
7937 AutoCaller autoCaller(this);
7938 AssertComRCReturnVoid(autoCaller.rc());
7939
7940 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7941
7942 /* releaseStateDependency() w/o addStateDependency()? */
7943 AssertReturnVoid(mData->mMachineStateDeps != 0);
7944 -- mData->mMachineStateDeps;
7945
7946 if (mData->mMachineStateDeps == 0)
7947 {
7948 /* inform ensureNoStateDependencies() that there are no more deps */
7949 if (mData->mMachineStateChangePending != 0)
7950 {
7951 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7952 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7953 }
7954 }
7955}
7956
7957Utf8Str Machine::getExtraData(const Utf8Str &strKey)
7958{
7959 /* start with nothing found */
7960 Utf8Str strResult("");
7961
7962 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7963
7964 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7965 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7966 // found:
7967 strResult = it->second; // source is a Utf8Str
7968
7969 return strResult;
7970}
7971
7972// protected methods
7973/////////////////////////////////////////////////////////////////////////////
7974
7975/**
7976 * Performs machine state checks based on the @a aDepType value. If a check
7977 * fails, this method will set extended error info, otherwise it will return
7978 * S_OK. It is supposed, that on failure, the caller will immediately return
7979 * the return value of this method to the upper level.
7980 *
7981 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7982 *
7983 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7984 * current state of this machine object allows to change settings of the
7985 * machine (i.e. the machine is not registered, or registered but not running
7986 * and not saved). It is useful to call this method from Machine setters
7987 * before performing any change.
7988 *
7989 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7990 * as for MutableStateDep except that if the machine is saved, S_OK is also
7991 * returned. This is useful in setters which allow changing machine
7992 * properties when it is in the saved state.
7993 *
7994 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7995 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7996 * Aborted).
7997 *
7998 * @param aDepType Dependency type to check.
7999 *
8000 * @note Non Machine based classes should use #addStateDependency() and
8001 * #releaseStateDependency() methods or the smart AutoStateDependency
8002 * template.
8003 *
8004 * @note This method must be called from under this object's read or write
8005 * lock.
8006 */
8007HRESULT Machine::checkStateDependency(StateDependency aDepType)
8008{
8009 switch (aDepType)
8010 {
8011 case AnyStateDep:
8012 {
8013 break;
8014 }
8015 case MutableStateDep:
8016 {
8017 if ( mData->mRegistered
8018 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8019 || ( mData->mMachineState != MachineState_Paused
8020 && mData->mMachineState != MachineState_Running
8021 && mData->mMachineState != MachineState_Aborted
8022 && mData->mMachineState != MachineState_Teleported
8023 && mData->mMachineState != MachineState_PoweredOff
8024 )
8025 )
8026 )
8027 return setError(VBOX_E_INVALID_VM_STATE,
8028 tr("The machine is not mutable (state is %s)"),
8029 Global::stringifyMachineState(mData->mMachineState));
8030 break;
8031 }
8032 case MutableOrSavedStateDep:
8033 {
8034 if ( mData->mRegistered
8035 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8036 || ( mData->mMachineState != MachineState_Paused
8037 && mData->mMachineState != MachineState_Running
8038 && mData->mMachineState != MachineState_Aborted
8039 && mData->mMachineState != MachineState_Teleported
8040 && mData->mMachineState != MachineState_Saved
8041 && mData->mMachineState != MachineState_PoweredOff
8042 )
8043 )
8044 )
8045 return setError(VBOX_E_INVALID_VM_STATE,
8046 tr("The machine is not mutable (state is %s)"),
8047 Global::stringifyMachineState(mData->mMachineState));
8048 break;
8049 }
8050 case OfflineStateDep:
8051 {
8052 if ( mData->mRegistered
8053 && ( !isSessionMachine()
8054 || ( mData->mMachineState != MachineState_PoweredOff
8055 && mData->mMachineState != MachineState_Saved
8056 && mData->mMachineState != MachineState_Aborted
8057 && mData->mMachineState != MachineState_Teleported
8058 )
8059 )
8060 )
8061 return setError(VBOX_E_INVALID_VM_STATE,
8062 tr("The machine is not offline (state is %s)"),
8063 Global::stringifyMachineState(mData->mMachineState));
8064 break;
8065 }
8066 }
8067
8068 return S_OK;
8069}
8070
8071/**
8072 * Helper to initialize all associated child objects and allocate data
8073 * structures.
8074 *
8075 * This method must be called as a part of the object's initialization procedure
8076 * (usually done in the #init() method).
8077 *
8078 * @note Must be called only from #init() or from #registeredInit().
8079 */
8080HRESULT Machine::initDataAndChildObjects()
8081{
8082 AutoCaller autoCaller(this);
8083 AssertComRCReturnRC(autoCaller.rc());
8084 AssertComRCReturn(autoCaller.state() == InInit ||
8085 autoCaller.state() == Limited, E_FAIL);
8086
8087 AssertReturn(!mData->mAccessible, E_FAIL);
8088
8089 /* allocate data structures */
8090 mSSData.allocate();
8091 mUserData.allocate();
8092 mHWData.allocate();
8093 mMediaData.allocate();
8094 mStorageControllers.allocate();
8095
8096 /* initialize mOSTypeId */
8097 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
8098
8099 /* create associated BIOS settings object */
8100 unconst(mBIOSSettings).createObject();
8101 mBIOSSettings->init(this);
8102
8103 /* create an associated VRDE object (default is disabled) */
8104 unconst(mVRDEServer).createObject();
8105 mVRDEServer->init(this);
8106
8107 /* create associated serial port objects */
8108 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8109 {
8110 unconst(mSerialPorts[slot]).createObject();
8111 mSerialPorts[slot]->init(this, slot);
8112 }
8113
8114 /* create associated parallel port objects */
8115 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8116 {
8117 unconst(mParallelPorts[slot]).createObject();
8118 mParallelPorts[slot]->init(this, slot);
8119 }
8120
8121 /* create the audio adapter object (always present, default is disabled) */
8122 unconst(mAudioAdapter).createObject();
8123 mAudioAdapter->init(this);
8124
8125 /* create the USB controller object (always present, default is disabled) */
8126 unconst(mUSBController).createObject();
8127 mUSBController->init(this);
8128
8129 /* create associated network adapter objects */
8130 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8131 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8132 {
8133 unconst(mNetworkAdapters[slot]).createObject();
8134 mNetworkAdapters[slot]->init(this, slot);
8135 }
8136
8137 /* create the bandwidth control */
8138 unconst(mBandwidthControl).createObject();
8139 mBandwidthControl->init(this);
8140
8141 return S_OK;
8142}
8143
8144/**
8145 * Helper to uninitialize all associated child objects and to free all data
8146 * structures.
8147 *
8148 * This method must be called as a part of the object's uninitialization
8149 * procedure (usually done in the #uninit() method).
8150 *
8151 * @note Must be called only from #uninit() or from #registeredInit().
8152 */
8153void Machine::uninitDataAndChildObjects()
8154{
8155 AutoCaller autoCaller(this);
8156 AssertComRCReturnVoid(autoCaller.rc());
8157 AssertComRCReturnVoid( autoCaller.state() == InUninit
8158 || autoCaller.state() == Limited);
8159
8160 /* tell all our other child objects we've been uninitialized */
8161 if (mBandwidthControl)
8162 {
8163 mBandwidthControl->uninit();
8164 unconst(mBandwidthControl).setNull();
8165 }
8166
8167 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8168 {
8169 if (mNetworkAdapters[slot])
8170 {
8171 mNetworkAdapters[slot]->uninit();
8172 unconst(mNetworkAdapters[slot]).setNull();
8173 }
8174 }
8175
8176 if (mUSBController)
8177 {
8178 mUSBController->uninit();
8179 unconst(mUSBController).setNull();
8180 }
8181
8182 if (mAudioAdapter)
8183 {
8184 mAudioAdapter->uninit();
8185 unconst(mAudioAdapter).setNull();
8186 }
8187
8188 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8189 {
8190 if (mParallelPorts[slot])
8191 {
8192 mParallelPorts[slot]->uninit();
8193 unconst(mParallelPorts[slot]).setNull();
8194 }
8195 }
8196
8197 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8198 {
8199 if (mSerialPorts[slot])
8200 {
8201 mSerialPorts[slot]->uninit();
8202 unconst(mSerialPorts[slot]).setNull();
8203 }
8204 }
8205
8206 if (mVRDEServer)
8207 {
8208 mVRDEServer->uninit();
8209 unconst(mVRDEServer).setNull();
8210 }
8211
8212 if (mBIOSSettings)
8213 {
8214 mBIOSSettings->uninit();
8215 unconst(mBIOSSettings).setNull();
8216 }
8217
8218 /* Deassociate media (only when a real Machine or a SnapshotMachine
8219 * instance is uninitialized; SessionMachine instances refer to real
8220 * Machine media). This is necessary for a clean re-initialization of
8221 * the VM after successfully re-checking the accessibility state. Note
8222 * that in case of normal Machine or SnapshotMachine uninitialization (as
8223 * a result of unregistering or deleting the snapshot), outdated media
8224 * attachments will already be uninitialized and deleted, so this
8225 * code will not affect them. */
8226 if ( !!mMediaData
8227 && (!isSessionMachine())
8228 )
8229 {
8230 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8231 it != mMediaData->mAttachments.end();
8232 ++it)
8233 {
8234 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8235 if (pMedium.isNull())
8236 continue;
8237 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8238 AssertComRC(rc);
8239 }
8240 }
8241
8242 if (!isSessionMachine() && !isSnapshotMachine())
8243 {
8244 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8245 if (mData->mFirstSnapshot)
8246 {
8247 // snapshots tree is protected by machine write lock; strictly
8248 // this isn't necessary here since we're deleting the entire
8249 // machine, but otherwise we assert in Snapshot::uninit()
8250 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8251 mData->mFirstSnapshot->uninit();
8252 mData->mFirstSnapshot.setNull();
8253 }
8254
8255 mData->mCurrentSnapshot.setNull();
8256 }
8257
8258 /* free data structures (the essential mData structure is not freed here
8259 * since it may be still in use) */
8260 mMediaData.free();
8261 mStorageControllers.free();
8262 mHWData.free();
8263 mUserData.free();
8264 mSSData.free();
8265}
8266
8267/**
8268 * Returns a pointer to the Machine object for this machine that acts like a
8269 * parent for complex machine data objects such as shared folders, etc.
8270 *
8271 * For primary Machine objects and for SnapshotMachine objects, returns this
8272 * object's pointer itself. For SessionMachine objects, returns the peer
8273 * (primary) machine pointer.
8274 */
8275Machine* Machine::getMachine()
8276{
8277 if (isSessionMachine())
8278 return (Machine*)mPeer;
8279 return this;
8280}
8281
8282/**
8283 * Makes sure that there are no machine state dependents. If necessary, waits
8284 * for the number of dependents to drop to zero.
8285 *
8286 * Make sure this method is called from under this object's write lock to
8287 * guarantee that no new dependents may be added when this method returns
8288 * control to the caller.
8289 *
8290 * @note Locks this object for writing. The lock will be released while waiting
8291 * (if necessary).
8292 *
8293 * @warning To be used only in methods that change the machine state!
8294 */
8295void Machine::ensureNoStateDependencies()
8296{
8297 AssertReturnVoid(isWriteLockOnCurrentThread());
8298
8299 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8300
8301 /* Wait for all state dependents if necessary */
8302 if (mData->mMachineStateDeps != 0)
8303 {
8304 /* lazy semaphore creation */
8305 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8306 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8307
8308 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8309 mData->mMachineStateDeps));
8310
8311 ++mData->mMachineStateChangePending;
8312
8313 /* reset the semaphore before waiting, the last dependent will signal
8314 * it */
8315 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8316
8317 alock.release();
8318
8319 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8320
8321 alock.acquire();
8322
8323 -- mData->mMachineStateChangePending;
8324 }
8325}
8326
8327/**
8328 * Changes the machine state and informs callbacks.
8329 *
8330 * This method is not intended to fail so it either returns S_OK or asserts (and
8331 * returns a failure).
8332 *
8333 * @note Locks this object for writing.
8334 */
8335HRESULT Machine::setMachineState(MachineState_T aMachineState)
8336{
8337 LogFlowThisFuncEnter();
8338 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8339
8340 AutoCaller autoCaller(this);
8341 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8342
8343 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8344
8345 /* wait for state dependents to drop to zero */
8346 ensureNoStateDependencies();
8347
8348 if (mData->mMachineState != aMachineState)
8349 {
8350 mData->mMachineState = aMachineState;
8351
8352 RTTimeNow(&mData->mLastStateChange);
8353
8354 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8355 }
8356
8357 LogFlowThisFuncLeave();
8358 return S_OK;
8359}
8360
8361/**
8362 * Searches for a shared folder with the given logical name
8363 * in the collection of shared folders.
8364 *
8365 * @param aName logical name of the shared folder
8366 * @param aSharedFolder where to return the found object
8367 * @param aSetError whether to set the error info if the folder is
8368 * not found
8369 * @return
8370 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8371 *
8372 * @note
8373 * must be called from under the object's lock!
8374 */
8375HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8376 ComObjPtr<SharedFolder> &aSharedFolder,
8377 bool aSetError /* = false */)
8378{
8379 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8380 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8381 it != mHWData->mSharedFolders.end();
8382 ++it)
8383 {
8384 SharedFolder *pSF = *it;
8385 AutoCaller autoCaller(pSF);
8386 if (pSF->getName() == aName)
8387 {
8388 aSharedFolder = pSF;
8389 rc = S_OK;
8390 break;
8391 }
8392 }
8393
8394 if (aSetError && FAILED(rc))
8395 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8396
8397 return rc;
8398}
8399
8400/**
8401 * Initializes all machine instance data from the given settings structures
8402 * from XML. The exception is the machine UUID which needs special handling
8403 * depending on the caller's use case, so the caller needs to set that herself.
8404 *
8405 * This gets called in several contexts during machine initialization:
8406 *
8407 * -- When machine XML exists on disk already and needs to be loaded into memory,
8408 * for example, from registeredInit() to load all registered machines on
8409 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8410 * attached to the machine should be part of some media registry already.
8411 *
8412 * -- During OVF import, when a machine config has been constructed from an
8413 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8414 * ensure that the media listed as attachments in the config (which have
8415 * been imported from the OVF) receive the correct registry ID.
8416 *
8417 * -- During VM cloning.
8418 *
8419 * @param config Machine settings from XML.
8420 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8421 * @return
8422 */
8423HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8424 const Guid *puuidRegistry)
8425{
8426 // copy name, description, OS type, teleporter, UTC etc.
8427 mUserData->s = config.machineUserData;
8428
8429 // look up the object by Id to check it is valid
8430 ComPtr<IGuestOSType> guestOSType;
8431 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8432 guestOSType.asOutParam());
8433 if (FAILED(rc)) return rc;
8434
8435 // stateFile (optional)
8436 if (config.strStateFile.isEmpty())
8437 mSSData->strStateFilePath.setNull();
8438 else
8439 {
8440 Utf8Str stateFilePathFull(config.strStateFile);
8441 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8442 if (RT_FAILURE(vrc))
8443 return setError(E_FAIL,
8444 tr("Invalid saved state file path '%s' (%Rrc)"),
8445 config.strStateFile.c_str(),
8446 vrc);
8447 mSSData->strStateFilePath = stateFilePathFull;
8448 }
8449
8450 // snapshot folder needs special processing so set it again
8451 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8452 if (FAILED(rc)) return rc;
8453
8454 /* Copy the extra data items (Not in any case config is already the same as
8455 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8456 * make sure the extra data map is copied). */
8457 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8458
8459 /* currentStateModified (optional, default is true) */
8460 mData->mCurrentStateModified = config.fCurrentStateModified;
8461
8462 mData->mLastStateChange = config.timeLastStateChange;
8463
8464 /*
8465 * note: all mUserData members must be assigned prior this point because
8466 * we need to commit changes in order to let mUserData be shared by all
8467 * snapshot machine instances.
8468 */
8469 mUserData.commitCopy();
8470
8471 // machine registry, if present (must be loaded before snapshots)
8472 if (config.canHaveOwnMediaRegistry())
8473 {
8474 // determine machine folder
8475 Utf8Str strMachineFolder = getSettingsFileFull();
8476 strMachineFolder.stripFilename();
8477 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8478 config.mediaRegistry,
8479 strMachineFolder);
8480 if (FAILED(rc)) return rc;
8481 }
8482
8483 /* Snapshot node (optional) */
8484 size_t cRootSnapshots;
8485 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8486 {
8487 // there must be only one root snapshot
8488 Assert(cRootSnapshots == 1);
8489
8490 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8491
8492 rc = loadSnapshot(snap,
8493 config.uuidCurrentSnapshot,
8494 NULL); // no parent == first snapshot
8495 if (FAILED(rc)) return rc;
8496 }
8497
8498 // hardware data
8499 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8500 if (FAILED(rc)) return rc;
8501
8502 // load storage controllers
8503 rc = loadStorageControllers(config.storageMachine,
8504 puuidRegistry,
8505 NULL /* puuidSnapshot */);
8506 if (FAILED(rc)) return rc;
8507
8508 /*
8509 * NOTE: the assignment below must be the last thing to do,
8510 * otherwise it will be not possible to change the settings
8511 * somewhere in the code above because all setters will be
8512 * blocked by checkStateDependency(MutableStateDep).
8513 */
8514
8515 /* set the machine state to Aborted or Saved when appropriate */
8516 if (config.fAborted)
8517 {
8518 mSSData->strStateFilePath.setNull();
8519
8520 /* no need to use setMachineState() during init() */
8521 mData->mMachineState = MachineState_Aborted;
8522 }
8523 else if (!mSSData->strStateFilePath.isEmpty())
8524 {
8525 /* no need to use setMachineState() during init() */
8526 mData->mMachineState = MachineState_Saved;
8527 }
8528
8529 // after loading settings, we are no longer different from the XML on disk
8530 mData->flModifications = 0;
8531
8532 return S_OK;
8533}
8534
8535/**
8536 * Recursively loads all snapshots starting from the given.
8537 *
8538 * @param aNode <Snapshot> node.
8539 * @param aCurSnapshotId Current snapshot ID from the settings file.
8540 * @param aParentSnapshot Parent snapshot.
8541 */
8542HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8543 const Guid &aCurSnapshotId,
8544 Snapshot *aParentSnapshot)
8545{
8546 AssertReturn(!isSnapshotMachine(), E_FAIL);
8547 AssertReturn(!isSessionMachine(), E_FAIL);
8548
8549 HRESULT rc = S_OK;
8550
8551 Utf8Str strStateFile;
8552 if (!data.strStateFile.isEmpty())
8553 {
8554 /* optional */
8555 strStateFile = data.strStateFile;
8556 int vrc = calculateFullPath(strStateFile, strStateFile);
8557 if (RT_FAILURE(vrc))
8558 return setError(E_FAIL,
8559 tr("Invalid saved state file path '%s' (%Rrc)"),
8560 strStateFile.c_str(),
8561 vrc);
8562 }
8563
8564 /* create a snapshot machine object */
8565 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8566 pSnapshotMachine.createObject();
8567 rc = pSnapshotMachine->initFromSettings(this,
8568 data.hardware,
8569 &data.debugging,
8570 &data.autostart,
8571 data.storage,
8572 data.uuid.ref(),
8573 strStateFile);
8574 if (FAILED(rc)) return rc;
8575
8576 /* create a snapshot object */
8577 ComObjPtr<Snapshot> pSnapshot;
8578 pSnapshot.createObject();
8579 /* initialize the snapshot */
8580 rc = pSnapshot->init(mParent, // VirtualBox object
8581 data.uuid,
8582 data.strName,
8583 data.strDescription,
8584 data.timestamp,
8585 pSnapshotMachine,
8586 aParentSnapshot);
8587 if (FAILED(rc)) return rc;
8588
8589 /* memorize the first snapshot if necessary */
8590 if (!mData->mFirstSnapshot)
8591 mData->mFirstSnapshot = pSnapshot;
8592
8593 /* memorize the current snapshot when appropriate */
8594 if ( !mData->mCurrentSnapshot
8595 && pSnapshot->getId() == aCurSnapshotId
8596 )
8597 mData->mCurrentSnapshot = pSnapshot;
8598
8599 // now create the children
8600 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8601 it != data.llChildSnapshots.end();
8602 ++it)
8603 {
8604 const settings::Snapshot &childData = *it;
8605 // recurse
8606 rc = loadSnapshot(childData,
8607 aCurSnapshotId,
8608 pSnapshot); // parent = the one we created above
8609 if (FAILED(rc)) return rc;
8610 }
8611
8612 return rc;
8613}
8614
8615/**
8616 * Loads settings into mHWData.
8617 *
8618 * @param data Reference to the hardware settings.
8619 * @param pDbg Pointer to the debugging settings.
8620 * @param pAutostart Pointer to the autostart settings.
8621 */
8622HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8623 const settings::Autostart *pAutostart)
8624{
8625 AssertReturn(!isSessionMachine(), E_FAIL);
8626
8627 HRESULT rc = S_OK;
8628
8629 try
8630 {
8631 /* The hardware version attribute (optional). */
8632 mHWData->mHWVersion = data.strVersion;
8633 mHWData->mHardwareUUID = data.uuid;
8634
8635 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8636 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8637 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8638 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8639 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8640 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8641 mHWData->mPAEEnabled = data.fPAE;
8642 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8643 mHWData->mLongMode = data.enmLongMode;
8644 mHWData->mCPUCount = data.cCPUs;
8645 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8646 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8647
8648 // cpu
8649 if (mHWData->mCPUHotPlugEnabled)
8650 {
8651 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8652 it != data.llCpus.end();
8653 ++it)
8654 {
8655 const settings::Cpu &cpu = *it;
8656
8657 mHWData->mCPUAttached[cpu.ulId] = true;
8658 }
8659 }
8660
8661 // cpuid leafs
8662 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8663 it != data.llCpuIdLeafs.end();
8664 ++it)
8665 {
8666 const settings::CpuIdLeaf &leaf = *it;
8667
8668 switch (leaf.ulId)
8669 {
8670 case 0x0:
8671 case 0x1:
8672 case 0x2:
8673 case 0x3:
8674 case 0x4:
8675 case 0x5:
8676 case 0x6:
8677 case 0x7:
8678 case 0x8:
8679 case 0x9:
8680 case 0xA:
8681 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8682 break;
8683
8684 case 0x80000000:
8685 case 0x80000001:
8686 case 0x80000002:
8687 case 0x80000003:
8688 case 0x80000004:
8689 case 0x80000005:
8690 case 0x80000006:
8691 case 0x80000007:
8692 case 0x80000008:
8693 case 0x80000009:
8694 case 0x8000000A:
8695 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8696 break;
8697
8698 default:
8699 /* just ignore */
8700 break;
8701 }
8702 }
8703
8704 mHWData->mMemorySize = data.ulMemorySizeMB;
8705 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8706
8707 // boot order
8708 for (size_t i = 0;
8709 i < RT_ELEMENTS(mHWData->mBootOrder);
8710 i++)
8711 {
8712 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8713 if (it == data.mapBootOrder.end())
8714 mHWData->mBootOrder[i] = DeviceType_Null;
8715 else
8716 mHWData->mBootOrder[i] = it->second;
8717 }
8718
8719 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8720 mHWData->mMonitorCount = data.cMonitors;
8721 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8722 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8723 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8724 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8725 mHWData->mVideoCaptureEnabled = false; /* @todo r=klaus restore to data.fVideoCaptureEnabled */
8726 mHWData->mVideoCaptureFile = data.strVideoCaptureFile;
8727 mHWData->mFirmwareType = data.firmwareType;
8728 mHWData->mPointingHIDType = data.pointingHIDType;
8729 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8730 mHWData->mChipsetType = data.chipsetType;
8731 mHWData->mEmulatedUSBWebcamEnabled = data.fEmulatedUSBWebcam;
8732 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8733 mHWData->mHPETEnabled = data.fHPETEnabled;
8734
8735 /* VRDEServer */
8736 rc = mVRDEServer->loadSettings(data.vrdeSettings);
8737 if (FAILED(rc)) return rc;
8738
8739 /* BIOS */
8740 rc = mBIOSSettings->loadSettings(data.biosSettings);
8741 if (FAILED(rc)) return rc;
8742
8743 // Bandwidth control (must come before network adapters)
8744 rc = mBandwidthControl->loadSettings(data.ioSettings);
8745 if (FAILED(rc)) return rc;
8746
8747 /* USB Controller */
8748 rc = mUSBController->loadSettings(data.usbController);
8749 if (FAILED(rc)) return rc;
8750
8751 // network adapters
8752 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8753 uint32_t oldCount = mNetworkAdapters.size();
8754 if (newCount > oldCount)
8755 {
8756 mNetworkAdapters.resize(newCount);
8757 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
8758 {
8759 unconst(mNetworkAdapters[slot]).createObject();
8760 mNetworkAdapters[slot]->init(this, slot);
8761 }
8762 }
8763 else if (newCount < oldCount)
8764 mNetworkAdapters.resize(newCount);
8765 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8766 it != data.llNetworkAdapters.end();
8767 ++it)
8768 {
8769 const settings::NetworkAdapter &nic = *it;
8770
8771 /* slot unicity is guaranteed by XML Schema */
8772 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8773 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
8774 if (FAILED(rc)) return rc;
8775 }
8776
8777 // serial ports
8778 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8779 it != data.llSerialPorts.end();
8780 ++it)
8781 {
8782 const settings::SerialPort &s = *it;
8783
8784 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8785 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
8786 if (FAILED(rc)) return rc;
8787 }
8788
8789 // parallel ports (optional)
8790 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8791 it != data.llParallelPorts.end();
8792 ++it)
8793 {
8794 const settings::ParallelPort &p = *it;
8795
8796 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8797 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
8798 if (FAILED(rc)) return rc;
8799 }
8800
8801 /* AudioAdapter */
8802 rc = mAudioAdapter->loadSettings(data.audioAdapter);
8803 if (FAILED(rc)) return rc;
8804
8805 /* Shared folders */
8806 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8807 it != data.llSharedFolders.end();
8808 ++it)
8809 {
8810 const settings::SharedFolder &sf = *it;
8811
8812 ComObjPtr<SharedFolder> sharedFolder;
8813 /* Check for double entries. Not allowed! */
8814 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8815 if (SUCCEEDED(rc))
8816 return setError(VBOX_E_OBJECT_IN_USE,
8817 tr("Shared folder named '%s' already exists"),
8818 sf.strName.c_str());
8819
8820 /* Create the new shared folder. Don't break on error. This will be
8821 * reported when the machine starts. */
8822 sharedFolder.createObject();
8823 rc = sharedFolder->init(getMachine(),
8824 sf.strName,
8825 sf.strHostPath,
8826 RT_BOOL(sf.fWritable),
8827 RT_BOOL(sf.fAutoMount),
8828 false /* fFailOnError */);
8829 if (FAILED(rc)) return rc;
8830 mHWData->mSharedFolders.push_back(sharedFolder);
8831 }
8832
8833 // Clipboard
8834 mHWData->mClipboardMode = data.clipboardMode;
8835
8836 // drag'n'drop
8837 mHWData->mDragAndDropMode = data.dragAndDropMode;
8838
8839 // guest settings
8840 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8841
8842 // IO settings
8843 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8844 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8845
8846 // Host PCI devices
8847 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8848 it != data.pciAttachments.end();
8849 ++it)
8850 {
8851 const settings::HostPCIDeviceAttachment &hpda = *it;
8852 ComObjPtr<PCIDeviceAttachment> pda;
8853
8854 pda.createObject();
8855 pda->loadSettings(this, hpda);
8856 mHWData->mPCIDeviceAssignments.push_back(pda);
8857 }
8858
8859 /*
8860 * (The following isn't really real hardware, but it lives in HWData
8861 * for reasons of convenience.)
8862 */
8863
8864#ifdef VBOX_WITH_GUEST_PROPS
8865 /* Guest properties (optional) */
8866 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8867 it != data.llGuestProperties.end();
8868 ++it)
8869 {
8870 const settings::GuestProperty &prop = *it;
8871 uint32_t fFlags = guestProp::NILFLAG;
8872 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8873 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8874 mHWData->mGuestProperties[prop.strName] = property;
8875 }
8876
8877 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8878#endif /* VBOX_WITH_GUEST_PROPS defined */
8879
8880 rc = loadDebugging(pDbg);
8881 if (FAILED(rc))
8882 return rc;
8883
8884 mHWData->mAutostart = *pAutostart;
8885
8886 /* default frontend */
8887 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8888 }
8889 catch(std::bad_alloc &)
8890 {
8891 return E_OUTOFMEMORY;
8892 }
8893
8894 AssertComRC(rc);
8895 return rc;
8896}
8897
8898/**
8899 * Called from Machine::loadHardware() to load the debugging settings of the
8900 * machine.
8901 *
8902 * @param pDbg Pointer to the settings.
8903 */
8904HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
8905{
8906 mHWData->mDebugging = *pDbg;
8907 /* no more processing currently required, this will probably change. */
8908 return S_OK;
8909}
8910
8911/**
8912 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
8913 *
8914 * @param data
8915 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8916 * @param puuidSnapshot
8917 * @return
8918 */
8919HRESULT Machine::loadStorageControllers(const settings::Storage &data,
8920 const Guid *puuidRegistry,
8921 const Guid *puuidSnapshot)
8922{
8923 AssertReturn(!isSessionMachine(), E_FAIL);
8924
8925 HRESULT rc = S_OK;
8926
8927 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8928 it != data.llStorageControllers.end();
8929 ++it)
8930 {
8931 const settings::StorageController &ctlData = *it;
8932
8933 ComObjPtr<StorageController> pCtl;
8934 /* Try to find one with the name first. */
8935 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8936 if (SUCCEEDED(rc))
8937 return setError(VBOX_E_OBJECT_IN_USE,
8938 tr("Storage controller named '%s' already exists"),
8939 ctlData.strName.c_str());
8940
8941 pCtl.createObject();
8942 rc = pCtl->init(this,
8943 ctlData.strName,
8944 ctlData.storageBus,
8945 ctlData.ulInstance,
8946 ctlData.fBootable);
8947 if (FAILED(rc)) return rc;
8948
8949 mStorageControllers->push_back(pCtl);
8950
8951 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8952 if (FAILED(rc)) return rc;
8953
8954 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8955 if (FAILED(rc)) return rc;
8956
8957 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8958 if (FAILED(rc)) return rc;
8959
8960 /* Set IDE emulation settings (only for AHCI controller). */
8961 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8962 {
8963 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8964 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8965 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8966 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8967 )
8968 return rc;
8969 }
8970
8971 /* Load the attached devices now. */
8972 rc = loadStorageDevices(pCtl,
8973 ctlData,
8974 puuidRegistry,
8975 puuidSnapshot);
8976 if (FAILED(rc)) return rc;
8977 }
8978
8979 return S_OK;
8980}
8981
8982/**
8983 * Called from loadStorageControllers for a controller's devices.
8984 *
8985 * @param aStorageController
8986 * @param data
8987 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8988 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
8989 * @return
8990 */
8991HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
8992 const settings::StorageController &data,
8993 const Guid *puuidRegistry,
8994 const Guid *puuidSnapshot)
8995{
8996 HRESULT rc = S_OK;
8997
8998 /* paranoia: detect duplicate attachments */
8999 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9000 it != data.llAttachedDevices.end();
9001 ++it)
9002 {
9003 const settings::AttachedDevice &ad = *it;
9004
9005 for (settings::AttachedDevicesList::const_iterator it2 = it;
9006 it2 != data.llAttachedDevices.end();
9007 ++it2)
9008 {
9009 if (it == it2)
9010 continue;
9011
9012 const settings::AttachedDevice &ad2 = *it2;
9013
9014 if ( ad.lPort == ad2.lPort
9015 && ad.lDevice == ad2.lDevice)
9016 {
9017 return setError(E_FAIL,
9018 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9019 aStorageController->getName().c_str(),
9020 ad.lPort,
9021 ad.lDevice,
9022 mUserData->s.strName.c_str());
9023 }
9024 }
9025 }
9026
9027 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9028 it != data.llAttachedDevices.end();
9029 ++it)
9030 {
9031 const settings::AttachedDevice &dev = *it;
9032 ComObjPtr<Medium> medium;
9033
9034 switch (dev.deviceType)
9035 {
9036 case DeviceType_Floppy:
9037 case DeviceType_DVD:
9038 if (dev.strHostDriveSrc.isNotEmpty())
9039 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9040 else
9041 rc = mParent->findRemoveableMedium(dev.deviceType,
9042 dev.uuid,
9043 false /* fRefresh */,
9044 false /* aSetError */,
9045 medium);
9046 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9047 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9048 rc = S_OK;
9049 break;
9050
9051 case DeviceType_HardDisk:
9052 {
9053 /* find a hard disk by UUID */
9054 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9055 if (FAILED(rc))
9056 {
9057 if (isSnapshotMachine())
9058 {
9059 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9060 // so the user knows that the bad disk is in a snapshot somewhere
9061 com::ErrorInfo info;
9062 return setError(E_FAIL,
9063 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9064 puuidSnapshot->raw(),
9065 info.getText().raw());
9066 }
9067 else
9068 return rc;
9069 }
9070
9071 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9072
9073 if (medium->getType() == MediumType_Immutable)
9074 {
9075 if (isSnapshotMachine())
9076 return setError(E_FAIL,
9077 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9078 "of the virtual machine '%s' ('%s')"),
9079 medium->getLocationFull().c_str(),
9080 dev.uuid.raw(),
9081 puuidSnapshot->raw(),
9082 mUserData->s.strName.c_str(),
9083 mData->m_strConfigFileFull.c_str());
9084
9085 return setError(E_FAIL,
9086 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9087 medium->getLocationFull().c_str(),
9088 dev.uuid.raw(),
9089 mUserData->s.strName.c_str(),
9090 mData->m_strConfigFileFull.c_str());
9091 }
9092
9093 if (medium->getType() == MediumType_MultiAttach)
9094 {
9095 if (isSnapshotMachine())
9096 return setError(E_FAIL,
9097 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9098 "of the virtual machine '%s' ('%s')"),
9099 medium->getLocationFull().c_str(),
9100 dev.uuid.raw(),
9101 puuidSnapshot->raw(),
9102 mUserData->s.strName.c_str(),
9103 mData->m_strConfigFileFull.c_str());
9104
9105 return setError(E_FAIL,
9106 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9107 medium->getLocationFull().c_str(),
9108 dev.uuid.raw(),
9109 mUserData->s.strName.c_str(),
9110 mData->m_strConfigFileFull.c_str());
9111 }
9112
9113 if ( !isSnapshotMachine()
9114 && medium->getChildren().size() != 0
9115 )
9116 return setError(E_FAIL,
9117 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9118 "because it has %d differencing child hard disks"),
9119 medium->getLocationFull().c_str(),
9120 dev.uuid.raw(),
9121 mUserData->s.strName.c_str(),
9122 mData->m_strConfigFileFull.c_str(),
9123 medium->getChildren().size());
9124
9125 if (findAttachment(mMediaData->mAttachments,
9126 medium))
9127 return setError(E_FAIL,
9128 tr("Hard disk '%s' with UUID {%RTuuid} is already 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 break;
9135 }
9136
9137 default:
9138 return setError(E_FAIL,
9139 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9140 medium->getLocationFull().c_str(),
9141 mUserData->s.strName.c_str(),
9142 mData->m_strConfigFileFull.c_str());
9143 }
9144
9145 if (FAILED(rc))
9146 break;
9147
9148 /* Bandwidth groups are loaded at this point. */
9149 ComObjPtr<BandwidthGroup> pBwGroup;
9150
9151 if (!dev.strBwGroup.isEmpty())
9152 {
9153 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9154 if (FAILED(rc))
9155 return setError(E_FAIL,
9156 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9157 medium->getLocationFull().c_str(),
9158 dev.strBwGroup.c_str(),
9159 mUserData->s.strName.c_str(),
9160 mData->m_strConfigFileFull.c_str());
9161 pBwGroup->reference();
9162 }
9163
9164 const Bstr controllerName = aStorageController->getName();
9165 ComObjPtr<MediumAttachment> pAttachment;
9166 pAttachment.createObject();
9167 rc = pAttachment->init(this,
9168 medium,
9169 controllerName,
9170 dev.lPort,
9171 dev.lDevice,
9172 dev.deviceType,
9173 false,
9174 dev.fPassThrough,
9175 dev.fTempEject,
9176 dev.fNonRotational,
9177 dev.fDiscard,
9178 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
9179 if (FAILED(rc)) break;
9180
9181 /* associate the medium with this machine and snapshot */
9182 if (!medium.isNull())
9183 {
9184 AutoCaller medCaller(medium);
9185 if (FAILED(medCaller.rc())) return medCaller.rc();
9186 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9187
9188 if (isSnapshotMachine())
9189 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
9190 else
9191 rc = medium->addBackReference(mData->mUuid);
9192 /* If the medium->addBackReference fails it sets an appropriate
9193 * error message, so no need to do any guesswork here. */
9194
9195 if (puuidRegistry)
9196 // caller wants registry ID to be set on all attached media (OVF import case)
9197 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9198 }
9199
9200 if (FAILED(rc))
9201 break;
9202
9203 /* back up mMediaData to let registeredInit() properly rollback on failure
9204 * (= limited accessibility) */
9205 setModified(IsModified_Storage);
9206 mMediaData.backup();
9207 mMediaData->mAttachments.push_back(pAttachment);
9208 }
9209
9210 return rc;
9211}
9212
9213/**
9214 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9215 *
9216 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9217 * @param aSnapshot where to return the found snapshot
9218 * @param aSetError true to set extended error info on failure
9219 */
9220HRESULT Machine::findSnapshotById(const Guid &aId,
9221 ComObjPtr<Snapshot> &aSnapshot,
9222 bool aSetError /* = false */)
9223{
9224 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9225
9226 if (!mData->mFirstSnapshot)
9227 {
9228 if (aSetError)
9229 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9230 return E_FAIL;
9231 }
9232
9233 if (aId.isZero())
9234 aSnapshot = mData->mFirstSnapshot;
9235 else
9236 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9237
9238 if (!aSnapshot)
9239 {
9240 if (aSetError)
9241 return setError(E_FAIL,
9242 tr("Could not find a snapshot with UUID {%s}"),
9243 aId.toString().c_str());
9244 return E_FAIL;
9245 }
9246
9247 return S_OK;
9248}
9249
9250/**
9251 * Returns the snapshot with the given name or fails of no such snapshot.
9252 *
9253 * @param aName snapshot name to find
9254 * @param aSnapshot where to return the found snapshot
9255 * @param aSetError true to set extended error info on failure
9256 */
9257HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9258 ComObjPtr<Snapshot> &aSnapshot,
9259 bool aSetError /* = false */)
9260{
9261 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9262
9263 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9264
9265 if (!mData->mFirstSnapshot)
9266 {
9267 if (aSetError)
9268 return setError(VBOX_E_OBJECT_NOT_FOUND,
9269 tr("This machine does not have any snapshots"));
9270 return VBOX_E_OBJECT_NOT_FOUND;
9271 }
9272
9273 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9274
9275 if (!aSnapshot)
9276 {
9277 if (aSetError)
9278 return setError(VBOX_E_OBJECT_NOT_FOUND,
9279 tr("Could not find a snapshot named '%s'"), strName.c_str());
9280 return VBOX_E_OBJECT_NOT_FOUND;
9281 }
9282
9283 return S_OK;
9284}
9285
9286/**
9287 * Returns a storage controller object with the given name.
9288 *
9289 * @param aName storage controller name to find
9290 * @param aStorageController where to return the found storage controller
9291 * @param aSetError true to set extended error info on failure
9292 */
9293HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9294 ComObjPtr<StorageController> &aStorageController,
9295 bool aSetError /* = false */)
9296{
9297 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9298
9299 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9300 it != mStorageControllers->end();
9301 ++it)
9302 {
9303 if ((*it)->getName() == aName)
9304 {
9305 aStorageController = (*it);
9306 return S_OK;
9307 }
9308 }
9309
9310 if (aSetError)
9311 return setError(VBOX_E_OBJECT_NOT_FOUND,
9312 tr("Could not find a storage controller named '%s'"),
9313 aName.c_str());
9314 return VBOX_E_OBJECT_NOT_FOUND;
9315}
9316
9317HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9318 MediaData::AttachmentList &atts)
9319{
9320 AutoCaller autoCaller(this);
9321 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9322
9323 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9324
9325 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9326 it != mMediaData->mAttachments.end();
9327 ++it)
9328 {
9329 const ComObjPtr<MediumAttachment> &pAtt = *it;
9330
9331 // should never happen, but deal with NULL pointers in the list.
9332 AssertStmt(!pAtt.isNull(), continue);
9333
9334 // getControllerName() needs caller+read lock
9335 AutoCaller autoAttCaller(pAtt);
9336 if (FAILED(autoAttCaller.rc()))
9337 {
9338 atts.clear();
9339 return autoAttCaller.rc();
9340 }
9341 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9342
9343 if (pAtt->getControllerName() == aName)
9344 atts.push_back(pAtt);
9345 }
9346
9347 return S_OK;
9348}
9349
9350/**
9351 * Helper for #saveSettings. Cares about renaming the settings directory and
9352 * file if the machine name was changed and about creating a new settings file
9353 * if this is a new machine.
9354 *
9355 * @note Must be never called directly but only from #saveSettings().
9356 */
9357HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9358{
9359 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9360
9361 HRESULT rc = S_OK;
9362
9363 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9364
9365 /// @todo need to handle primary group change, too
9366
9367 /* attempt to rename the settings file if machine name is changed */
9368 if ( mUserData->s.fNameSync
9369 && mUserData.isBackedUp()
9370 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9371 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9372 )
9373 {
9374 bool dirRenamed = false;
9375 bool fileRenamed = false;
9376
9377 Utf8Str configFile, newConfigFile;
9378 Utf8Str configFilePrev, newConfigFilePrev;
9379 Utf8Str configDir, newConfigDir;
9380
9381 do
9382 {
9383 int vrc = VINF_SUCCESS;
9384
9385 Utf8Str name = mUserData.backedUpData()->s.strName;
9386 Utf8Str newName = mUserData->s.strName;
9387 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9388 if (group == "/")
9389 group.setNull();
9390 Utf8Str newGroup = mUserData->s.llGroups.front();
9391 if (newGroup == "/")
9392 newGroup.setNull();
9393
9394 configFile = mData->m_strConfigFileFull;
9395
9396 /* first, rename the directory if it matches the group and machine name */
9397 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9398 group.c_str(), RTPATH_DELIMITER, name.c_str());
9399 /** @todo hack, make somehow use of ComposeMachineFilename */
9400 if (mUserData->s.fDirectoryIncludesUUID)
9401 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9402 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9403 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9404 /** @todo hack, make somehow use of ComposeMachineFilename */
9405 if (mUserData->s.fDirectoryIncludesUUID)
9406 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9407 configDir = configFile;
9408 configDir.stripFilename();
9409 newConfigDir = configDir;
9410 if ( configDir.length() >= groupPlusName.length()
9411 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9412 {
9413 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9414 Utf8Str newConfigBaseDir(newConfigDir);
9415 newConfigDir.append(newGroupPlusName);
9416 /* consistency: use \ if appropriate on the platform */
9417 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9418 /* new dir and old dir cannot be equal here because of 'if'
9419 * above and because name != newName */
9420 Assert(configDir != newConfigDir);
9421 if (!fSettingsFileIsNew)
9422 {
9423 /* perform real rename only if the machine is not new */
9424 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9425 if ( vrc == VERR_FILE_NOT_FOUND
9426 || vrc == VERR_PATH_NOT_FOUND)
9427 {
9428 /* create the parent directory, then retry renaming */
9429 Utf8Str parent(newConfigDir);
9430 parent.stripFilename();
9431 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9432 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9433 }
9434 if (RT_FAILURE(vrc))
9435 {
9436 rc = setError(E_FAIL,
9437 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9438 configDir.c_str(),
9439 newConfigDir.c_str(),
9440 vrc);
9441 break;
9442 }
9443 /* delete subdirectories which are no longer needed */
9444 Utf8Str dir(configDir);
9445 dir.stripFilename();
9446 while (dir != newConfigBaseDir && dir != ".")
9447 {
9448 vrc = RTDirRemove(dir.c_str());
9449 if (RT_FAILURE(vrc))
9450 break;
9451 dir.stripFilename();
9452 }
9453 dirRenamed = true;
9454 }
9455 }
9456
9457 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9458 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9459
9460 /* then try to rename the settings file itself */
9461 if (newConfigFile != configFile)
9462 {
9463 /* get the path to old settings file in renamed directory */
9464 configFile = Utf8StrFmt("%s%c%s",
9465 newConfigDir.c_str(),
9466 RTPATH_DELIMITER,
9467 RTPathFilename(configFile.c_str()));
9468 if (!fSettingsFileIsNew)
9469 {
9470 /* perform real rename only if the machine is not new */
9471 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9472 if (RT_FAILURE(vrc))
9473 {
9474 rc = setError(E_FAIL,
9475 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9476 configFile.c_str(),
9477 newConfigFile.c_str(),
9478 vrc);
9479 break;
9480 }
9481 fileRenamed = true;
9482 configFilePrev = configFile;
9483 configFilePrev += "-prev";
9484 newConfigFilePrev = newConfigFile;
9485 newConfigFilePrev += "-prev";
9486 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9487 }
9488 }
9489
9490 // update m_strConfigFileFull amd mConfigFile
9491 mData->m_strConfigFileFull = newConfigFile;
9492 // compute the relative path too
9493 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9494
9495 // store the old and new so that VirtualBox::saveSettings() can update
9496 // the media registry
9497 if ( mData->mRegistered
9498 && configDir != newConfigDir)
9499 {
9500 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9501
9502 if (pfNeedsGlobalSaveSettings)
9503 *pfNeedsGlobalSaveSettings = true;
9504 }
9505
9506 // in the saved state file path, replace the old directory with the new directory
9507 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9508 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9509
9510 // and do the same thing for the saved state file paths of all the online snapshots
9511 if (mData->mFirstSnapshot)
9512 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9513 newConfigDir.c_str());
9514 }
9515 while (0);
9516
9517 if (FAILED(rc))
9518 {
9519 /* silently try to rename everything back */
9520 if (fileRenamed)
9521 {
9522 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9523 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9524 }
9525 if (dirRenamed)
9526 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9527 }
9528
9529 if (FAILED(rc)) return rc;
9530 }
9531
9532 if (fSettingsFileIsNew)
9533 {
9534 /* create a virgin config file */
9535 int vrc = VINF_SUCCESS;
9536
9537 /* ensure the settings directory exists */
9538 Utf8Str path(mData->m_strConfigFileFull);
9539 path.stripFilename();
9540 if (!RTDirExists(path.c_str()))
9541 {
9542 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9543 if (RT_FAILURE(vrc))
9544 {
9545 return setError(E_FAIL,
9546 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9547 path.c_str(),
9548 vrc);
9549 }
9550 }
9551
9552 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9553 path = Utf8Str(mData->m_strConfigFileFull);
9554 RTFILE f = NIL_RTFILE;
9555 vrc = RTFileOpen(&f, path.c_str(),
9556 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9557 if (RT_FAILURE(vrc))
9558 return setError(E_FAIL,
9559 tr("Could not create the settings file '%s' (%Rrc)"),
9560 path.c_str(),
9561 vrc);
9562 RTFileClose(f);
9563 }
9564
9565 return rc;
9566}
9567
9568/**
9569 * Saves and commits machine data, user data and hardware data.
9570 *
9571 * Note that on failure, the data remains uncommitted.
9572 *
9573 * @a aFlags may combine the following flags:
9574 *
9575 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9576 * Used when saving settings after an operation that makes them 100%
9577 * correspond to the settings from the current snapshot.
9578 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9579 * #isReallyModified() returns false. This is necessary for cases when we
9580 * change machine data directly, not through the backup()/commit() mechanism.
9581 * - SaveS_Force: settings will be saved without doing a deep compare of the
9582 * settings structures. This is used when this is called because snapshots
9583 * have changed to avoid the overhead of the deep compare.
9584 *
9585 * @note Must be called from under this object's write lock. Locks children for
9586 * writing.
9587 *
9588 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9589 * initialized to false and that will be set to true by this function if
9590 * the caller must invoke VirtualBox::saveSettings() because the global
9591 * settings have changed. This will happen if a machine rename has been
9592 * saved and the global machine and media registries will therefore need
9593 * updating.
9594 */
9595HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9596 int aFlags /*= 0*/)
9597{
9598 LogFlowThisFuncEnter();
9599
9600 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9601
9602 /* make sure child objects are unable to modify the settings while we are
9603 * saving them */
9604 ensureNoStateDependencies();
9605
9606 AssertReturn(!isSnapshotMachine(),
9607 E_FAIL);
9608
9609 HRESULT rc = S_OK;
9610 bool fNeedsWrite = false;
9611
9612 /* First, prepare to save settings. It will care about renaming the
9613 * settings directory and file if the machine name was changed and about
9614 * creating a new settings file if this is a new machine. */
9615 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9616 if (FAILED(rc)) return rc;
9617
9618 // keep a pointer to the current settings structures
9619 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9620 settings::MachineConfigFile *pNewConfig = NULL;
9621
9622 try
9623 {
9624 // make a fresh one to have everyone write stuff into
9625 pNewConfig = new settings::MachineConfigFile(NULL);
9626 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9627
9628 // now go and copy all the settings data from COM to the settings structures
9629 // (this calles saveSettings() on all the COM objects in the machine)
9630 copyMachineDataToSettings(*pNewConfig);
9631
9632 if (aFlags & SaveS_ResetCurStateModified)
9633 {
9634 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9635 mData->mCurrentStateModified = FALSE;
9636 fNeedsWrite = true; // always, no need to compare
9637 }
9638 else if (aFlags & SaveS_Force)
9639 {
9640 fNeedsWrite = true; // always, no need to compare
9641 }
9642 else
9643 {
9644 if (!mData->mCurrentStateModified)
9645 {
9646 // do a deep compare of the settings that we just saved with the settings
9647 // previously stored in the config file; this invokes MachineConfigFile::operator==
9648 // which does a deep compare of all the settings, which is expensive but less expensive
9649 // than writing out XML in vain
9650 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9651
9652 // could still be modified if any settings changed
9653 mData->mCurrentStateModified = fAnySettingsChanged;
9654
9655 fNeedsWrite = fAnySettingsChanged;
9656 }
9657 else
9658 fNeedsWrite = true;
9659 }
9660
9661 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9662
9663 if (fNeedsWrite)
9664 // now spit it all out!
9665 pNewConfig->write(mData->m_strConfigFileFull);
9666
9667 mData->pMachineConfigFile = pNewConfig;
9668 delete pOldConfig;
9669 commit();
9670
9671 // after saving settings, we are no longer different from the XML on disk
9672 mData->flModifications = 0;
9673 }
9674 catch (HRESULT err)
9675 {
9676 // we assume that error info is set by the thrower
9677 rc = err;
9678
9679 // restore old config
9680 delete pNewConfig;
9681 mData->pMachineConfigFile = pOldConfig;
9682 }
9683 catch (...)
9684 {
9685 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9686 }
9687
9688 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9689 {
9690 /* Fire the data change event, even on failure (since we've already
9691 * committed all data). This is done only for SessionMachines because
9692 * mutable Machine instances are always not registered (i.e. private
9693 * to the client process that creates them) and thus don't need to
9694 * inform callbacks. */
9695 if (isSessionMachine())
9696 mParent->onMachineDataChange(mData->mUuid);
9697 }
9698
9699 LogFlowThisFunc(("rc=%08X\n", rc));
9700 LogFlowThisFuncLeave();
9701 return rc;
9702}
9703
9704/**
9705 * Implementation for saving the machine settings into the given
9706 * settings::MachineConfigFile instance. This copies machine extradata
9707 * from the previous machine config file in the instance data, if any.
9708 *
9709 * This gets called from two locations:
9710 *
9711 * -- Machine::saveSettings(), during the regular XML writing;
9712 *
9713 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9714 * exported to OVF and we write the VirtualBox proprietary XML
9715 * into a <vbox:Machine> tag.
9716 *
9717 * This routine fills all the fields in there, including snapshots, *except*
9718 * for the following:
9719 *
9720 * -- fCurrentStateModified. There is some special logic associated with that.
9721 *
9722 * The caller can then call MachineConfigFile::write() or do something else
9723 * with it.
9724 *
9725 * Caller must hold the machine lock!
9726 *
9727 * This throws XML errors and HRESULT, so the caller must have a catch block!
9728 */
9729void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
9730{
9731 // deep copy extradata
9732 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9733
9734 config.uuid = mData->mUuid;
9735
9736 // copy name, description, OS type, teleport, UTC etc.
9737 config.machineUserData = mUserData->s;
9738
9739 if ( mData->mMachineState == MachineState_Saved
9740 || mData->mMachineState == MachineState_Restoring
9741 // when deleting a snapshot we may or may not have a saved state in the current state,
9742 // so let's not assert here please
9743 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9744 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9745 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9746 && (!mSSData->strStateFilePath.isEmpty())
9747 )
9748 )
9749 {
9750 Assert(!mSSData->strStateFilePath.isEmpty());
9751 /* try to make the file name relative to the settings file dir */
9752 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9753 }
9754 else
9755 {
9756 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9757 config.strStateFile.setNull();
9758 }
9759
9760 if (mData->mCurrentSnapshot)
9761 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
9762 else
9763 config.uuidCurrentSnapshot.clear();
9764
9765 config.timeLastStateChange = mData->mLastStateChange;
9766 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9767 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9768
9769 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9770 if (FAILED(rc)) throw rc;
9771
9772 rc = saveStorageControllers(config.storageMachine);
9773 if (FAILED(rc)) throw rc;
9774
9775 // save machine's media registry if this is VirtualBox 4.0 or later
9776 if (config.canHaveOwnMediaRegistry())
9777 {
9778 // determine machine folder
9779 Utf8Str strMachineFolder = getSettingsFileFull();
9780 strMachineFolder.stripFilename();
9781 mParent->saveMediaRegistry(config.mediaRegistry,
9782 getId(), // only media with registry ID == machine UUID
9783 strMachineFolder);
9784 // this throws HRESULT
9785 }
9786
9787 // save snapshots
9788 rc = saveAllSnapshots(config);
9789 if (FAILED(rc)) throw rc;
9790}
9791
9792/**
9793 * Saves all snapshots of the machine into the given machine config file. Called
9794 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9795 * @param config
9796 * @return
9797 */
9798HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
9799{
9800 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9801
9802 HRESULT rc = S_OK;
9803
9804 try
9805 {
9806 config.llFirstSnapshot.clear();
9807
9808 if (mData->mFirstSnapshot)
9809 {
9810 settings::Snapshot snapNew;
9811 config.llFirstSnapshot.push_back(snapNew);
9812
9813 // get reference to the fresh copy of the snapshot on the list and
9814 // work on that copy directly to avoid excessive copying later
9815 settings::Snapshot &snap = config.llFirstSnapshot.front();
9816
9817 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
9818 if (FAILED(rc)) throw rc;
9819 }
9820
9821// if (mType == IsSessionMachine)
9822// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9823
9824 }
9825 catch (HRESULT err)
9826 {
9827 /* we assume that error info is set by the thrower */
9828 rc = err;
9829 }
9830 catch (...)
9831 {
9832 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9833 }
9834
9835 return rc;
9836}
9837
9838/**
9839 * Saves the VM hardware configuration. It is assumed that the
9840 * given node is empty.
9841 *
9842 * @param data Reference to the settings object for the hardware config.
9843 * @param pDbg Pointer to the settings object for the debugging config
9844 * which happens to live in mHWData.
9845 * @param pAutostart Pointer to the settings object for the autostart config
9846 * which happens to live in mHWData.
9847 */
9848HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9849 settings::Autostart *pAutostart)
9850{
9851 HRESULT rc = S_OK;
9852
9853 try
9854 {
9855 /* The hardware version attribute (optional).
9856 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9857 if ( mHWData->mHWVersion == "1"
9858 && mSSData->strStateFilePath.isEmpty()
9859 )
9860 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. */
9861
9862 data.strVersion = mHWData->mHWVersion;
9863 data.uuid = mHWData->mHardwareUUID;
9864
9865 // CPU
9866 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9867 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
9868 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9869 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9870 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9871 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9872 data.fPAE = !!mHWData->mPAEEnabled;
9873 data.enmLongMode = mHWData->mLongMode;
9874 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9875
9876 /* Standard and Extended CPUID leafs. */
9877 data.llCpuIdLeafs.clear();
9878 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
9879 {
9880 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9881 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9882 }
9883 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
9884 {
9885 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9886 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9887 }
9888
9889 data.cCPUs = mHWData->mCPUCount;
9890 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9891 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9892
9893 data.llCpus.clear();
9894 if (data.fCpuHotPlug)
9895 {
9896 for (unsigned idx = 0; idx < data.cCPUs; idx++)
9897 {
9898 if (mHWData->mCPUAttached[idx])
9899 {
9900 settings::Cpu cpu;
9901 cpu.ulId = idx;
9902 data.llCpus.push_back(cpu);
9903 }
9904 }
9905 }
9906
9907 // memory
9908 data.ulMemorySizeMB = mHWData->mMemorySize;
9909 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9910
9911 // firmware
9912 data.firmwareType = mHWData->mFirmwareType;
9913
9914 // HID
9915 data.pointingHIDType = mHWData->mPointingHIDType;
9916 data.keyboardHIDType = mHWData->mKeyboardHIDType;
9917
9918 // chipset
9919 data.chipsetType = mHWData->mChipsetType;
9920
9921 data.fEmulatedUSBWebcam = !!mHWData->mEmulatedUSBWebcamEnabled;
9922 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9923
9924 // HPET
9925 data.fHPETEnabled = !!mHWData->mHPETEnabled;
9926
9927 // boot order
9928 data.mapBootOrder.clear();
9929 for (size_t i = 0;
9930 i < RT_ELEMENTS(mHWData->mBootOrder);
9931 ++i)
9932 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9933
9934 // display
9935 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9936 data.cMonitors = mHWData->mMonitorCount;
9937 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9938 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9939 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
9940 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
9941 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
9942 data.strVideoCaptureFile = mHWData->mVideoCaptureFile;
9943
9944 /* VRDEServer settings (optional) */
9945 rc = mVRDEServer->saveSettings(data.vrdeSettings);
9946 if (FAILED(rc)) throw rc;
9947
9948 /* BIOS (required) */
9949 rc = mBIOSSettings->saveSettings(data.biosSettings);
9950 if (FAILED(rc)) throw rc;
9951
9952 /* USB Controller (required) */
9953 rc = mUSBController->saveSettings(data.usbController);
9954 if (FAILED(rc)) throw rc;
9955
9956 /* Network adapters (required) */
9957 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
9958 data.llNetworkAdapters.clear();
9959 /* Write out only the nominal number of network adapters for this
9960 * chipset type. Since Machine::commit() hasn't been called there
9961 * may be extra NIC settings in the vector. */
9962 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
9963 {
9964 settings::NetworkAdapter nic;
9965 nic.ulSlot = slot;
9966 /* paranoia check... must not be NULL, but must not crash either. */
9967 if (mNetworkAdapters[slot])
9968 {
9969 rc = mNetworkAdapters[slot]->saveSettings(nic);
9970 if (FAILED(rc)) throw rc;
9971
9972 data.llNetworkAdapters.push_back(nic);
9973 }
9974 }
9975
9976 /* Serial ports */
9977 data.llSerialPorts.clear();
9978 for (ULONG slot = 0;
9979 slot < RT_ELEMENTS(mSerialPorts);
9980 ++slot)
9981 {
9982 settings::SerialPort s;
9983 s.ulSlot = slot;
9984 rc = mSerialPorts[slot]->saveSettings(s);
9985 if (FAILED(rc)) return rc;
9986
9987 data.llSerialPorts.push_back(s);
9988 }
9989
9990 /* Parallel ports */
9991 data.llParallelPorts.clear();
9992 for (ULONG slot = 0;
9993 slot < RT_ELEMENTS(mParallelPorts);
9994 ++slot)
9995 {
9996 settings::ParallelPort p;
9997 p.ulSlot = slot;
9998 rc = mParallelPorts[slot]->saveSettings(p);
9999 if (FAILED(rc)) return rc;
10000
10001 data.llParallelPorts.push_back(p);
10002 }
10003
10004 /* Audio adapter */
10005 rc = mAudioAdapter->saveSettings(data.audioAdapter);
10006 if (FAILED(rc)) return rc;
10007
10008 /* Shared folders */
10009 data.llSharedFolders.clear();
10010 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10011 it != mHWData->mSharedFolders.end();
10012 ++it)
10013 {
10014 SharedFolder *pSF = *it;
10015 AutoCaller sfCaller(pSF);
10016 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10017 settings::SharedFolder sf;
10018 sf.strName = pSF->getName();
10019 sf.strHostPath = pSF->getHostPath();
10020 sf.fWritable = !!pSF->isWritable();
10021 sf.fAutoMount = !!pSF->isAutoMounted();
10022
10023 data.llSharedFolders.push_back(sf);
10024 }
10025
10026 // clipboard
10027 data.clipboardMode = mHWData->mClipboardMode;
10028
10029 // drag'n'drop
10030 data.dragAndDropMode = mHWData->mDragAndDropMode;
10031
10032 /* Guest */
10033 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10034
10035 // IO settings
10036 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10037 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10038
10039 /* BandwidthControl (required) */
10040 rc = mBandwidthControl->saveSettings(data.ioSettings);
10041 if (FAILED(rc)) throw rc;
10042
10043 /* Host PCI devices */
10044 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10045 it != mHWData->mPCIDeviceAssignments.end();
10046 ++it)
10047 {
10048 ComObjPtr<PCIDeviceAttachment> pda = *it;
10049 settings::HostPCIDeviceAttachment hpda;
10050
10051 rc = pda->saveSettings(hpda);
10052 if (FAILED(rc)) throw rc;
10053
10054 data.pciAttachments.push_back(hpda);
10055 }
10056
10057
10058 // guest properties
10059 data.llGuestProperties.clear();
10060#ifdef VBOX_WITH_GUEST_PROPS
10061 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10062 it != mHWData->mGuestProperties.end();
10063 ++it)
10064 {
10065 HWData::GuestProperty property = it->second;
10066
10067 /* Remove transient guest properties at shutdown unless we
10068 * are saving state */
10069 if ( ( mData->mMachineState == MachineState_PoweredOff
10070 || mData->mMachineState == MachineState_Aborted
10071 || mData->mMachineState == MachineState_Teleported)
10072 && ( property.mFlags & guestProp::TRANSIENT
10073 || property.mFlags & guestProp::TRANSRESET))
10074 continue;
10075 settings::GuestProperty prop;
10076 prop.strName = it->first;
10077 prop.strValue = property.strValue;
10078 prop.timestamp = property.mTimestamp;
10079 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10080 guestProp::writeFlags(property.mFlags, szFlags);
10081 prop.strFlags = szFlags;
10082
10083 data.llGuestProperties.push_back(prop);
10084 }
10085
10086 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10087 /* I presume this doesn't require a backup(). */
10088 mData->mGuestPropertiesModified = FALSE;
10089#endif /* VBOX_WITH_GUEST_PROPS defined */
10090
10091 *pDbg = mHWData->mDebugging;
10092 *pAutostart = mHWData->mAutostart;
10093
10094 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10095 }
10096 catch(std::bad_alloc &)
10097 {
10098 return E_OUTOFMEMORY;
10099 }
10100
10101 AssertComRC(rc);
10102 return rc;
10103}
10104
10105/**
10106 * Saves the storage controller configuration.
10107 *
10108 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10109 */
10110HRESULT Machine::saveStorageControllers(settings::Storage &data)
10111{
10112 data.llStorageControllers.clear();
10113
10114 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10115 it != mStorageControllers->end();
10116 ++it)
10117 {
10118 HRESULT rc;
10119 ComObjPtr<StorageController> pCtl = *it;
10120
10121 settings::StorageController ctl;
10122 ctl.strName = pCtl->getName();
10123 ctl.controllerType = pCtl->getControllerType();
10124 ctl.storageBus = pCtl->getStorageBus();
10125 ctl.ulInstance = pCtl->getInstance();
10126 ctl.fBootable = pCtl->getBootable();
10127
10128 /* Save the port count. */
10129 ULONG portCount;
10130 rc = pCtl->COMGETTER(PortCount)(&portCount);
10131 ComAssertComRCRet(rc, rc);
10132 ctl.ulPortCount = portCount;
10133
10134 /* Save fUseHostIOCache */
10135 BOOL fUseHostIOCache;
10136 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10137 ComAssertComRCRet(rc, rc);
10138 ctl.fUseHostIOCache = !!fUseHostIOCache;
10139
10140 /* Save IDE emulation settings. */
10141 if (ctl.controllerType == StorageControllerType_IntelAhci)
10142 {
10143 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10144 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10145 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10146 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10147 )
10148 ComAssertComRCRet(rc, rc);
10149 }
10150
10151 /* save the devices now. */
10152 rc = saveStorageDevices(pCtl, ctl);
10153 ComAssertComRCRet(rc, rc);
10154
10155 data.llStorageControllers.push_back(ctl);
10156 }
10157
10158 return S_OK;
10159}
10160
10161/**
10162 * Saves the hard disk configuration.
10163 */
10164HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10165 settings::StorageController &data)
10166{
10167 MediaData::AttachmentList atts;
10168
10169 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
10170 if (FAILED(rc)) return rc;
10171
10172 data.llAttachedDevices.clear();
10173 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10174 it != atts.end();
10175 ++it)
10176 {
10177 settings::AttachedDevice dev;
10178
10179 MediumAttachment *pAttach = *it;
10180 Medium *pMedium = pAttach->getMedium();
10181
10182 dev.deviceType = pAttach->getType();
10183 dev.lPort = pAttach->getPort();
10184 dev.lDevice = pAttach->getDevice();
10185 if (pMedium)
10186 {
10187 if (pMedium->isHostDrive())
10188 dev.strHostDriveSrc = pMedium->getLocationFull();
10189 else
10190 dev.uuid = pMedium->getId();
10191 dev.fPassThrough = pAttach->getPassthrough();
10192 dev.fTempEject = pAttach->getTempEject();
10193 dev.fNonRotational = pAttach->getNonRotational();
10194 dev.fDiscard = pAttach->getDiscard();
10195 }
10196
10197 dev.strBwGroup = pAttach->getBandwidthGroup();
10198
10199 data.llAttachedDevices.push_back(dev);
10200 }
10201
10202 return S_OK;
10203}
10204
10205/**
10206 * Saves machine state settings as defined by aFlags
10207 * (SaveSTS_* values).
10208 *
10209 * @param aFlags Combination of SaveSTS_* flags.
10210 *
10211 * @note Locks objects for writing.
10212 */
10213HRESULT Machine::saveStateSettings(int aFlags)
10214{
10215 if (aFlags == 0)
10216 return S_OK;
10217
10218 AutoCaller autoCaller(this);
10219 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10220
10221 /* This object's write lock is also necessary to serialize file access
10222 * (prevent concurrent reads and writes) */
10223 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10224
10225 HRESULT rc = S_OK;
10226
10227 Assert(mData->pMachineConfigFile);
10228
10229 try
10230 {
10231 if (aFlags & SaveSTS_CurStateModified)
10232 mData->pMachineConfigFile->fCurrentStateModified = true;
10233
10234 if (aFlags & SaveSTS_StateFilePath)
10235 {
10236 if (!mSSData->strStateFilePath.isEmpty())
10237 /* try to make the file name relative to the settings file dir */
10238 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10239 else
10240 mData->pMachineConfigFile->strStateFile.setNull();
10241 }
10242
10243 if (aFlags & SaveSTS_StateTimeStamp)
10244 {
10245 Assert( mData->mMachineState != MachineState_Aborted
10246 || mSSData->strStateFilePath.isEmpty());
10247
10248 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10249
10250 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10251//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10252 }
10253
10254 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10255 }
10256 catch (...)
10257 {
10258 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10259 }
10260
10261 return rc;
10262}
10263
10264/**
10265 * Ensures that the given medium is added to a media registry. If this machine
10266 * was created with 4.0 or later, then the machine registry is used. Otherwise
10267 * the global VirtualBox media registry is used.
10268 *
10269 * Caller must NOT hold machine lock, media tree or any medium locks!
10270 *
10271 * @param pMedium
10272 */
10273void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10274{
10275 /* Paranoia checks: do not hold machine or media tree locks. */
10276 AssertReturnVoid(!isWriteLockOnCurrentThread());
10277 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10278
10279 ComObjPtr<Medium> pBase;
10280 {
10281 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10282 pBase = pMedium->getBase();
10283 }
10284
10285 /* Paranoia checks: do not hold medium locks. */
10286 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10287 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10288
10289 // decide which medium registry to use now that the medium is attached:
10290 Guid uuid;
10291 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10292 // machine XML is VirtualBox 4.0 or higher:
10293 uuid = getId(); // machine UUID
10294 else
10295 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10296
10297 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10298 mParent->markRegistryModified(uuid);
10299
10300 /* For more complex hard disk structures it can happen that the base
10301 * medium isn't yet associated with any medium registry. Do that now. */
10302 if (pMedium != pBase)
10303 {
10304 if (pBase->addRegistry(uuid, true /* fRecurse */))
10305 mParent->markRegistryModified(uuid);
10306 }
10307}
10308
10309/**
10310 * Creates differencing hard disks for all normal hard disks attached to this
10311 * machine and a new set of attachments to refer to created disks.
10312 *
10313 * Used when taking a snapshot or when deleting the current state. Gets called
10314 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10315 *
10316 * This method assumes that mMediaData contains the original hard disk attachments
10317 * it needs to create diffs for. On success, these attachments will be replaced
10318 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10319 * called to delete created diffs which will also rollback mMediaData and restore
10320 * whatever was backed up before calling this method.
10321 *
10322 * Attachments with non-normal hard disks are left as is.
10323 *
10324 * If @a aOnline is @c false then the original hard disks that require implicit
10325 * diffs will be locked for reading. Otherwise it is assumed that they are
10326 * already locked for writing (when the VM was started). Note that in the latter
10327 * case it is responsibility of the caller to lock the newly created diffs for
10328 * writing if this method succeeds.
10329 *
10330 * @param aProgress Progress object to run (must contain at least as
10331 * many operations left as the number of hard disks
10332 * attached).
10333 * @param aOnline Whether the VM was online prior to this operation.
10334 *
10335 * @note The progress object is not marked as completed, neither on success nor
10336 * on failure. This is a responsibility of the caller.
10337 *
10338 * @note Locks this object and the media tree for writing.
10339 */
10340HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10341 ULONG aWeight,
10342 bool aOnline)
10343{
10344 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10345
10346 AutoCaller autoCaller(this);
10347 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10348
10349 AutoMultiWriteLock2 alock(this->lockHandle(),
10350 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10351
10352 /* must be in a protective state because we release the lock below */
10353 AssertReturn( mData->mMachineState == MachineState_Saving
10354 || mData->mMachineState == MachineState_LiveSnapshotting
10355 || mData->mMachineState == MachineState_RestoringSnapshot
10356 || mData->mMachineState == MachineState_DeletingSnapshot
10357 , E_FAIL);
10358
10359 HRESULT rc = S_OK;
10360
10361 // use appropriate locked media map (online or offline)
10362 MediumLockListMap lockedMediaOffline;
10363 MediumLockListMap *lockedMediaMap;
10364 if (aOnline)
10365 lockedMediaMap = &mData->mSession.mLockedMedia;
10366 else
10367 lockedMediaMap = &lockedMediaOffline;
10368
10369 try
10370 {
10371 if (!aOnline)
10372 {
10373 /* lock all attached hard disks early to detect "in use"
10374 * situations before creating actual diffs */
10375 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10376 it != mMediaData->mAttachments.end();
10377 ++it)
10378 {
10379 MediumAttachment* pAtt = *it;
10380 if (pAtt->getType() == DeviceType_HardDisk)
10381 {
10382 Medium* pMedium = pAtt->getMedium();
10383 Assert(pMedium);
10384
10385 MediumLockList *pMediumLockList(new MediumLockList());
10386 alock.release();
10387 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10388 false /* fMediumLockWrite */,
10389 NULL,
10390 *pMediumLockList);
10391 alock.acquire();
10392 if (FAILED(rc))
10393 {
10394 delete pMediumLockList;
10395 throw rc;
10396 }
10397 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10398 if (FAILED(rc))
10399 {
10400 throw setError(rc,
10401 tr("Collecting locking information for all attached media failed"));
10402 }
10403 }
10404 }
10405
10406 /* Now lock all media. If this fails, nothing is locked. */
10407 alock.release();
10408 rc = lockedMediaMap->Lock();
10409 alock.acquire();
10410 if (FAILED(rc))
10411 {
10412 throw setError(rc,
10413 tr("Locking of attached media failed"));
10414 }
10415 }
10416
10417 /* remember the current list (note that we don't use backup() since
10418 * mMediaData may be already backed up) */
10419 MediaData::AttachmentList atts = mMediaData->mAttachments;
10420
10421 /* start from scratch */
10422 mMediaData->mAttachments.clear();
10423
10424 /* go through remembered attachments and create diffs for normal hard
10425 * disks and attach them */
10426 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10427 it != atts.end();
10428 ++it)
10429 {
10430 MediumAttachment* pAtt = *it;
10431
10432 DeviceType_T devType = pAtt->getType();
10433 Medium* pMedium = pAtt->getMedium();
10434
10435 if ( devType != DeviceType_HardDisk
10436 || pMedium == NULL
10437 || pMedium->getType() != MediumType_Normal)
10438 {
10439 /* copy the attachment as is */
10440
10441 /** @todo the progress object created in Console::TakeSnaphot
10442 * only expects operations for hard disks. Later other
10443 * device types need to show up in the progress as well. */
10444 if (devType == DeviceType_HardDisk)
10445 {
10446 if (pMedium == NULL)
10447 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10448 aWeight); // weight
10449 else
10450 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10451 pMedium->getBase()->getName().c_str()).raw(),
10452 aWeight); // weight
10453 }
10454
10455 mMediaData->mAttachments.push_back(pAtt);
10456 continue;
10457 }
10458
10459 /* need a diff */
10460 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10461 pMedium->getBase()->getName().c_str()).raw(),
10462 aWeight); // weight
10463
10464 Utf8Str strFullSnapshotFolder;
10465 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10466
10467 ComObjPtr<Medium> diff;
10468 diff.createObject();
10469 // store the diff in the same registry as the parent
10470 // (this cannot fail here because we can't create implicit diffs for
10471 // unregistered images)
10472 Guid uuidRegistryParent;
10473 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10474 Assert(fInRegistry); NOREF(fInRegistry);
10475 rc = diff->init(mParent,
10476 pMedium->getPreferredDiffFormat(),
10477 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10478 uuidRegistryParent);
10479 if (FAILED(rc)) throw rc;
10480
10481 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10482 * the push_back? Looks like we're going to release medium with the
10483 * wrong kind of lock (general issue with if we fail anywhere at all)
10484 * and an orphaned VDI in the snapshots folder. */
10485
10486 /* update the appropriate lock list */
10487 MediumLockList *pMediumLockList;
10488 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10489 AssertComRCThrowRC(rc);
10490 if (aOnline)
10491 {
10492 alock.release();
10493 /* The currently attached medium will be read-only, change
10494 * the lock type to read. */
10495 rc = pMediumLockList->Update(pMedium, false);
10496 alock.acquire();
10497 AssertComRCThrowRC(rc);
10498 }
10499
10500 /* release the locks before the potentially lengthy operation */
10501 alock.release();
10502 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10503 pMediumLockList,
10504 NULL /* aProgress */,
10505 true /* aWait */);
10506 alock.acquire();
10507 if (FAILED(rc)) throw rc;
10508
10509 rc = lockedMediaMap->Unlock();
10510 AssertComRCThrowRC(rc);
10511 alock.release();
10512 rc = pMediumLockList->Append(diff, true);
10513 alock.acquire();
10514 AssertComRCThrowRC(rc);
10515 alock.release();
10516 rc = lockedMediaMap->Lock();
10517 alock.acquire();
10518 AssertComRCThrowRC(rc);
10519
10520 rc = diff->addBackReference(mData->mUuid);
10521 AssertComRCThrowRC(rc);
10522
10523 /* add a new attachment */
10524 ComObjPtr<MediumAttachment> attachment;
10525 attachment.createObject();
10526 rc = attachment->init(this,
10527 diff,
10528 pAtt->getControllerName(),
10529 pAtt->getPort(),
10530 pAtt->getDevice(),
10531 DeviceType_HardDisk,
10532 true /* aImplicit */,
10533 false /* aPassthrough */,
10534 false /* aTempEject */,
10535 pAtt->getNonRotational(),
10536 pAtt->getDiscard(),
10537 pAtt->getBandwidthGroup());
10538 if (FAILED(rc)) throw rc;
10539
10540 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10541 AssertComRCThrowRC(rc);
10542 mMediaData->mAttachments.push_back(attachment);
10543 }
10544 }
10545 catch (HRESULT aRC) { rc = aRC; }
10546
10547 /* unlock all hard disks we locked when there is no VM */
10548 if (!aOnline)
10549 {
10550 ErrorInfoKeeper eik;
10551
10552 HRESULT rc1 = lockedMediaMap->Clear();
10553 AssertComRC(rc1);
10554 }
10555
10556 return rc;
10557}
10558
10559/**
10560 * Deletes implicit differencing hard disks created either by
10561 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10562 *
10563 * Note that to delete hard disks created by #AttachDevice() this method is
10564 * called from #fixupMedia() when the changes are rolled back.
10565 *
10566 * @note Locks this object and the media tree for writing.
10567 */
10568HRESULT Machine::deleteImplicitDiffs(bool aOnline)
10569{
10570 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10571
10572 AutoCaller autoCaller(this);
10573 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10574
10575 AutoMultiWriteLock2 alock(this->lockHandle(),
10576 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10577
10578 /* We absolutely must have backed up state. */
10579 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10580
10581 /* Check if there are any implicitly created diff images. */
10582 bool fImplicitDiffs = false;
10583 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10584 it != mMediaData->mAttachments.end();
10585 ++it)
10586 {
10587 const ComObjPtr<MediumAttachment> &pAtt = *it;
10588 if (pAtt->isImplicit())
10589 {
10590 fImplicitDiffs = true;
10591 break;
10592 }
10593 }
10594 /* If there is nothing to do, leave early. This saves lots of image locking
10595 * effort. It also avoids a MachineStateChanged event without real reason.
10596 * This is important e.g. when loading a VM config, because there should be
10597 * no events. Otherwise API clients can become thoroughly confused for
10598 * inaccessible VMs (the code for loading VM configs uses this method for
10599 * cleanup if the config makes no sense), as they take such events as an
10600 * indication that the VM is alive, and they would force the VM config to
10601 * be reread, leading to an endless loop. */
10602 if (!fImplicitDiffs)
10603 return S_OK;
10604
10605 HRESULT rc = S_OK;
10606 MachineState_T oldState = mData->mMachineState;
10607
10608 /* will release the lock before the potentially lengthy operation,
10609 * so protect with the special state (unless already protected) */
10610 if ( oldState != MachineState_Saving
10611 && oldState != MachineState_LiveSnapshotting
10612 && oldState != MachineState_RestoringSnapshot
10613 && oldState != MachineState_DeletingSnapshot
10614 && oldState != MachineState_DeletingSnapshotOnline
10615 && oldState != MachineState_DeletingSnapshotPaused
10616 )
10617 setMachineState(MachineState_SettingUp);
10618
10619 // use appropriate locked media map (online or offline)
10620 MediumLockListMap lockedMediaOffline;
10621 MediumLockListMap *lockedMediaMap;
10622 if (aOnline)
10623 lockedMediaMap = &mData->mSession.mLockedMedia;
10624 else
10625 lockedMediaMap = &lockedMediaOffline;
10626
10627 try
10628 {
10629 if (!aOnline)
10630 {
10631 /* lock all attached hard disks early to detect "in use"
10632 * situations before deleting actual diffs */
10633 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10634 it != mMediaData->mAttachments.end();
10635 ++it)
10636 {
10637 MediumAttachment* pAtt = *it;
10638 if (pAtt->getType() == DeviceType_HardDisk)
10639 {
10640 Medium* pMedium = pAtt->getMedium();
10641 Assert(pMedium);
10642
10643 MediumLockList *pMediumLockList(new MediumLockList());
10644 alock.release();
10645 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10646 false /* fMediumLockWrite */,
10647 NULL,
10648 *pMediumLockList);
10649 alock.acquire();
10650
10651 if (FAILED(rc))
10652 {
10653 delete pMediumLockList;
10654 throw rc;
10655 }
10656
10657 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10658 if (FAILED(rc))
10659 throw rc;
10660 }
10661 }
10662
10663 if (FAILED(rc))
10664 throw rc;
10665 } // end of offline
10666
10667 /* Lock lists are now up to date and include implicitly created media */
10668
10669 /* Go through remembered attachments and delete all implicitly created
10670 * diffs and fix up the attachment information */
10671 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10672 MediaData::AttachmentList implicitAtts;
10673 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10674 it != mMediaData->mAttachments.end();
10675 ++it)
10676 {
10677 ComObjPtr<MediumAttachment> pAtt = *it;
10678 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10679 if (pMedium.isNull())
10680 continue;
10681
10682 // Implicit attachments go on the list for deletion and back references are removed.
10683 if (pAtt->isImplicit())
10684 {
10685 /* Deassociate and mark for deletion */
10686 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
10687 rc = pMedium->removeBackReference(mData->mUuid);
10688 if (FAILED(rc))
10689 throw rc;
10690 implicitAtts.push_back(pAtt);
10691 continue;
10692 }
10693
10694 /* Was this medium attached before? */
10695 if (!findAttachment(oldAtts, pMedium))
10696 {
10697 /* no: de-associate */
10698 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
10699 rc = pMedium->removeBackReference(mData->mUuid);
10700 if (FAILED(rc))
10701 throw rc;
10702 continue;
10703 }
10704 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
10705 }
10706
10707 /* If there are implicit attachments to delete, throw away the lock
10708 * map contents (which will unlock all media) since the medium
10709 * attachments will be rolled back. Below we need to completely
10710 * recreate the lock map anyway since it is infinitely complex to
10711 * do this incrementally (would need reconstructing each attachment
10712 * change, which would be extremely hairy). */
10713 if (implicitAtts.size() != 0)
10714 {
10715 ErrorInfoKeeper eik;
10716
10717 HRESULT rc1 = lockedMediaMap->Clear();
10718 AssertComRC(rc1);
10719 }
10720
10721 /* rollback hard disk changes */
10722 mMediaData.rollback();
10723
10724 MultiResult mrc(S_OK);
10725
10726 // Delete unused implicit diffs.
10727 if (implicitAtts.size() != 0)
10728 {
10729 alock.release();
10730
10731 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
10732 it != implicitAtts.end();
10733 ++it)
10734 {
10735 // Remove medium associated with this attachment.
10736 ComObjPtr<MediumAttachment> pAtt = *it;
10737 Assert(pAtt);
10738 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
10739 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10740 Assert(pMedium);
10741
10742 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10743 // continue on delete failure, just collect error messages
10744 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
10745 mrc = rc;
10746 }
10747
10748 alock.acquire();
10749
10750 /* if there is a VM recreate media lock map as mentioned above,
10751 * otherwise it is a waste of time and we leave things unlocked */
10752 if (aOnline)
10753 {
10754 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10755 /* must never be NULL, but better safe than sorry */
10756 if (!pMachine.isNull())
10757 {
10758 alock.release();
10759 rc = mData->mSession.mMachine->lockMedia();
10760 alock.acquire();
10761 if (FAILED(rc))
10762 throw rc;
10763 }
10764 }
10765 }
10766 }
10767 catch (HRESULT aRC) {rc = aRC;}
10768
10769 if (mData->mMachineState == MachineState_SettingUp)
10770 setMachineState(oldState);
10771
10772 /* unlock all hard disks we locked when there is no VM */
10773 if (!aOnline)
10774 {
10775 ErrorInfoKeeper eik;
10776
10777 HRESULT rc1 = lockedMediaMap->Clear();
10778 AssertComRC(rc1);
10779 }
10780
10781 return rc;
10782}
10783
10784
10785/**
10786 * Looks through the given list of media attachments for one with the given parameters
10787 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10788 * can be searched as well if needed.
10789 *
10790 * @param list
10791 * @param aControllerName
10792 * @param aControllerPort
10793 * @param aDevice
10794 * @return
10795 */
10796MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10797 IN_BSTR aControllerName,
10798 LONG aControllerPort,
10799 LONG aDevice)
10800{
10801 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10802 it != ll.end();
10803 ++it)
10804 {
10805 MediumAttachment *pAttach = *it;
10806 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
10807 return pAttach;
10808 }
10809
10810 return NULL;
10811}
10812
10813/**
10814 * Looks through the given list of media attachments for one with the given parameters
10815 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10816 * can be searched as well if needed.
10817 *
10818 * @param list
10819 * @param aControllerName
10820 * @param aControllerPort
10821 * @param aDevice
10822 * @return
10823 */
10824MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10825 ComObjPtr<Medium> pMedium)
10826{
10827 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10828 it != ll.end();
10829 ++it)
10830 {
10831 MediumAttachment *pAttach = *it;
10832 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10833 if (pMediumThis == pMedium)
10834 return pAttach;
10835 }
10836
10837 return NULL;
10838}
10839
10840/**
10841 * Looks through the given list of media attachments for one with the given parameters
10842 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10843 * can be searched as well if needed.
10844 *
10845 * @param list
10846 * @param aControllerName
10847 * @param aControllerPort
10848 * @param aDevice
10849 * @return
10850 */
10851MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10852 Guid &id)
10853{
10854 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10855 it != ll.end();
10856 ++it)
10857 {
10858 MediumAttachment *pAttach = *it;
10859 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10860 if (pMediumThis->getId() == id)
10861 return pAttach;
10862 }
10863
10864 return NULL;
10865}
10866
10867/**
10868 * Main implementation for Machine::DetachDevice. This also gets called
10869 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10870 *
10871 * @param pAttach Medium attachment to detach.
10872 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10873 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
10874 * @return
10875 */
10876HRESULT Machine::detachDevice(MediumAttachment *pAttach,
10877 AutoWriteLock &writeLock,
10878 Snapshot *pSnapshot)
10879{
10880 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
10881 DeviceType_T mediumType = pAttach->getType();
10882
10883 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
10884
10885 if (pAttach->isImplicit())
10886 {
10887 /* attempt to implicitly delete the implicitly created diff */
10888
10889 /// @todo move the implicit flag from MediumAttachment to Medium
10890 /// and forbid any hard disk operation when it is implicit. Or maybe
10891 /// a special media state for it to make it even more simple.
10892
10893 Assert(mMediaData.isBackedUp());
10894
10895 /* will release the lock before the potentially lengthy operation, so
10896 * protect with the special state */
10897 MachineState_T oldState = mData->mMachineState;
10898 setMachineState(MachineState_SettingUp);
10899
10900 writeLock.release();
10901
10902 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
10903 true /*aWait*/);
10904
10905 writeLock.acquire();
10906
10907 setMachineState(oldState);
10908
10909 if (FAILED(rc)) return rc;
10910 }
10911
10912 setModified(IsModified_Storage);
10913 mMediaData.backup();
10914 mMediaData->mAttachments.remove(pAttach);
10915
10916 if (!oldmedium.isNull())
10917 {
10918 // if this is from a snapshot, do not defer detachment to commitMedia()
10919 if (pSnapshot)
10920 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
10921 // else if non-hard disk media, do not defer detachment to commitMedia() either
10922 else if (mediumType != DeviceType_HardDisk)
10923 oldmedium->removeBackReference(mData->mUuid);
10924 }
10925
10926 return S_OK;
10927}
10928
10929/**
10930 * Goes thru all media of the given list and
10931 *
10932 * 1) calls detachDevice() on each of them for this machine and
10933 * 2) adds all Medium objects found in the process to the given list,
10934 * depending on cleanupMode.
10935 *
10936 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10937 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10938 * media to the list.
10939 *
10940 * This gets called from Machine::Unregister, both for the actual Machine and
10941 * the SnapshotMachine objects that might be found in the snapshots.
10942 *
10943 * Requires caller and locking. The machine lock must be passed in because it
10944 * will be passed on to detachDevice which needs it for temporary unlocking.
10945 *
10946 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
10947 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
10948 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
10949 * otherwise no media get added.
10950 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
10951 * @return
10952 */
10953HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
10954 Snapshot *pSnapshot,
10955 CleanupMode_T cleanupMode,
10956 MediaList &llMedia)
10957{
10958 Assert(isWriteLockOnCurrentThread());
10959
10960 HRESULT rc;
10961
10962 // make a temporary list because detachDevice invalidates iterators into
10963 // mMediaData->mAttachments
10964 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
10965
10966 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
10967 it != llAttachments2.end();
10968 ++it)
10969 {
10970 ComObjPtr<MediumAttachment> &pAttach = *it;
10971 ComObjPtr<Medium> pMedium = pAttach->getMedium();
10972
10973 if (!pMedium.isNull())
10974 {
10975 AutoCaller mac(pMedium);
10976 if (FAILED(mac.rc())) return mac.rc();
10977 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
10978 DeviceType_T devType = pMedium->getDeviceType();
10979 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
10980 && devType == DeviceType_HardDisk)
10981 || (cleanupMode == CleanupMode_Full)
10982 )
10983 {
10984 llMedia.push_back(pMedium);
10985 ComObjPtr<Medium> pParent = pMedium->getParent();
10986 /*
10987 * Search for medias which are not attached to any machine, but
10988 * in the chain to an attached disk. Mediums are only consided
10989 * if they are:
10990 * - have only one child
10991 * - no references to any machines
10992 * - are of normal medium type
10993 */
10994 while (!pParent.isNull())
10995 {
10996 AutoCaller mac1(pParent);
10997 if (FAILED(mac1.rc())) return mac1.rc();
10998 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
10999 if (pParent->getChildren().size() == 1)
11000 {
11001 if ( pParent->getMachineBackRefCount() == 0
11002 && pParent->getType() == MediumType_Normal
11003 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11004 llMedia.push_back(pParent);
11005 }
11006 else
11007 break;
11008 pParent = pParent->getParent();
11009 }
11010 }
11011 }
11012
11013 // real machine: then we need to use the proper method
11014 rc = detachDevice(pAttach, writeLock, pSnapshot);
11015
11016 if (FAILED(rc))
11017 return rc;
11018 }
11019
11020 return S_OK;
11021}
11022
11023/**
11024 * Perform deferred hard disk detachments.
11025 *
11026 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11027 * backed up).
11028 *
11029 * If @a aOnline is @c true then this method will also unlock the old hard disks
11030 * for which the new implicit diffs were created and will lock these new diffs for
11031 * writing.
11032 *
11033 * @param aOnline Whether the VM was online prior to this operation.
11034 *
11035 * @note Locks this object for writing!
11036 */
11037void Machine::commitMedia(bool aOnline /*= false*/)
11038{
11039 AutoCaller autoCaller(this);
11040 AssertComRCReturnVoid(autoCaller.rc());
11041
11042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11043
11044 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11045
11046 HRESULT rc = S_OK;
11047
11048 /* no attach/detach operations -- nothing to do */
11049 if (!mMediaData.isBackedUp())
11050 return;
11051
11052 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11053 bool fMediaNeedsLocking = false;
11054
11055 /* enumerate new attachments */
11056 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11057 it != mMediaData->mAttachments.end();
11058 ++it)
11059 {
11060 MediumAttachment *pAttach = *it;
11061
11062 pAttach->commit();
11063
11064 Medium* pMedium = pAttach->getMedium();
11065 bool fImplicit = pAttach->isImplicit();
11066
11067 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11068 (pMedium) ? pMedium->getName().c_str() : "NULL",
11069 fImplicit));
11070
11071 /** @todo convert all this Machine-based voodoo to MediumAttachment
11072 * based commit logic. */
11073 if (fImplicit)
11074 {
11075 /* convert implicit attachment to normal */
11076 pAttach->setImplicit(false);
11077
11078 if ( aOnline
11079 && pMedium
11080 && pAttach->getType() == DeviceType_HardDisk
11081 )
11082 {
11083 ComObjPtr<Medium> parent = pMedium->getParent();
11084 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11085
11086 /* update the appropriate lock list */
11087 MediumLockList *pMediumLockList;
11088 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11089 AssertComRC(rc);
11090 if (pMediumLockList)
11091 {
11092 /* unlock if there's a need to change the locking */
11093 if (!fMediaNeedsLocking)
11094 {
11095 rc = mData->mSession.mLockedMedia.Unlock();
11096 AssertComRC(rc);
11097 fMediaNeedsLocking = true;
11098 }
11099 rc = pMediumLockList->Update(parent, false);
11100 AssertComRC(rc);
11101 rc = pMediumLockList->Append(pMedium, true);
11102 AssertComRC(rc);
11103 }
11104 }
11105
11106 continue;
11107 }
11108
11109 if (pMedium)
11110 {
11111 /* was this medium attached before? */
11112 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11113 oldIt != oldAtts.end();
11114 ++oldIt)
11115 {
11116 MediumAttachment *pOldAttach = *oldIt;
11117 if (pOldAttach->getMedium() == pMedium)
11118 {
11119 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
11120
11121 /* yes: remove from old to avoid de-association */
11122 oldAtts.erase(oldIt);
11123 break;
11124 }
11125 }
11126 }
11127 }
11128
11129 /* enumerate remaining old attachments and de-associate from the
11130 * current machine state */
11131 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11132 it != oldAtts.end();
11133 ++it)
11134 {
11135 MediumAttachment *pAttach = *it;
11136 Medium* pMedium = pAttach->getMedium();
11137
11138 /* Detach only hard disks, since DVD/floppy media is detached
11139 * instantly in MountMedium. */
11140 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
11141 {
11142 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
11143
11144 /* now de-associate from the current machine state */
11145 rc = pMedium->removeBackReference(mData->mUuid);
11146 AssertComRC(rc);
11147
11148 if (aOnline)
11149 {
11150 /* unlock since medium is not used anymore */
11151 MediumLockList *pMediumLockList;
11152 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11153 AssertComRC(rc);
11154 if (pMediumLockList)
11155 {
11156 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11157 AssertComRC(rc);
11158 }
11159 }
11160 }
11161 }
11162
11163 /* take media locks again so that the locking state is consistent */
11164 if (fMediaNeedsLocking)
11165 {
11166 Assert(aOnline);
11167 rc = mData->mSession.mLockedMedia.Lock();
11168 AssertComRC(rc);
11169 }
11170
11171 /* commit the hard disk changes */
11172 mMediaData.commit();
11173
11174 if (isSessionMachine())
11175 {
11176 /*
11177 * Update the parent machine to point to the new owner.
11178 * This is necessary because the stored parent will point to the
11179 * session machine otherwise and cause crashes or errors later
11180 * when the session machine gets invalid.
11181 */
11182 /** @todo Change the MediumAttachment class to behave like any other
11183 * class in this regard by creating peer MediumAttachment
11184 * objects for session machines and share the data with the peer
11185 * machine.
11186 */
11187 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11188 it != mMediaData->mAttachments.end();
11189 ++it)
11190 {
11191 (*it)->updateParentMachine(mPeer);
11192 }
11193
11194 /* attach new data to the primary machine and reshare it */
11195 mPeer->mMediaData.attach(mMediaData);
11196 }
11197
11198 return;
11199}
11200
11201/**
11202 * Perform deferred deletion of implicitly created diffs.
11203 *
11204 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11205 * backed up).
11206 *
11207 * @note Locks this object for writing!
11208 */
11209void Machine::rollbackMedia()
11210{
11211 AutoCaller autoCaller(this);
11212 AssertComRCReturnVoid(autoCaller.rc());
11213
11214 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11215 LogFlowThisFunc(("Entering rollbackMedia\n"));
11216
11217 HRESULT rc = S_OK;
11218
11219 /* no attach/detach operations -- nothing to do */
11220 if (!mMediaData.isBackedUp())
11221 return;
11222
11223 /* enumerate new attachments */
11224 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11225 it != mMediaData->mAttachments.end();
11226 ++it)
11227 {
11228 MediumAttachment *pAttach = *it;
11229 /* Fix up the backrefs for DVD/floppy media. */
11230 if (pAttach->getType() != DeviceType_HardDisk)
11231 {
11232 Medium* pMedium = pAttach->getMedium();
11233 if (pMedium)
11234 {
11235 rc = pMedium->removeBackReference(mData->mUuid);
11236 AssertComRC(rc);
11237 }
11238 }
11239
11240 (*it)->rollback();
11241
11242 pAttach = *it;
11243 /* Fix up the backrefs for DVD/floppy media. */
11244 if (pAttach->getType() != DeviceType_HardDisk)
11245 {
11246 Medium* pMedium = pAttach->getMedium();
11247 if (pMedium)
11248 {
11249 rc = pMedium->addBackReference(mData->mUuid);
11250 AssertComRC(rc);
11251 }
11252 }
11253 }
11254
11255 /** @todo convert all this Machine-based voodoo to MediumAttachment
11256 * based rollback logic. */
11257 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11258
11259 return;
11260}
11261
11262/**
11263 * Returns true if the settings file is located in the directory named exactly
11264 * as the machine; this means, among other things, that the machine directory
11265 * should be auto-renamed.
11266 *
11267 * @param aSettingsDir if not NULL, the full machine settings file directory
11268 * name will be assigned there.
11269 *
11270 * @note Doesn't lock anything.
11271 * @note Not thread safe (must be called from this object's lock).
11272 */
11273bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11274{
11275 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11276 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11277 if (aSettingsDir)
11278 *aSettingsDir = strMachineDirName;
11279 strMachineDirName.stripPath(); // vmname
11280 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11281 strConfigFileOnly.stripPath() // vmname.vbox
11282 .stripExt(); // vmname
11283 /** @todo hack, make somehow use of ComposeMachineFilename */
11284 if (mUserData->s.fDirectoryIncludesUUID)
11285 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11286
11287 AssertReturn(!strMachineDirName.isEmpty(), false);
11288 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11289
11290 return strMachineDirName == strConfigFileOnly;
11291}
11292
11293/**
11294 * Discards all changes to machine settings.
11295 *
11296 * @param aNotify Whether to notify the direct session about changes or not.
11297 *
11298 * @note Locks objects for writing!
11299 */
11300void Machine::rollback(bool aNotify)
11301{
11302 AutoCaller autoCaller(this);
11303 AssertComRCReturn(autoCaller.rc(), (void)0);
11304
11305 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11306
11307 if (!mStorageControllers.isNull())
11308 {
11309 if (mStorageControllers.isBackedUp())
11310 {
11311 /* unitialize all new devices (absent in the backed up list). */
11312 StorageControllerList::const_iterator it = mStorageControllers->begin();
11313 StorageControllerList *backedList = mStorageControllers.backedUpData();
11314 while (it != mStorageControllers->end())
11315 {
11316 if ( std::find(backedList->begin(), backedList->end(), *it)
11317 == backedList->end()
11318 )
11319 {
11320 (*it)->uninit();
11321 }
11322 ++it;
11323 }
11324
11325 /* restore the list */
11326 mStorageControllers.rollback();
11327 }
11328
11329 /* rollback any changes to devices after restoring the list */
11330 if (mData->flModifications & IsModified_Storage)
11331 {
11332 StorageControllerList::const_iterator it = mStorageControllers->begin();
11333 while (it != mStorageControllers->end())
11334 {
11335 (*it)->rollback();
11336 ++it;
11337 }
11338 }
11339 }
11340
11341 mUserData.rollback();
11342
11343 mHWData.rollback();
11344
11345 if (mData->flModifications & IsModified_Storage)
11346 rollbackMedia();
11347
11348 if (mBIOSSettings)
11349 mBIOSSettings->rollback();
11350
11351 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11352 mVRDEServer->rollback();
11353
11354 if (mAudioAdapter)
11355 mAudioAdapter->rollback();
11356
11357 if (mUSBController && (mData->flModifications & IsModified_USB))
11358 mUSBController->rollback();
11359
11360 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11361 mBandwidthControl->rollback();
11362
11363 if (!mHWData.isNull())
11364 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11365 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11366 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11367 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11368
11369 if (mData->flModifications & IsModified_NetworkAdapters)
11370 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11371 if ( mNetworkAdapters[slot]
11372 && mNetworkAdapters[slot]->isModified())
11373 {
11374 mNetworkAdapters[slot]->rollback();
11375 networkAdapters[slot] = mNetworkAdapters[slot];
11376 }
11377
11378 if (mData->flModifications & IsModified_SerialPorts)
11379 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11380 if ( mSerialPorts[slot]
11381 && mSerialPorts[slot]->isModified())
11382 {
11383 mSerialPorts[slot]->rollback();
11384 serialPorts[slot] = mSerialPorts[slot];
11385 }
11386
11387 if (mData->flModifications & IsModified_ParallelPorts)
11388 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11389 if ( mParallelPorts[slot]
11390 && mParallelPorts[slot]->isModified())
11391 {
11392 mParallelPorts[slot]->rollback();
11393 parallelPorts[slot] = mParallelPorts[slot];
11394 }
11395
11396 if (aNotify)
11397 {
11398 /* inform the direct session about changes */
11399
11400 ComObjPtr<Machine> that = this;
11401 uint32_t flModifications = mData->flModifications;
11402 alock.release();
11403
11404 if (flModifications & IsModified_SharedFolders)
11405 that->onSharedFolderChange();
11406
11407 if (flModifications & IsModified_VRDEServer)
11408 that->onVRDEServerChange(/* aRestart */ TRUE);
11409 if (flModifications & IsModified_USB)
11410 that->onUSBControllerChange();
11411
11412 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
11413 if (networkAdapters[slot])
11414 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
11415 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
11416 if (serialPorts[slot])
11417 that->onSerialPortChange(serialPorts[slot]);
11418 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
11419 if (parallelPorts[slot])
11420 that->onParallelPortChange(parallelPorts[slot]);
11421
11422 if (flModifications & IsModified_Storage)
11423 that->onStorageControllerChange();
11424
11425#if 0
11426 if (flModifications & IsModified_BandwidthControl)
11427 that->onBandwidthControlChange();
11428#endif
11429 }
11430}
11431
11432/**
11433 * Commits all the changes to machine settings.
11434 *
11435 * Note that this operation is supposed to never fail.
11436 *
11437 * @note Locks this object and children for writing.
11438 */
11439void Machine::commit()
11440{
11441 AutoCaller autoCaller(this);
11442 AssertComRCReturnVoid(autoCaller.rc());
11443
11444 AutoCaller peerCaller(mPeer);
11445 AssertComRCReturnVoid(peerCaller.rc());
11446
11447 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11448
11449 /*
11450 * use safe commit to ensure Snapshot machines (that share mUserData)
11451 * will still refer to a valid memory location
11452 */
11453 mUserData.commitCopy();
11454
11455 mHWData.commit();
11456
11457 if (mMediaData.isBackedUp())
11458 commitMedia(Global::IsOnline(mData->mMachineState));
11459
11460 mBIOSSettings->commit();
11461 mVRDEServer->commit();
11462 mAudioAdapter->commit();
11463 mUSBController->commit();
11464 mBandwidthControl->commit();
11465
11466 /* Since mNetworkAdapters is a list which might have been changed (resized)
11467 * without using the Backupable<> template we need to handle the copying
11468 * of the list entries manually, including the creation of peers for the
11469 * new objects. */
11470 bool commitNetworkAdapters = false;
11471 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11472 if (mPeer)
11473 {
11474 /* commit everything, even the ones which will go away */
11475 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11476 mNetworkAdapters[slot]->commit();
11477 /* copy over the new entries, creating a peer and uninit the original */
11478 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11479 for (size_t slot = 0; slot < newSize; slot++)
11480 {
11481 /* look if this adapter has a peer device */
11482 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
11483 if (!peer)
11484 {
11485 /* no peer means the adapter is a newly created one;
11486 * create a peer owning data this data share it with */
11487 peer.createObject();
11488 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11489 }
11490 mPeer->mNetworkAdapters[slot] = peer;
11491 }
11492 /* uninit any no longer needed network adapters */
11493 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
11494 mNetworkAdapters[slot]->uninit();
11495 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
11496 {
11497 if (mPeer->mNetworkAdapters[slot])
11498 mPeer->mNetworkAdapters[slot]->uninit();
11499 }
11500 /* Keep the original network adapter count until this point, so that
11501 * discarding a chipset type change will not lose settings. */
11502 mNetworkAdapters.resize(newSize);
11503 mPeer->mNetworkAdapters.resize(newSize);
11504 }
11505 else
11506 {
11507 /* we have no peer (our parent is the newly created machine);
11508 * just commit changes to the network adapters */
11509 commitNetworkAdapters = true;
11510 }
11511 if (commitNetworkAdapters)
11512 {
11513 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11514 mNetworkAdapters[slot]->commit();
11515 }
11516
11517 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11518 mSerialPorts[slot]->commit();
11519 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11520 mParallelPorts[slot]->commit();
11521
11522 bool commitStorageControllers = false;
11523
11524 if (mStorageControllers.isBackedUp())
11525 {
11526 mStorageControllers.commit();
11527
11528 if (mPeer)
11529 {
11530 /* Commit all changes to new controllers (this will reshare data with
11531 * peers for those who have peers) */
11532 StorageControllerList *newList = new StorageControllerList();
11533 StorageControllerList::const_iterator it = mStorageControllers->begin();
11534 while (it != mStorageControllers->end())
11535 {
11536 (*it)->commit();
11537
11538 /* look if this controller has a peer device */
11539 ComObjPtr<StorageController> peer = (*it)->getPeer();
11540 if (!peer)
11541 {
11542 /* no peer means the device is a newly created one;
11543 * create a peer owning data this device share it with */
11544 peer.createObject();
11545 peer->init(mPeer, *it, true /* aReshare */);
11546 }
11547 else
11548 {
11549 /* remove peer from the old list */
11550 mPeer->mStorageControllers->remove(peer);
11551 }
11552 /* and add it to the new list */
11553 newList->push_back(peer);
11554
11555 ++it;
11556 }
11557
11558 /* uninit old peer's controllers that are left */
11559 it = mPeer->mStorageControllers->begin();
11560 while (it != mPeer->mStorageControllers->end())
11561 {
11562 (*it)->uninit();
11563 ++it;
11564 }
11565
11566 /* attach new list of controllers to our peer */
11567 mPeer->mStorageControllers.attach(newList);
11568 }
11569 else
11570 {
11571 /* we have no peer (our parent is the newly created machine);
11572 * just commit changes to devices */
11573 commitStorageControllers = true;
11574 }
11575 }
11576 else
11577 {
11578 /* the list of controllers itself is not changed,
11579 * just commit changes to controllers themselves */
11580 commitStorageControllers = true;
11581 }
11582
11583 if (commitStorageControllers)
11584 {
11585 StorageControllerList::const_iterator it = mStorageControllers->begin();
11586 while (it != mStorageControllers->end())
11587 {
11588 (*it)->commit();
11589 ++it;
11590 }
11591 }
11592
11593 if (isSessionMachine())
11594 {
11595 /* attach new data to the primary machine and reshare it */
11596 mPeer->mUserData.attach(mUserData);
11597 mPeer->mHWData.attach(mHWData);
11598 /* mMediaData is reshared by fixupMedia */
11599 // mPeer->mMediaData.attach(mMediaData);
11600 Assert(mPeer->mMediaData.data() == mMediaData.data());
11601 }
11602}
11603
11604/**
11605 * Copies all the hardware data from the given machine.
11606 *
11607 * Currently, only called when the VM is being restored from a snapshot. In
11608 * particular, this implies that the VM is not running during this method's
11609 * call.
11610 *
11611 * @note This method must be called from under this object's lock.
11612 *
11613 * @note This method doesn't call #commit(), so all data remains backed up and
11614 * unsaved.
11615 */
11616void Machine::copyFrom(Machine *aThat)
11617{
11618 AssertReturnVoid(!isSnapshotMachine());
11619 AssertReturnVoid(aThat->isSnapshotMachine());
11620
11621 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11622
11623 mHWData.assignCopy(aThat->mHWData);
11624
11625 // create copies of all shared folders (mHWData after attaching a copy
11626 // contains just references to original objects)
11627 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11628 it != mHWData->mSharedFolders.end();
11629 ++it)
11630 {
11631 ComObjPtr<SharedFolder> folder;
11632 folder.createObject();
11633 HRESULT rc = folder->initCopy(getMachine(), *it);
11634 AssertComRC(rc);
11635 *it = folder;
11636 }
11637
11638 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
11639 mVRDEServer->copyFrom(aThat->mVRDEServer);
11640 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
11641 mUSBController->copyFrom(aThat->mUSBController);
11642 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
11643
11644 /* create private copies of all controllers */
11645 mStorageControllers.backup();
11646 mStorageControllers->clear();
11647 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11648 it != aThat->mStorageControllers->end();
11649 ++it)
11650 {
11651 ComObjPtr<StorageController> ctrl;
11652 ctrl.createObject();
11653 ctrl->initCopy(this, *it);
11654 mStorageControllers->push_back(ctrl);
11655 }
11656
11657 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11658 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11659 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
11660 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11661 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
11662 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11663 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
11664}
11665
11666/**
11667 * Returns whether the given storage controller is hotplug capable.
11668 *
11669 * @returns true if the controller supports hotplugging
11670 * false otherwise.
11671 * @param enmCtrlType The controller type to check for.
11672 */
11673bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11674{
11675 switch (enmCtrlType)
11676 {
11677 case StorageControllerType_IntelAhci:
11678 return true;
11679 case StorageControllerType_LsiLogic:
11680 case StorageControllerType_LsiLogicSas:
11681 case StorageControllerType_BusLogic:
11682 case StorageControllerType_PIIX3:
11683 case StorageControllerType_PIIX4:
11684 case StorageControllerType_ICH6:
11685 case StorageControllerType_I82078:
11686 default:
11687 return false;
11688 }
11689}
11690
11691#ifdef VBOX_WITH_RESOURCE_USAGE_API
11692
11693void Machine::getDiskList(MediaList &list)
11694{
11695 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11696 it != mMediaData->mAttachments.end();
11697 ++it)
11698 {
11699 MediumAttachment* pAttach = *it;
11700 /* just in case */
11701 AssertStmt(pAttach, continue);
11702
11703 AutoCaller localAutoCallerA(pAttach);
11704 if (FAILED(localAutoCallerA.rc())) continue;
11705
11706 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11707
11708 if (pAttach->getType() == DeviceType_HardDisk)
11709 list.push_back(pAttach->getMedium());
11710 }
11711}
11712
11713void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11714{
11715 AssertReturnVoid(isWriteLockOnCurrentThread());
11716 AssertPtrReturnVoid(aCollector);
11717
11718 pm::CollectorHAL *hal = aCollector->getHAL();
11719 /* Create sub metrics */
11720 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11721 "Percentage of processor time spent in user mode by the VM process.");
11722 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11723 "Percentage of processor time spent in kernel mode by the VM process.");
11724 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11725 "Size of resident portion of VM process in memory.");
11726 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11727 "Actual size of all VM disks combined.");
11728 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11729 "Network receive rate.");
11730 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11731 "Network transmit rate.");
11732 /* Create and register base metrics */
11733 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11734 cpuLoadUser, cpuLoadKernel);
11735 aCollector->registerBaseMetric(cpuLoad);
11736 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11737 ramUsageUsed);
11738 aCollector->registerBaseMetric(ramUsage);
11739 MediaList disks;
11740 getDiskList(disks);
11741 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11742 diskUsageUsed);
11743 aCollector->registerBaseMetric(diskUsage);
11744
11745 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11746 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11747 new pm::AggregateAvg()));
11748 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11749 new pm::AggregateMin()));
11750 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11751 new pm::AggregateMax()));
11752 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11753 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11754 new pm::AggregateAvg()));
11755 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11756 new pm::AggregateMin()));
11757 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11758 new pm::AggregateMax()));
11759
11760 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11761 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11762 new pm::AggregateAvg()));
11763 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11764 new pm::AggregateMin()));
11765 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11766 new pm::AggregateMax()));
11767
11768 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11769 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11770 new pm::AggregateAvg()));
11771 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11772 new pm::AggregateMin()));
11773 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11774 new pm::AggregateMax()));
11775
11776
11777 /* Guest metrics collector */
11778 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11779 aCollector->registerGuest(mCollectorGuest);
11780 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11781 this, __PRETTY_FUNCTION__, mCollectorGuest));
11782
11783 /* Create sub metrics */
11784 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11785 "Percentage of processor time spent in user mode as seen by the guest.");
11786 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11787 "Percentage of processor time spent in kernel mode as seen by the guest.");
11788 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11789 "Percentage of processor time spent idling as seen by the guest.");
11790
11791 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11792 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11793 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11794 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11795 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11796 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11797
11798 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11799
11800 /* Create and register base metrics */
11801 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
11802 machineNetRx, machineNetTx);
11803 aCollector->registerBaseMetric(machineNetRate);
11804
11805 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11806 guestLoadUser, guestLoadKernel, guestLoadIdle);
11807 aCollector->registerBaseMetric(guestCpuLoad);
11808
11809 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11810 guestMemTotal, guestMemFree,
11811 guestMemBalloon, guestMemShared,
11812 guestMemCache, guestPagedTotal);
11813 aCollector->registerBaseMetric(guestCpuMem);
11814
11815 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
11816 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
11817 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
11818 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
11819
11820 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
11821 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
11822 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
11823 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
11824
11825 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11826 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11827 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11828 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11829
11830 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11831 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11832 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11833 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11834
11835 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11836 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11837 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11838 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11839
11840 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11841 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11842 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11843 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11844
11845 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11846 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11847 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11848 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11849
11850 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11851 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11852 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11853 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11854
11855 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11856 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11857 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11858 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11859
11860 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11861 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11862 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11863 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11864
11865 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11866 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11867 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11868 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11869}
11870
11871void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11872{
11873 AssertReturnVoid(isWriteLockOnCurrentThread());
11874
11875 if (aCollector)
11876 {
11877 aCollector->unregisterMetricsFor(aMachine);
11878 aCollector->unregisterBaseMetricsFor(aMachine);
11879 }
11880}
11881
11882#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11883
11884
11885////////////////////////////////////////////////////////////////////////////////
11886
11887DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11888
11889HRESULT SessionMachine::FinalConstruct()
11890{
11891 LogFlowThisFunc(("\n"));
11892
11893#if defined(RT_OS_WINDOWS)
11894 mIPCSem = NULL;
11895#elif defined(RT_OS_OS2)
11896 mIPCSem = NULLHANDLE;
11897#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11898 mIPCSem = -1;
11899#else
11900# error "Port me!"
11901#endif
11902
11903 return BaseFinalConstruct();
11904}
11905
11906void SessionMachine::FinalRelease()
11907{
11908 LogFlowThisFunc(("\n"));
11909
11910 uninit(Uninit::Unexpected);
11911
11912 BaseFinalRelease();
11913}
11914
11915/**
11916 * @note Must be called only by Machine::openSession() from its own write lock.
11917 */
11918HRESULT SessionMachine::init(Machine *aMachine)
11919{
11920 LogFlowThisFuncEnter();
11921 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
11922
11923 AssertReturn(aMachine, E_INVALIDARG);
11924
11925 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
11926
11927 /* Enclose the state transition NotReady->InInit->Ready */
11928 AutoInitSpan autoInitSpan(this);
11929 AssertReturn(autoInitSpan.isOk(), E_FAIL);
11930
11931 /* create the interprocess semaphore */
11932#if defined(RT_OS_WINDOWS)
11933 mIPCSemName = aMachine->mData->m_strConfigFileFull;
11934 for (size_t i = 0; i < mIPCSemName.length(); i++)
11935 if (mIPCSemName.raw()[i] == '\\')
11936 mIPCSemName.raw()[i] = '/';
11937 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
11938 ComAssertMsgRet(mIPCSem,
11939 ("Cannot create IPC mutex '%ls', err=%d",
11940 mIPCSemName.raw(), ::GetLastError()),
11941 E_FAIL);
11942#elif defined(RT_OS_OS2)
11943 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
11944 aMachine->mData->mUuid.raw());
11945 mIPCSemName = ipcSem;
11946 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
11947 ComAssertMsgRet(arc == NO_ERROR,
11948 ("Cannot create IPC mutex '%s', arc=%ld",
11949 ipcSem.c_str(), arc),
11950 E_FAIL);
11951#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11952# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11953# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
11954 /** @todo Check that this still works correctly. */
11955 AssertCompileSize(key_t, 8);
11956# else
11957 AssertCompileSize(key_t, 4);
11958# endif
11959 key_t key;
11960 mIPCSem = -1;
11961 mIPCKey = "0";
11962 for (uint32_t i = 0; i < 1 << 24; i++)
11963 {
11964 key = ((uint32_t)'V' << 24) | i;
11965 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
11966 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
11967 {
11968 mIPCSem = sem;
11969 if (sem >= 0)
11970 mIPCKey = BstrFmt("%u", key);
11971 break;
11972 }
11973 }
11974# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11975 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
11976 char *pszSemName = NULL;
11977 RTStrUtf8ToCurrentCP(&pszSemName, semName);
11978 key_t key = ::ftok(pszSemName, 'V');
11979 RTStrFree(pszSemName);
11980
11981 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
11982# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11983
11984 int errnoSave = errno;
11985 if (mIPCSem < 0 && errnoSave == ENOSYS)
11986 {
11987 setError(E_FAIL,
11988 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
11989 "support for SysV IPC. Check the host kernel configuration for "
11990 "CONFIG_SYSVIPC=y"));
11991 return E_FAIL;
11992 }
11993 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
11994 * the IPC semaphores */
11995 if (mIPCSem < 0 && errnoSave == ENOSPC)
11996 {
11997#ifdef RT_OS_LINUX
11998 setError(E_FAIL,
11999 tr("Cannot create IPC semaphore because the system limit for the "
12000 "maximum number of semaphore sets (SEMMNI), or the system wide "
12001 "maximum number of semaphores (SEMMNS) would be exceeded. The "
12002 "current set of SysV IPC semaphores can be determined from "
12003 "the file /proc/sysvipc/sem"));
12004#else
12005 setError(E_FAIL,
12006 tr("Cannot create IPC semaphore because the system-imposed limit "
12007 "on the maximum number of allowed semaphores or semaphore "
12008 "identifiers system-wide would be exceeded"));
12009#endif
12010 return E_FAIL;
12011 }
12012 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
12013 E_FAIL);
12014 /* set the initial value to 1 */
12015 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
12016 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
12017 E_FAIL);
12018#else
12019# error "Port me!"
12020#endif
12021
12022 /* memorize the peer Machine */
12023 unconst(mPeer) = aMachine;
12024 /* share the parent pointer */
12025 unconst(mParent) = aMachine->mParent;
12026
12027 /* take the pointers to data to share */
12028 mData.share(aMachine->mData);
12029 mSSData.share(aMachine->mSSData);
12030
12031 mUserData.share(aMachine->mUserData);
12032 mHWData.share(aMachine->mHWData);
12033 mMediaData.share(aMachine->mMediaData);
12034
12035 mStorageControllers.allocate();
12036 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12037 it != aMachine->mStorageControllers->end();
12038 ++it)
12039 {
12040 ComObjPtr<StorageController> ctl;
12041 ctl.createObject();
12042 ctl->init(this, *it);
12043 mStorageControllers->push_back(ctl);
12044 }
12045
12046 unconst(mBIOSSettings).createObject();
12047 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12048 /* create another VRDEServer object that will be mutable */
12049 unconst(mVRDEServer).createObject();
12050 mVRDEServer->init(this, aMachine->mVRDEServer);
12051 /* create another audio adapter object that will be mutable */
12052 unconst(mAudioAdapter).createObject();
12053 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12054 /* create a list of serial ports that will be mutable */
12055 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12056 {
12057 unconst(mSerialPorts[slot]).createObject();
12058 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12059 }
12060 /* create a list of parallel ports that will be mutable */
12061 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12062 {
12063 unconst(mParallelPorts[slot]).createObject();
12064 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12065 }
12066 /* create another USB controller object that will be mutable */
12067 unconst(mUSBController).createObject();
12068 mUSBController->init(this, aMachine->mUSBController);
12069
12070 /* create a list of network adapters that will be mutable */
12071 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12072 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12073 {
12074 unconst(mNetworkAdapters[slot]).createObject();
12075 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12076 }
12077
12078 /* create another bandwidth control object that will be mutable */
12079 unconst(mBandwidthControl).createObject();
12080 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12081
12082 /* default is to delete saved state on Saved -> PoweredOff transition */
12083 mRemoveSavedState = true;
12084
12085 /* Confirm a successful initialization when it's the case */
12086 autoInitSpan.setSucceeded();
12087
12088 LogFlowThisFuncLeave();
12089 return S_OK;
12090}
12091
12092/**
12093 * Uninitializes this session object. If the reason is other than
12094 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
12095 *
12096 * @param aReason uninitialization reason
12097 *
12098 * @note Locks mParent + this object for writing.
12099 */
12100void SessionMachine::uninit(Uninit::Reason aReason)
12101{
12102 LogFlowThisFuncEnter();
12103 LogFlowThisFunc(("reason=%d\n", aReason));
12104
12105 /*
12106 * Strongly reference ourselves to prevent this object deletion after
12107 * mData->mSession.mMachine.setNull() below (which can release the last
12108 * reference and call the destructor). Important: this must be done before
12109 * accessing any members (and before AutoUninitSpan that does it as well).
12110 * This self reference will be released as the very last step on return.
12111 */
12112 ComObjPtr<SessionMachine> selfRef = this;
12113
12114 /* Enclose the state transition Ready->InUninit->NotReady */
12115 AutoUninitSpan autoUninitSpan(this);
12116 if (autoUninitSpan.uninitDone())
12117 {
12118 LogFlowThisFunc(("Already uninitialized\n"));
12119 LogFlowThisFuncLeave();
12120 return;
12121 }
12122
12123 if (autoUninitSpan.initFailed())
12124 {
12125 /* We've been called by init() because it's failed. It's not really
12126 * necessary (nor it's safe) to perform the regular uninit sequence
12127 * below, the following is enough.
12128 */
12129 LogFlowThisFunc(("Initialization failed.\n"));
12130#if defined(RT_OS_WINDOWS)
12131 if (mIPCSem)
12132 ::CloseHandle(mIPCSem);
12133 mIPCSem = NULL;
12134#elif defined(RT_OS_OS2)
12135 if (mIPCSem != NULLHANDLE)
12136 ::DosCloseMutexSem(mIPCSem);
12137 mIPCSem = NULLHANDLE;
12138#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12139 if (mIPCSem >= 0)
12140 ::semctl(mIPCSem, 0, IPC_RMID);
12141 mIPCSem = -1;
12142# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12143 mIPCKey = "0";
12144# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12145#else
12146# error "Port me!"
12147#endif
12148 uninitDataAndChildObjects();
12149 mData.free();
12150 unconst(mParent) = NULL;
12151 unconst(mPeer) = NULL;
12152 LogFlowThisFuncLeave();
12153 return;
12154 }
12155
12156 MachineState_T lastState;
12157 {
12158 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12159 lastState = mData->mMachineState;
12160 }
12161 NOREF(lastState);
12162
12163#ifdef VBOX_WITH_USB
12164 // release all captured USB devices, but do this before requesting the locks below
12165 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12166 {
12167 /* Console::captureUSBDevices() is called in the VM process only after
12168 * setting the machine state to Starting or Restoring.
12169 * Console::detachAllUSBDevices() will be called upon successful
12170 * termination. So, we need to release USB devices only if there was
12171 * an abnormal termination of a running VM.
12172 *
12173 * This is identical to SessionMachine::DetachAllUSBDevices except
12174 * for the aAbnormal argument. */
12175 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12176 AssertComRC(rc);
12177 NOREF(rc);
12178
12179 USBProxyService *service = mParent->host()->usbProxyService();
12180 if (service)
12181 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12182 }
12183#endif /* VBOX_WITH_USB */
12184
12185 // we need to lock this object in uninit() because the lock is shared
12186 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12187 // and others need mParent lock, and USB needs host lock.
12188 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12189
12190#if 0
12191 // Trigger async cleanup tasks, avoid doing things here which are not
12192 // vital to be done immediately and maybe need more locks. This calls
12193 // Machine::unregisterMetrics().
12194 mParent->onMachineUninit(mPeer);
12195#else
12196 /*
12197 * It is safe to call Machine::unregisterMetrics() here because
12198 * PerformanceCollector::samplerCallback no longer accesses guest methods
12199 * holding the lock.
12200 */
12201 unregisterMetrics(mParent->performanceCollector(), mPeer);
12202#endif
12203 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12204 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12205 this, __PRETTY_FUNCTION__, mCollectorGuest));
12206 if (mCollectorGuest)
12207 {
12208 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
12209 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12210 mCollectorGuest = NULL;
12211 }
12212
12213 if (aReason == Uninit::Abnormal)
12214 {
12215 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12216 Global::IsOnlineOrTransient(lastState)));
12217
12218 /* reset the state to Aborted */
12219 if (mData->mMachineState != MachineState_Aborted)
12220 setMachineState(MachineState_Aborted);
12221 }
12222
12223 // any machine settings modified?
12224 if (mData->flModifications)
12225 {
12226 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12227 rollback(false /* aNotify */);
12228 }
12229
12230 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12231 || !mConsoleTaskData.mSnapshot);
12232 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12233 {
12234 LogWarningThisFunc(("canceling failed save state request!\n"));
12235 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12236 }
12237 else if (!mConsoleTaskData.mSnapshot.isNull())
12238 {
12239 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12240
12241 /* delete all differencing hard disks created (this will also attach
12242 * their parents back by rolling back mMediaData) */
12243 rollbackMedia();
12244
12245 // delete the saved state file (it might have been already created)
12246 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12247 // think it's still in use
12248 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12249 mConsoleTaskData.mSnapshot->uninit();
12250 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12251 }
12252
12253 if (!mData->mSession.mType.isEmpty())
12254 {
12255 /* mType is not null when this machine's process has been started by
12256 * Machine::LaunchVMProcess(), therefore it is our child. We
12257 * need to queue the PID to reap the process (and avoid zombies on
12258 * Linux). */
12259 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12260 mParent->addProcessToReap(mData->mSession.mPID);
12261 }
12262
12263 mData->mSession.mPID = NIL_RTPROCESS;
12264
12265 if (aReason == Uninit::Unexpected)
12266 {
12267 /* Uninitialization didn't come from #checkForDeath(), so tell the
12268 * client watcher thread to update the set of machines that have open
12269 * sessions. */
12270 mParent->updateClientWatcher();
12271 }
12272
12273 /* uninitialize all remote controls */
12274 if (mData->mSession.mRemoteControls.size())
12275 {
12276 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12277 mData->mSession.mRemoteControls.size()));
12278
12279 Data::Session::RemoteControlList::iterator it =
12280 mData->mSession.mRemoteControls.begin();
12281 while (it != mData->mSession.mRemoteControls.end())
12282 {
12283 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12284 HRESULT rc = (*it)->Uninitialize();
12285 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12286 if (FAILED(rc))
12287 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12288 ++it;
12289 }
12290 mData->mSession.mRemoteControls.clear();
12291 }
12292
12293 /*
12294 * An expected uninitialization can come only from #checkForDeath().
12295 * Otherwise it means that something's gone really wrong (for example,
12296 * the Session implementation has released the VirtualBox reference
12297 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12298 * etc). However, it's also possible, that the client releases the IPC
12299 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12300 * but the VirtualBox release event comes first to the server process.
12301 * This case is practically possible, so we should not assert on an
12302 * unexpected uninit, just log a warning.
12303 */
12304
12305 if ((aReason == Uninit::Unexpected))
12306 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12307
12308 if (aReason != Uninit::Normal)
12309 {
12310 mData->mSession.mDirectControl.setNull();
12311 }
12312 else
12313 {
12314 /* this must be null here (see #OnSessionEnd()) */
12315 Assert(mData->mSession.mDirectControl.isNull());
12316 Assert(mData->mSession.mState == SessionState_Unlocking);
12317 Assert(!mData->mSession.mProgress.isNull());
12318 }
12319 if (mData->mSession.mProgress)
12320 {
12321 if (aReason == Uninit::Normal)
12322 mData->mSession.mProgress->notifyComplete(S_OK);
12323 else
12324 mData->mSession.mProgress->notifyComplete(E_FAIL,
12325 COM_IIDOF(ISession),
12326 getComponentName(),
12327 tr("The VM session was aborted"));
12328 mData->mSession.mProgress.setNull();
12329 }
12330
12331 /* remove the association between the peer machine and this session machine */
12332 Assert( (SessionMachine*)mData->mSession.mMachine == this
12333 || aReason == Uninit::Unexpected);
12334
12335 /* reset the rest of session data */
12336 mData->mSession.mMachine.setNull();
12337 mData->mSession.mState = SessionState_Unlocked;
12338 mData->mSession.mType.setNull();
12339
12340 /* close the interprocess semaphore before leaving the exclusive lock */
12341#if defined(RT_OS_WINDOWS)
12342 if (mIPCSem)
12343 ::CloseHandle(mIPCSem);
12344 mIPCSem = NULL;
12345#elif defined(RT_OS_OS2)
12346 if (mIPCSem != NULLHANDLE)
12347 ::DosCloseMutexSem(mIPCSem);
12348 mIPCSem = NULLHANDLE;
12349#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12350 if (mIPCSem >= 0)
12351 ::semctl(mIPCSem, 0, IPC_RMID);
12352 mIPCSem = -1;
12353# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12354 mIPCKey = "0";
12355# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12356#else
12357# error "Port me!"
12358#endif
12359
12360 /* fire an event */
12361 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12362
12363 uninitDataAndChildObjects();
12364
12365 /* free the essential data structure last */
12366 mData.free();
12367
12368 /* release the exclusive lock before setting the below two to NULL */
12369 multilock.release();
12370
12371 unconst(mParent) = NULL;
12372 unconst(mPeer) = NULL;
12373
12374 LogFlowThisFuncLeave();
12375}
12376
12377// util::Lockable interface
12378////////////////////////////////////////////////////////////////////////////////
12379
12380/**
12381 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12382 * with the primary Machine instance (mPeer).
12383 */
12384RWLockHandle *SessionMachine::lockHandle() const
12385{
12386 AssertReturn(mPeer != NULL, NULL);
12387 return mPeer->lockHandle();
12388}
12389
12390// IInternalMachineControl methods
12391////////////////////////////////////////////////////////////////////////////////
12392
12393/**
12394 * Passes collected guest statistics to performance collector object
12395 */
12396STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12397 ULONG aCpuKernel, ULONG aCpuIdle,
12398 ULONG aMemTotal, ULONG aMemFree,
12399 ULONG aMemBalloon, ULONG aMemShared,
12400 ULONG aMemCache, ULONG aPageTotal,
12401 ULONG aAllocVMM, ULONG aFreeVMM,
12402 ULONG aBalloonedVMM, ULONG aSharedVMM,
12403 ULONG aVmNetRx, ULONG aVmNetTx)
12404{
12405 if (mCollectorGuest)
12406 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12407 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12408 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12409 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12410
12411 return S_OK;
12412}
12413
12414/**
12415 * @note Locks this object for writing.
12416 */
12417STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12418{
12419 AutoCaller autoCaller(this);
12420 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12421
12422 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12423
12424 mRemoveSavedState = aRemove;
12425
12426 return S_OK;
12427}
12428
12429/**
12430 * @note Locks the same as #setMachineState() does.
12431 */
12432STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12433{
12434 return setMachineState(aMachineState);
12435}
12436
12437/**
12438 * @note Locks this object for reading.
12439 */
12440STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
12441{
12442 AutoCaller autoCaller(this);
12443 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12444
12445 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12446
12447#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
12448 mIPCSemName.cloneTo(aId);
12449 return S_OK;
12450#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12451# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12452 mIPCKey.cloneTo(aId);
12453# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12454 mData->m_strConfigFileFull.cloneTo(aId);
12455# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12456 return S_OK;
12457#else
12458# error "Port me!"
12459#endif
12460}
12461
12462/**
12463 * @note Locks this object for writing.
12464 */
12465STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12466{
12467 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12468 AutoCaller autoCaller(this);
12469 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12470
12471 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12472
12473 if (mData->mSession.mState != SessionState_Locked)
12474 return VBOX_E_INVALID_OBJECT_STATE;
12475
12476 if (!mData->mSession.mProgress.isNull())
12477 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12478
12479 LogFlowThisFunc(("returns S_OK.\n"));
12480 return S_OK;
12481}
12482
12483/**
12484 * @note Locks this object for writing.
12485 */
12486STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12487{
12488 AutoCaller autoCaller(this);
12489 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12490
12491 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12492
12493 if (mData->mSession.mState != SessionState_Locked)
12494 return VBOX_E_INVALID_OBJECT_STATE;
12495
12496 /* Finalize the LaunchVMProcess progress object. */
12497 if (mData->mSession.mProgress)
12498 {
12499 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12500 mData->mSession.mProgress.setNull();
12501 }
12502
12503 if (SUCCEEDED((HRESULT)iResult))
12504 {
12505#ifdef VBOX_WITH_RESOURCE_USAGE_API
12506 /* The VM has been powered up successfully, so it makes sense
12507 * now to offer the performance metrics for a running machine
12508 * object. Doing it earlier wouldn't be safe. */
12509 registerMetrics(mParent->performanceCollector(), mPeer,
12510 mData->mSession.mPID);
12511#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12512 }
12513
12514 return S_OK;
12515}
12516
12517/**
12518 * @note Locks this object for writing.
12519 */
12520STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12521{
12522 LogFlowThisFuncEnter();
12523
12524 CheckComArgOutPointerValid(aProgress);
12525
12526 AutoCaller autoCaller(this);
12527 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12528
12529 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12530
12531 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12532 E_FAIL);
12533
12534 /* create a progress object to track operation completion */
12535 ComObjPtr<Progress> pProgress;
12536 pProgress.createObject();
12537 pProgress->init(getVirtualBox(),
12538 static_cast<IMachine *>(this) /* aInitiator */,
12539 Bstr(tr("Stopping the virtual machine")).raw(),
12540 FALSE /* aCancelable */);
12541
12542 /* fill in the console task data */
12543 mConsoleTaskData.mLastState = mData->mMachineState;
12544 mConsoleTaskData.mProgress = pProgress;
12545
12546 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12547 setMachineState(MachineState_Stopping);
12548
12549 pProgress.queryInterfaceTo(aProgress);
12550
12551 return S_OK;
12552}
12553
12554/**
12555 * @note Locks this object for writing.
12556 */
12557STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12558{
12559 LogFlowThisFuncEnter();
12560
12561 AutoCaller autoCaller(this);
12562 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12563
12564 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12565
12566 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12567 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12568 && mConsoleTaskData.mLastState != MachineState_Null,
12569 E_FAIL);
12570
12571 /*
12572 * On failure, set the state to the state we had when BeginPoweringDown()
12573 * was called (this is expected by Console::PowerDown() and the associated
12574 * task). On success the VM process already changed the state to
12575 * MachineState_PoweredOff, so no need to do anything.
12576 */
12577 if (FAILED(iResult))
12578 setMachineState(mConsoleTaskData.mLastState);
12579
12580 /* notify the progress object about operation completion */
12581 Assert(mConsoleTaskData.mProgress);
12582 if (SUCCEEDED(iResult))
12583 mConsoleTaskData.mProgress->notifyComplete(S_OK);
12584 else
12585 {
12586 Utf8Str strErrMsg(aErrMsg);
12587 if (strErrMsg.length())
12588 mConsoleTaskData.mProgress->notifyComplete(iResult,
12589 COM_IIDOF(ISession),
12590 getComponentName(),
12591 strErrMsg.c_str());
12592 else
12593 mConsoleTaskData.mProgress->notifyComplete(iResult);
12594 }
12595
12596 /* clear out the temporary saved state data */
12597 mConsoleTaskData.mLastState = MachineState_Null;
12598 mConsoleTaskData.mProgress.setNull();
12599
12600 LogFlowThisFuncLeave();
12601 return S_OK;
12602}
12603
12604
12605/**
12606 * Goes through the USB filters of the given machine to see if the given
12607 * device matches any filter or not.
12608 *
12609 * @note Locks the same as USBController::hasMatchingFilter() does.
12610 */
12611STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12612 BOOL *aMatched,
12613 ULONG *aMaskedIfs)
12614{
12615 LogFlowThisFunc(("\n"));
12616
12617 CheckComArgNotNull(aUSBDevice);
12618 CheckComArgOutPointerValid(aMatched);
12619
12620 AutoCaller autoCaller(this);
12621 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12622
12623#ifdef VBOX_WITH_USB
12624 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
12625#else
12626 NOREF(aUSBDevice);
12627 NOREF(aMaskedIfs);
12628 *aMatched = FALSE;
12629#endif
12630
12631 return S_OK;
12632}
12633
12634/**
12635 * @note Locks the same as Host::captureUSBDevice() does.
12636 */
12637STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12638{
12639 LogFlowThisFunc(("\n"));
12640
12641 AutoCaller autoCaller(this);
12642 AssertComRCReturnRC(autoCaller.rc());
12643
12644#ifdef VBOX_WITH_USB
12645 /* if captureDeviceForVM() fails, it must have set extended error info */
12646 clearError();
12647 MultiResult rc = mParent->host()->checkUSBProxyService();
12648 if (FAILED(rc)) return rc;
12649
12650 USBProxyService *service = mParent->host()->usbProxyService();
12651 AssertReturn(service, E_FAIL);
12652 return service->captureDeviceForVM(this, Guid(aId).ref());
12653#else
12654 NOREF(aId);
12655 return E_NOTIMPL;
12656#endif
12657}
12658
12659/**
12660 * @note Locks the same as Host::detachUSBDevice() does.
12661 */
12662STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12663{
12664 LogFlowThisFunc(("\n"));
12665
12666 AutoCaller autoCaller(this);
12667 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12668
12669#ifdef VBOX_WITH_USB
12670 USBProxyService *service = mParent->host()->usbProxyService();
12671 AssertReturn(service, E_FAIL);
12672 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12673#else
12674 NOREF(aId);
12675 NOREF(aDone);
12676 return E_NOTIMPL;
12677#endif
12678}
12679
12680/**
12681 * Inserts all machine filters to the USB proxy service and then calls
12682 * Host::autoCaptureUSBDevices().
12683 *
12684 * Called by Console from the VM process upon VM startup.
12685 *
12686 * @note Locks what called methods lock.
12687 */
12688STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12689{
12690 LogFlowThisFunc(("\n"));
12691
12692 AutoCaller autoCaller(this);
12693 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12694
12695#ifdef VBOX_WITH_USB
12696 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
12697 AssertComRC(rc);
12698 NOREF(rc);
12699
12700 USBProxyService *service = mParent->host()->usbProxyService();
12701 AssertReturn(service, E_FAIL);
12702 return service->autoCaptureDevicesForVM(this);
12703#else
12704 return S_OK;
12705#endif
12706}
12707
12708/**
12709 * Removes all machine filters from the USB proxy service and then calls
12710 * Host::detachAllUSBDevices().
12711 *
12712 * Called by Console from the VM process upon normal VM termination or by
12713 * SessionMachine::uninit() upon abnormal VM termination (from under the
12714 * Machine/SessionMachine lock).
12715 *
12716 * @note Locks what called methods lock.
12717 */
12718STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12719{
12720 LogFlowThisFunc(("\n"));
12721
12722 AutoCaller autoCaller(this);
12723 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12724
12725#ifdef VBOX_WITH_USB
12726 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12727 AssertComRC(rc);
12728 NOREF(rc);
12729
12730 USBProxyService *service = mParent->host()->usbProxyService();
12731 AssertReturn(service, E_FAIL);
12732 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12733#else
12734 NOREF(aDone);
12735 return S_OK;
12736#endif
12737}
12738
12739/**
12740 * @note Locks this object for writing.
12741 */
12742STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12743 IProgress **aProgress)
12744{
12745 LogFlowThisFuncEnter();
12746
12747 AssertReturn(aSession, E_INVALIDARG);
12748 AssertReturn(aProgress, E_INVALIDARG);
12749
12750 AutoCaller autoCaller(this);
12751
12752 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
12753 /*
12754 * We don't assert below because it might happen that a non-direct session
12755 * informs us it is closed right after we've been uninitialized -- it's ok.
12756 */
12757 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12758
12759 /* get IInternalSessionControl interface */
12760 ComPtr<IInternalSessionControl> control(aSession);
12761
12762 ComAssertRet(!control.isNull(), E_INVALIDARG);
12763
12764 /* Creating a Progress object requires the VirtualBox lock, and
12765 * thus locking it here is required by the lock order rules. */
12766 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12767
12768 if (control == mData->mSession.mDirectControl)
12769 {
12770 ComAssertRet(aProgress, E_POINTER);
12771
12772 /* The direct session is being normally closed by the client process
12773 * ----------------------------------------------------------------- */
12774
12775 /* go to the closing state (essential for all open*Session() calls and
12776 * for #checkForDeath()) */
12777 Assert(mData->mSession.mState == SessionState_Locked);
12778 mData->mSession.mState = SessionState_Unlocking;
12779
12780 /* set direct control to NULL to release the remote instance */
12781 mData->mSession.mDirectControl.setNull();
12782 LogFlowThisFunc(("Direct control is set to NULL\n"));
12783
12784 if (mData->mSession.mProgress)
12785 {
12786 /* finalize the progress, someone might wait if a frontend
12787 * closes the session before powering on the VM. */
12788 mData->mSession.mProgress->notifyComplete(E_FAIL,
12789 COM_IIDOF(ISession),
12790 getComponentName(),
12791 tr("The VM session was closed before any attempt to power it on"));
12792 mData->mSession.mProgress.setNull();
12793 }
12794
12795 /* Create the progress object the client will use to wait until
12796 * #checkForDeath() is called to uninitialize this session object after
12797 * it releases the IPC semaphore.
12798 * Note! Because we're "reusing" mProgress here, this must be a proxy
12799 * object just like for LaunchVMProcess. */
12800 Assert(mData->mSession.mProgress.isNull());
12801 ComObjPtr<ProgressProxy> progress;
12802 progress.createObject();
12803 ComPtr<IUnknown> pPeer(mPeer);
12804 progress->init(mParent, pPeer,
12805 Bstr(tr("Closing session")).raw(),
12806 FALSE /* aCancelable */);
12807 progress.queryInterfaceTo(aProgress);
12808 mData->mSession.mProgress = progress;
12809 }
12810 else
12811 {
12812 /* the remote session is being normally closed */
12813 Data::Session::RemoteControlList::iterator it =
12814 mData->mSession.mRemoteControls.begin();
12815 while (it != mData->mSession.mRemoteControls.end())
12816 {
12817 if (control == *it)
12818 break;
12819 ++it;
12820 }
12821 BOOL found = it != mData->mSession.mRemoteControls.end();
12822 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12823 E_INVALIDARG);
12824 // This MUST be erase(it), not remove(*it) as the latter triggers a
12825 // very nasty use after free due to the place where the value "lives".
12826 mData->mSession.mRemoteControls.erase(it);
12827 }
12828
12829 /* signal the client watcher thread, because the client is going away */
12830 mParent->updateClientWatcher();
12831
12832 LogFlowThisFuncLeave();
12833 return S_OK;
12834}
12835
12836/**
12837 * @note Locks this object for writing.
12838 */
12839STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12840{
12841 LogFlowThisFuncEnter();
12842
12843 CheckComArgOutPointerValid(aProgress);
12844 CheckComArgOutPointerValid(aStateFilePath);
12845
12846 AutoCaller autoCaller(this);
12847 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12848
12849 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12850
12851 AssertReturn( mData->mMachineState == MachineState_Paused
12852 && mConsoleTaskData.mLastState == MachineState_Null
12853 && mConsoleTaskData.strStateFilePath.isEmpty(),
12854 E_FAIL);
12855
12856 /* create a progress object to track operation completion */
12857 ComObjPtr<Progress> pProgress;
12858 pProgress.createObject();
12859 pProgress->init(getVirtualBox(),
12860 static_cast<IMachine *>(this) /* aInitiator */,
12861 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12862 FALSE /* aCancelable */);
12863
12864 Utf8Str strStateFilePath;
12865 /* stateFilePath is null when the machine is not running */
12866 if (mData->mMachineState == MachineState_Paused)
12867 composeSavedStateFilename(strStateFilePath);
12868
12869 /* fill in the console task data */
12870 mConsoleTaskData.mLastState = mData->mMachineState;
12871 mConsoleTaskData.strStateFilePath = strStateFilePath;
12872 mConsoleTaskData.mProgress = pProgress;
12873
12874 /* set the state to Saving (this is expected by Console::SaveState()) */
12875 setMachineState(MachineState_Saving);
12876
12877 strStateFilePath.cloneTo(aStateFilePath);
12878 pProgress.queryInterfaceTo(aProgress);
12879
12880 return S_OK;
12881}
12882
12883/**
12884 * @note Locks mParent + this object for writing.
12885 */
12886STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12887{
12888 LogFlowThisFunc(("\n"));
12889
12890 AutoCaller autoCaller(this);
12891 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12892
12893 /* endSavingState() need mParent lock */
12894 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12895
12896 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12897 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12898 && mConsoleTaskData.mLastState != MachineState_Null
12899 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12900 E_FAIL);
12901
12902 /*
12903 * On failure, set the state to the state we had when BeginSavingState()
12904 * was called (this is expected by Console::SaveState() and the associated
12905 * task). On success the VM process already changed the state to
12906 * MachineState_Saved, so no need to do anything.
12907 */
12908 if (FAILED(iResult))
12909 setMachineState(mConsoleTaskData.mLastState);
12910
12911 return endSavingState(iResult, aErrMsg);
12912}
12913
12914/**
12915 * @note Locks this object for writing.
12916 */
12917STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12918{
12919 LogFlowThisFunc(("\n"));
12920
12921 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12922
12923 AutoCaller autoCaller(this);
12924 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12925
12926 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12927
12928 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12929 || mData->mMachineState == MachineState_Teleported
12930 || mData->mMachineState == MachineState_Aborted
12931 , E_FAIL); /** @todo setError. */
12932
12933 Utf8Str stateFilePathFull = aSavedStateFile;
12934 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
12935 if (RT_FAILURE(vrc))
12936 return setError(VBOX_E_FILE_ERROR,
12937 tr("Invalid saved state file path '%ls' (%Rrc)"),
12938 aSavedStateFile,
12939 vrc);
12940
12941 mSSData->strStateFilePath = stateFilePathFull;
12942
12943 /* The below setMachineState() will detect the state transition and will
12944 * update the settings file */
12945
12946 return setMachineState(MachineState_Saved);
12947}
12948
12949STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
12950 ComSafeArrayOut(BSTR, aValues),
12951 ComSafeArrayOut(LONG64, aTimestamps),
12952 ComSafeArrayOut(BSTR, aFlags))
12953{
12954 LogFlowThisFunc(("\n"));
12955
12956#ifdef VBOX_WITH_GUEST_PROPS
12957 using namespace guestProp;
12958
12959 AutoCaller autoCaller(this);
12960 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12961
12962 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12963
12964 CheckComArgOutSafeArrayPointerValid(aNames);
12965 CheckComArgOutSafeArrayPointerValid(aValues);
12966 CheckComArgOutSafeArrayPointerValid(aTimestamps);
12967 CheckComArgOutSafeArrayPointerValid(aFlags);
12968
12969 size_t cEntries = mHWData->mGuestProperties.size();
12970 com::SafeArray<BSTR> names(cEntries);
12971 com::SafeArray<BSTR> values(cEntries);
12972 com::SafeArray<LONG64> timestamps(cEntries);
12973 com::SafeArray<BSTR> flags(cEntries);
12974 unsigned i = 0;
12975 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
12976 it != mHWData->mGuestProperties.end();
12977 ++it)
12978 {
12979 char szFlags[MAX_FLAGS_LEN + 1];
12980 it->first.cloneTo(&names[i]);
12981 it->second.strValue.cloneTo(&values[i]);
12982 timestamps[i] = it->second.mTimestamp;
12983 /* If it is NULL, keep it NULL. */
12984 if (it->second.mFlags)
12985 {
12986 writeFlags(it->second.mFlags, szFlags);
12987 Bstr(szFlags).cloneTo(&flags[i]);
12988 }
12989 else
12990 flags[i] = NULL;
12991 ++i;
12992 }
12993 names.detachTo(ComSafeArrayOutArg(aNames));
12994 values.detachTo(ComSafeArrayOutArg(aValues));
12995 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
12996 flags.detachTo(ComSafeArrayOutArg(aFlags));
12997 return S_OK;
12998#else
12999 ReturnComNotImplemented();
13000#endif
13001}
13002
13003STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13004 IN_BSTR aValue,
13005 LONG64 aTimestamp,
13006 IN_BSTR aFlags)
13007{
13008 LogFlowThisFunc(("\n"));
13009
13010#ifdef VBOX_WITH_GUEST_PROPS
13011 using namespace guestProp;
13012
13013 CheckComArgStrNotEmptyOrNull(aName);
13014 CheckComArgNotNull(aValue);
13015 CheckComArgNotNull(aFlags);
13016
13017 try
13018 {
13019 /*
13020 * Convert input up front.
13021 */
13022 Utf8Str utf8Name(aName);
13023 uint32_t fFlags = NILFLAG;
13024 if (aFlags)
13025 {
13026 Utf8Str utf8Flags(aFlags);
13027 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13028 AssertRCReturn(vrc, E_INVALIDARG);
13029 }
13030
13031 /*
13032 * Now grab the object lock, validate the state and do the update.
13033 */
13034 AutoCaller autoCaller(this);
13035 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13036
13037 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13038
13039 switch (mData->mMachineState)
13040 {
13041 case MachineState_Paused:
13042 case MachineState_Running:
13043 case MachineState_Teleporting:
13044 case MachineState_TeleportingPausedVM:
13045 case MachineState_LiveSnapshotting:
13046 case MachineState_DeletingSnapshotOnline:
13047 case MachineState_DeletingSnapshotPaused:
13048 case MachineState_Saving:
13049 case MachineState_Stopping:
13050 break;
13051
13052 default:
13053 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13054 VBOX_E_INVALID_VM_STATE);
13055 }
13056
13057 setModified(IsModified_MachineData);
13058 mHWData.backup();
13059
13060 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13061 if (it != mHWData->mGuestProperties.end())
13062 {
13063 if (RT_VALID_PTR(aValue) && *(aValue) != '\0')
13064 {
13065 it->second.strValue = aValue;
13066 it->second.mFlags = fFlags;
13067 it->second.mTimestamp = aTimestamp;
13068 }
13069 else
13070 mHWData->mGuestProperties.erase(it);
13071
13072 mData->mGuestPropertiesModified = TRUE;
13073 }
13074
13075 /*
13076 * Send a callback notification if appropriate
13077 */
13078 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13079 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13080 RTSTR_MAX,
13081 utf8Name.c_str(),
13082 RTSTR_MAX, NULL)
13083 )
13084 {
13085 alock.release();
13086
13087 mParent->onGuestPropertyChange(mData->mUuid,
13088 aName,
13089 aValue,
13090 aFlags);
13091 }
13092 }
13093 catch (...)
13094 {
13095 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13096 }
13097 return S_OK;
13098#else
13099 ReturnComNotImplemented();
13100#endif
13101}
13102
13103STDMETHODIMP SessionMachine::LockMedia()
13104{
13105 AutoCaller autoCaller(this);
13106 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13107
13108 AutoMultiWriteLock2 alock(this->lockHandle(),
13109 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13110
13111 AssertReturn( mData->mMachineState == MachineState_Starting
13112 || mData->mMachineState == MachineState_Restoring
13113 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13114
13115 clearError();
13116 alock.release();
13117 return lockMedia();
13118}
13119
13120STDMETHODIMP SessionMachine::UnlockMedia()
13121{
13122 unlockMedia();
13123 return S_OK;
13124}
13125
13126STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13127 IMediumAttachment **aNewAttachment)
13128{
13129 CheckComArgNotNull(aAttachment);
13130 CheckComArgOutPointerValid(aNewAttachment);
13131
13132 AutoCaller autoCaller(this);
13133 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13134
13135 // request the host lock first, since might be calling Host methods for getting host drives;
13136 // next, protect the media tree all the while we're in here, as well as our member variables
13137 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13138 this->lockHandle(),
13139 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13140
13141 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13142
13143 Bstr ctrlName;
13144 LONG lPort;
13145 LONG lDevice;
13146 bool fTempEject;
13147 {
13148 AutoCaller autoAttachCaller(this);
13149 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13150
13151 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13152
13153 /* Need to query the details first, as the IMediumAttachment reference
13154 * might be to the original settings, which we are going to change. */
13155 ctrlName = pAttach->getControllerName();
13156 lPort = pAttach->getPort();
13157 lDevice = pAttach->getDevice();
13158 fTempEject = pAttach->getTempEject();
13159 }
13160
13161 if (!fTempEject)
13162 {
13163 /* Remember previously mounted medium. The medium before taking the
13164 * backup is not necessarily the same thing. */
13165 ComObjPtr<Medium> oldmedium;
13166 oldmedium = pAttach->getMedium();
13167
13168 setModified(IsModified_Storage);
13169 mMediaData.backup();
13170
13171 // The backup operation makes the pAttach reference point to the
13172 // old settings. Re-get the correct reference.
13173 pAttach = findAttachment(mMediaData->mAttachments,
13174 ctrlName.raw(),
13175 lPort,
13176 lDevice);
13177
13178 {
13179 AutoCaller autoAttachCaller(this);
13180 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13181
13182 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13183 if (!oldmedium.isNull())
13184 oldmedium->removeBackReference(mData->mUuid);
13185
13186 pAttach->updateMedium(NULL);
13187 pAttach->updateEjected();
13188 }
13189
13190 setModified(IsModified_Storage);
13191 }
13192 else
13193 {
13194 {
13195 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13196 pAttach->updateEjected();
13197 }
13198 }
13199
13200 pAttach.queryInterfaceTo(aNewAttachment);
13201
13202 return S_OK;
13203}
13204
13205// public methods only for internal purposes
13206/////////////////////////////////////////////////////////////////////////////
13207
13208/**
13209 * Called from the client watcher thread to check for expected or unexpected
13210 * death of the client process that has a direct session to this machine.
13211 *
13212 * On Win32 and on OS/2, this method is called only when we've got the
13213 * mutex (i.e. the client has either died or terminated normally) so it always
13214 * returns @c true (the client is terminated, the session machine is
13215 * uninitialized).
13216 *
13217 * On other platforms, the method returns @c true if the client process has
13218 * terminated normally or abnormally and the session machine was uninitialized,
13219 * and @c false if the client process is still alive.
13220 *
13221 * @note Locks this object for writing.
13222 */
13223bool SessionMachine::checkForDeath()
13224{
13225 Uninit::Reason reason;
13226 bool terminated = false;
13227
13228 /* Enclose autoCaller with a block because calling uninit() from under it
13229 * will deadlock. */
13230 {
13231 AutoCaller autoCaller(this);
13232 if (!autoCaller.isOk())
13233 {
13234 /* return true if not ready, to cause the client watcher to exclude
13235 * the corresponding session from watching */
13236 LogFlowThisFunc(("Already uninitialized!\n"));
13237 return true;
13238 }
13239
13240 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13241
13242 /* Determine the reason of death: if the session state is Closing here,
13243 * everything is fine. Otherwise it means that the client did not call
13244 * OnSessionEnd() before it released the IPC semaphore. This may happen
13245 * either because the client process has abnormally terminated, or
13246 * because it simply forgot to call ISession::Close() before exiting. We
13247 * threat the latter also as an abnormal termination (see
13248 * Session::uninit() for details). */
13249 reason = mData->mSession.mState == SessionState_Unlocking ?
13250 Uninit::Normal :
13251 Uninit::Abnormal;
13252
13253#if defined(RT_OS_WINDOWS)
13254
13255 AssertMsg(mIPCSem, ("semaphore must be created"));
13256
13257 /* release the IPC mutex */
13258 ::ReleaseMutex(mIPCSem);
13259
13260 terminated = true;
13261
13262#elif defined(RT_OS_OS2)
13263
13264 AssertMsg(mIPCSem, ("semaphore must be created"));
13265
13266 /* release the IPC mutex */
13267 ::DosReleaseMutexSem(mIPCSem);
13268
13269 terminated = true;
13270
13271#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
13272
13273 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
13274
13275 int val = ::semctl(mIPCSem, 0, GETVAL);
13276 if (val > 0)
13277 {
13278 /* the semaphore is signaled, meaning the session is terminated */
13279 terminated = true;
13280 }
13281
13282#else
13283# error "Port me!"
13284#endif
13285
13286 } /* AutoCaller block */
13287
13288 if (terminated)
13289 uninit(reason);
13290
13291 return terminated;
13292}
13293
13294/**
13295 * @note Locks this object for reading.
13296 */
13297HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13298{
13299 LogFlowThisFunc(("\n"));
13300
13301 AutoCaller autoCaller(this);
13302 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13303
13304 ComPtr<IInternalSessionControl> directControl;
13305 {
13306 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13307 directControl = mData->mSession.mDirectControl;
13308 }
13309
13310 /* ignore notifications sent after #OnSessionEnd() is called */
13311 if (!directControl)
13312 return S_OK;
13313
13314 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13315}
13316
13317/**
13318 * @note Locks this object for reading.
13319 */
13320HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13321 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
13322{
13323 LogFlowThisFunc(("\n"));
13324
13325 AutoCaller autoCaller(this);
13326 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13327
13328 ComPtr<IInternalSessionControl> directControl;
13329 {
13330 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13331 directControl = mData->mSession.mDirectControl;
13332 }
13333
13334 /* ignore notifications sent after #OnSessionEnd() is called */
13335 if (!directControl)
13336 return S_OK;
13337 /*
13338 * instead acting like callback we ask IVirtualBox deliver corresponding event
13339 */
13340
13341 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13342 return S_OK;
13343}
13344
13345/**
13346 * @note Locks this object for reading.
13347 */
13348HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
13349{
13350 LogFlowThisFunc(("\n"));
13351
13352 AutoCaller autoCaller(this);
13353 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13354
13355 ComPtr<IInternalSessionControl> directControl;
13356 {
13357 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13358 directControl = mData->mSession.mDirectControl;
13359 }
13360
13361 /* ignore notifications sent after #OnSessionEnd() is called */
13362 if (!directControl)
13363 return S_OK;
13364
13365 return directControl->OnSerialPortChange(serialPort);
13366}
13367
13368/**
13369 * @note Locks this object for reading.
13370 */
13371HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
13372{
13373 LogFlowThisFunc(("\n"));
13374
13375 AutoCaller autoCaller(this);
13376 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13377
13378 ComPtr<IInternalSessionControl> directControl;
13379 {
13380 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13381 directControl = mData->mSession.mDirectControl;
13382 }
13383
13384 /* ignore notifications sent after #OnSessionEnd() is called */
13385 if (!directControl)
13386 return S_OK;
13387
13388 return directControl->OnParallelPortChange(parallelPort);
13389}
13390
13391/**
13392 * @note Locks this object for reading.
13393 */
13394HRESULT SessionMachine::onStorageControllerChange()
13395{
13396 LogFlowThisFunc(("\n"));
13397
13398 AutoCaller autoCaller(this);
13399 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13400
13401 ComPtr<IInternalSessionControl> directControl;
13402 {
13403 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13404 directControl = mData->mSession.mDirectControl;
13405 }
13406
13407 /* ignore notifications sent after #OnSessionEnd() is called */
13408 if (!directControl)
13409 return S_OK;
13410
13411 return directControl->OnStorageControllerChange();
13412}
13413
13414/**
13415 * @note Locks this object for reading.
13416 */
13417HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13418{
13419 LogFlowThisFunc(("\n"));
13420
13421 AutoCaller autoCaller(this);
13422 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13423
13424 ComPtr<IInternalSessionControl> directControl;
13425 {
13426 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13427 directControl = mData->mSession.mDirectControl;
13428 }
13429
13430 /* ignore notifications sent after #OnSessionEnd() is called */
13431 if (!directControl)
13432 return S_OK;
13433
13434 return directControl->OnMediumChange(aAttachment, aForce);
13435}
13436
13437/**
13438 * @note Locks this object for reading.
13439 */
13440HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
13441{
13442 LogFlowThisFunc(("\n"));
13443
13444 AutoCaller autoCaller(this);
13445 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13446
13447 ComPtr<IInternalSessionControl> directControl;
13448 {
13449 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13450 directControl = mData->mSession.mDirectControl;
13451 }
13452
13453 /* ignore notifications sent after #OnSessionEnd() is called */
13454 if (!directControl)
13455 return S_OK;
13456
13457 return directControl->OnCPUChange(aCPU, aRemove);
13458}
13459
13460HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
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->OnCPUExecutionCapChange(aExecutionCap);
13478}
13479
13480/**
13481 * @note Locks this object for reading.
13482 */
13483HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
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->OnVRDEServerChange(aRestart);
13501}
13502
13503/**
13504 * @note Locks this object for reading.
13505 */
13506HRESULT SessionMachine::onUSBControllerChange()
13507{
13508 LogFlowThisFunc(("\n"));
13509
13510 AutoCaller autoCaller(this);
13511 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13512
13513 ComPtr<IInternalSessionControl> directControl;
13514 {
13515 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13516 directControl = mData->mSession.mDirectControl;
13517 }
13518
13519 /* ignore notifications sent after #OnSessionEnd() is called */
13520 if (!directControl)
13521 return S_OK;
13522
13523 return directControl->OnUSBControllerChange();
13524}
13525
13526/**
13527 * @note Locks this object for reading.
13528 */
13529HRESULT SessionMachine::onSharedFolderChange()
13530{
13531 LogFlowThisFunc(("\n"));
13532
13533 AutoCaller autoCaller(this);
13534 AssertComRCReturnRC(autoCaller.rc());
13535
13536 ComPtr<IInternalSessionControl> directControl;
13537 {
13538 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13539 directControl = mData->mSession.mDirectControl;
13540 }
13541
13542 /* ignore notifications sent after #OnSessionEnd() is called */
13543 if (!directControl)
13544 return S_OK;
13545
13546 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13547}
13548
13549/**
13550 * @note Locks this object for reading.
13551 */
13552HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
13553{
13554 LogFlowThisFunc(("\n"));
13555
13556 AutoCaller autoCaller(this);
13557 AssertComRCReturnRC(autoCaller.rc());
13558
13559 ComPtr<IInternalSessionControl> directControl;
13560 {
13561 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13562 directControl = mData->mSession.mDirectControl;
13563 }
13564
13565 /* ignore notifications sent after #OnSessionEnd() is called */
13566 if (!directControl)
13567 return S_OK;
13568
13569 return directControl->OnClipboardModeChange(aClipboardMode);
13570}
13571
13572/**
13573 * @note Locks this object for reading.
13574 */
13575HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
13576{
13577 LogFlowThisFunc(("\n"));
13578
13579 AutoCaller autoCaller(this);
13580 AssertComRCReturnRC(autoCaller.rc());
13581
13582 ComPtr<IInternalSessionControl> directControl;
13583 {
13584 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13585 directControl = mData->mSession.mDirectControl;
13586 }
13587
13588 /* ignore notifications sent after #OnSessionEnd() is called */
13589 if (!directControl)
13590 return S_OK;
13591
13592 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
13593}
13594
13595/**
13596 * @note Locks this object for reading.
13597 */
13598HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13599{
13600 LogFlowThisFunc(("\n"));
13601
13602 AutoCaller autoCaller(this);
13603 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13604
13605 ComPtr<IInternalSessionControl> directControl;
13606 {
13607 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13608 directControl = mData->mSession.mDirectControl;
13609 }
13610
13611 /* ignore notifications sent after #OnSessionEnd() is called */
13612 if (!directControl)
13613 return S_OK;
13614
13615 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13616}
13617
13618/**
13619 * @note Locks this object for reading.
13620 */
13621HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13622{
13623 LogFlowThisFunc(("\n"));
13624
13625 AutoCaller autoCaller(this);
13626 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13627
13628 ComPtr<IInternalSessionControl> directControl;
13629 {
13630 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13631 directControl = mData->mSession.mDirectControl;
13632 }
13633
13634 /* ignore notifications sent after #OnSessionEnd() is called */
13635 if (!directControl)
13636 return S_OK;
13637
13638 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13639}
13640
13641/**
13642 * Returns @c true if this machine's USB controller reports it has a matching
13643 * filter for the given USB device and @c false otherwise.
13644 *
13645 * @note locks this object for reading.
13646 */
13647bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13648{
13649 AutoCaller autoCaller(this);
13650 /* silently return if not ready -- this method may be called after the
13651 * direct machine session has been called */
13652 if (!autoCaller.isOk())
13653 return false;
13654
13655#ifdef VBOX_WITH_USB
13656 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13657
13658 switch (mData->mMachineState)
13659 {
13660 case MachineState_Starting:
13661 case MachineState_Restoring:
13662 case MachineState_TeleportingIn:
13663 case MachineState_Paused:
13664 case MachineState_Running:
13665 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13666 * elsewhere... */
13667 alock.release();
13668 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
13669 default: break;
13670 }
13671#else
13672 NOREF(aDevice);
13673 NOREF(aMaskedIfs);
13674#endif
13675 return false;
13676}
13677
13678/**
13679 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13680 */
13681HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
13682 IVirtualBoxErrorInfo *aError,
13683 ULONG aMaskedIfs)
13684{
13685 LogFlowThisFunc(("\n"));
13686
13687 AutoCaller autoCaller(this);
13688
13689 /* This notification may happen after the machine object has been
13690 * uninitialized (the session was closed), so don't assert. */
13691 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13692
13693 ComPtr<IInternalSessionControl> directControl;
13694 {
13695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13696 directControl = mData->mSession.mDirectControl;
13697 }
13698
13699 /* fail on notifications sent after #OnSessionEnd() is called, it is
13700 * expected by the caller */
13701 if (!directControl)
13702 return E_FAIL;
13703
13704 /* No locks should be held at this point. */
13705 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13706 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13707
13708 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13709}
13710
13711/**
13712 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13713 */
13714HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
13715 IVirtualBoxErrorInfo *aError)
13716{
13717 LogFlowThisFunc(("\n"));
13718
13719 AutoCaller autoCaller(this);
13720
13721 /* This notification may happen after the machine object has been
13722 * uninitialized (the session was closed), so don't assert. */
13723 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13724
13725 ComPtr<IInternalSessionControl> directControl;
13726 {
13727 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13728 directControl = mData->mSession.mDirectControl;
13729 }
13730
13731 /* fail on notifications sent after #OnSessionEnd() is called, it is
13732 * expected by the caller */
13733 if (!directControl)
13734 return E_FAIL;
13735
13736 /* No locks should be held at this point. */
13737 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13738 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13739
13740 return directControl->OnUSBDeviceDetach(aId, aError);
13741}
13742
13743// protected methods
13744/////////////////////////////////////////////////////////////////////////////
13745
13746/**
13747 * Helper method to finalize saving the state.
13748 *
13749 * @note Must be called from under this object's lock.
13750 *
13751 * @param aRc S_OK if the snapshot has been taken successfully
13752 * @param aErrMsg human readable error message for failure
13753 *
13754 * @note Locks mParent + this objects for writing.
13755 */
13756HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13757{
13758 LogFlowThisFuncEnter();
13759
13760 AutoCaller autoCaller(this);
13761 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13762
13763 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13764
13765 HRESULT rc = S_OK;
13766
13767 if (SUCCEEDED(aRc))
13768 {
13769 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13770
13771 /* save all VM settings */
13772 rc = saveSettings(NULL);
13773 // no need to check whether VirtualBox.xml needs saving also since
13774 // we can't have a name change pending at this point
13775 }
13776 else
13777 {
13778 // delete the saved state file (it might have been already created);
13779 // we need not check whether this is shared with a snapshot here because
13780 // we certainly created this saved state file here anew
13781 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13782 }
13783
13784 /* notify the progress object about operation completion */
13785 Assert(mConsoleTaskData.mProgress);
13786 if (SUCCEEDED(aRc))
13787 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13788 else
13789 {
13790 if (aErrMsg.length())
13791 mConsoleTaskData.mProgress->notifyComplete(aRc,
13792 COM_IIDOF(ISession),
13793 getComponentName(),
13794 aErrMsg.c_str());
13795 else
13796 mConsoleTaskData.mProgress->notifyComplete(aRc);
13797 }
13798
13799 /* clear out the temporary saved state data */
13800 mConsoleTaskData.mLastState = MachineState_Null;
13801 mConsoleTaskData.strStateFilePath.setNull();
13802 mConsoleTaskData.mProgress.setNull();
13803
13804 LogFlowThisFuncLeave();
13805 return rc;
13806}
13807
13808/**
13809 * Deletes the given file if it is no longer in use by either the current machine state
13810 * (if the machine is "saved") or any of the machine's snapshots.
13811 *
13812 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13813 * but is different for each SnapshotMachine. When calling this, the order of calling this
13814 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13815 * is therefore critical. I know, it's all rather messy.
13816 *
13817 * @param strStateFile
13818 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
13819 */
13820void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
13821 Snapshot *pSnapshotToIgnore)
13822{
13823 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13824 if ( (strStateFile.isNotEmpty())
13825 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13826 )
13827 // ... and it must also not be shared with other snapshots
13828 if ( !mData->mFirstSnapshot
13829 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13830 // this checks the SnapshotMachine's state file paths
13831 )
13832 RTFileDelete(strStateFile.c_str());
13833}
13834
13835/**
13836 * Locks the attached media.
13837 *
13838 * All attached hard disks are locked for writing and DVD/floppy are locked for
13839 * reading. Parents of attached hard disks (if any) are locked for reading.
13840 *
13841 * This method also performs accessibility check of all media it locks: if some
13842 * media is inaccessible, the method will return a failure and a bunch of
13843 * extended error info objects per each inaccessible medium.
13844 *
13845 * Note that this method is atomic: if it returns a success, all media are
13846 * locked as described above; on failure no media is locked at all (all
13847 * succeeded individual locks will be undone).
13848 *
13849 * The caller is responsible for doing the necessary state sanity checks.
13850 *
13851 * The locks made by this method must be undone by calling #unlockMedia() when
13852 * no more needed.
13853 */
13854HRESULT SessionMachine::lockMedia()
13855{
13856 AutoCaller autoCaller(this);
13857 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13858
13859 AutoMultiWriteLock2 alock(this->lockHandle(),
13860 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13861
13862 /* bail out if trying to lock things with already set up locking */
13863 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13864
13865 MultiResult mrc(S_OK);
13866
13867 /* Collect locking information for all medium objects attached to the VM. */
13868 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13869 it != mMediaData->mAttachments.end();
13870 ++it)
13871 {
13872 MediumAttachment* pAtt = *it;
13873 DeviceType_T devType = pAtt->getType();
13874 Medium *pMedium = pAtt->getMedium();
13875
13876 MediumLockList *pMediumLockList(new MediumLockList());
13877 // There can be attachments without a medium (floppy/dvd), and thus
13878 // it's impossible to create a medium lock list. It still makes sense
13879 // to have the empty medium lock list in the map in case a medium is
13880 // attached later.
13881 if (pMedium != NULL)
13882 {
13883 MediumType_T mediumType = pMedium->getType();
13884 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13885 || mediumType == MediumType_Shareable;
13886 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13887
13888 alock.release();
13889 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13890 !fIsReadOnlyLock /* fMediumLockWrite */,
13891 NULL,
13892 *pMediumLockList);
13893 alock.acquire();
13894 if (FAILED(mrc))
13895 {
13896 delete pMediumLockList;
13897 mData->mSession.mLockedMedia.Clear();
13898 break;
13899 }
13900 }
13901
13902 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13903 if (FAILED(rc))
13904 {
13905 mData->mSession.mLockedMedia.Clear();
13906 mrc = setError(rc,
13907 tr("Collecting locking information for all attached media failed"));
13908 break;
13909 }
13910 }
13911
13912 if (SUCCEEDED(mrc))
13913 {
13914 /* Now lock all media. If this fails, nothing is locked. */
13915 alock.release();
13916 HRESULT rc = mData->mSession.mLockedMedia.Lock();
13917 alock.acquire();
13918 if (FAILED(rc))
13919 {
13920 mrc = setError(rc,
13921 tr("Locking of attached media failed"));
13922 }
13923 }
13924
13925 return mrc;
13926}
13927
13928/**
13929 * Undoes the locks made by by #lockMedia().
13930 */
13931void SessionMachine::unlockMedia()
13932{
13933 AutoCaller autoCaller(this);
13934 AssertComRCReturnVoid(autoCaller.rc());
13935
13936 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13937
13938 /* we may be holding important error info on the current thread;
13939 * preserve it */
13940 ErrorInfoKeeper eik;
13941
13942 HRESULT rc = mData->mSession.mLockedMedia.Clear();
13943 AssertComRC(rc);
13944}
13945
13946/**
13947 * Helper to change the machine state (reimplementation).
13948 *
13949 * @note Locks this object for writing.
13950 * @note This method must not call saveSettings or SaveSettings, otherwise
13951 * it can cause crashes in random places due to unexpectedly committing
13952 * the current settings. The caller is responsible for that. The call
13953 * to saveStateSettings is fine, because this method does not commit.
13954 */
13955HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
13956{
13957 LogFlowThisFuncEnter();
13958 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
13959
13960 AutoCaller autoCaller(this);
13961 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13962
13963 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13964
13965 MachineState_T oldMachineState = mData->mMachineState;
13966
13967 AssertMsgReturn(oldMachineState != aMachineState,
13968 ("oldMachineState=%s, aMachineState=%s\n",
13969 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
13970 E_FAIL);
13971
13972 HRESULT rc = S_OK;
13973
13974 int stsFlags = 0;
13975 bool deleteSavedState = false;
13976
13977 /* detect some state transitions */
13978
13979 if ( ( oldMachineState == MachineState_Saved
13980 && aMachineState == MachineState_Restoring)
13981 || ( ( oldMachineState == MachineState_PoweredOff
13982 || oldMachineState == MachineState_Teleported
13983 || oldMachineState == MachineState_Aborted
13984 )
13985 && ( aMachineState == MachineState_TeleportingIn
13986 || aMachineState == MachineState_Starting
13987 )
13988 )
13989 )
13990 {
13991 /* The EMT thread is about to start */
13992
13993 /* Nothing to do here for now... */
13994
13995 /// @todo NEWMEDIA don't let mDVDDrive and other children
13996 /// change anything when in the Starting/Restoring state
13997 }
13998 else if ( ( oldMachineState == MachineState_Running
13999 || oldMachineState == MachineState_Paused
14000 || oldMachineState == MachineState_Teleporting
14001 || oldMachineState == MachineState_LiveSnapshotting
14002 || oldMachineState == MachineState_Stuck
14003 || oldMachineState == MachineState_Starting
14004 || oldMachineState == MachineState_Stopping
14005 || oldMachineState == MachineState_Saving
14006 || oldMachineState == MachineState_Restoring
14007 || oldMachineState == MachineState_TeleportingPausedVM
14008 || oldMachineState == MachineState_TeleportingIn
14009 )
14010 && ( aMachineState == MachineState_PoweredOff
14011 || aMachineState == MachineState_Saved
14012 || aMachineState == MachineState_Teleported
14013 || aMachineState == MachineState_Aborted
14014 )
14015 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14016 * snapshot */
14017 && ( mConsoleTaskData.mSnapshot.isNull()
14018 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14019 )
14020 )
14021 {
14022 /* The EMT thread has just stopped, unlock attached media. Note that as
14023 * opposed to locking that is done from Console, we do unlocking here
14024 * because the VM process may have aborted before having a chance to
14025 * properly unlock all media it locked. */
14026
14027 unlockMedia();
14028 }
14029
14030 if (oldMachineState == MachineState_Restoring)
14031 {
14032 if (aMachineState != MachineState_Saved)
14033 {
14034 /*
14035 * delete the saved state file once the machine has finished
14036 * restoring from it (note that Console sets the state from
14037 * Restoring to Saved if the VM couldn't restore successfully,
14038 * to give the user an ability to fix an error and retry --
14039 * we keep the saved state file in this case)
14040 */
14041 deleteSavedState = true;
14042 }
14043 }
14044 else if ( oldMachineState == MachineState_Saved
14045 && ( aMachineState == MachineState_PoweredOff
14046 || aMachineState == MachineState_Aborted
14047 || aMachineState == MachineState_Teleported
14048 )
14049 )
14050 {
14051 /*
14052 * delete the saved state after Console::ForgetSavedState() is called
14053 * or if the VM process (owning a direct VM session) crashed while the
14054 * VM was Saved
14055 */
14056
14057 /// @todo (dmik)
14058 // Not sure that deleting the saved state file just because of the
14059 // client death before it attempted to restore the VM is a good
14060 // thing. But when it crashes we need to go to the Aborted state
14061 // which cannot have the saved state file associated... The only
14062 // way to fix this is to make the Aborted condition not a VM state
14063 // but a bool flag: i.e., when a crash occurs, set it to true and
14064 // change the state to PoweredOff or Saved depending on the
14065 // saved state presence.
14066
14067 deleteSavedState = true;
14068 mData->mCurrentStateModified = TRUE;
14069 stsFlags |= SaveSTS_CurStateModified;
14070 }
14071
14072 if ( aMachineState == MachineState_Starting
14073 || aMachineState == MachineState_Restoring
14074 || aMachineState == MachineState_TeleportingIn
14075 )
14076 {
14077 /* set the current state modified flag to indicate that the current
14078 * state is no more identical to the state in the
14079 * current snapshot */
14080 if (!mData->mCurrentSnapshot.isNull())
14081 {
14082 mData->mCurrentStateModified = TRUE;
14083 stsFlags |= SaveSTS_CurStateModified;
14084 }
14085 }
14086
14087 if (deleteSavedState)
14088 {
14089 if (mRemoveSavedState)
14090 {
14091 Assert(!mSSData->strStateFilePath.isEmpty());
14092
14093 // it is safe to delete the saved state file if ...
14094 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14095 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14096 // ... none of the snapshots share the saved state file
14097 )
14098 RTFileDelete(mSSData->strStateFilePath.c_str());
14099 }
14100
14101 mSSData->strStateFilePath.setNull();
14102 stsFlags |= SaveSTS_StateFilePath;
14103 }
14104
14105 /* redirect to the underlying peer machine */
14106 mPeer->setMachineState(aMachineState);
14107
14108 if ( aMachineState == MachineState_PoweredOff
14109 || aMachineState == MachineState_Teleported
14110 || aMachineState == MachineState_Aborted
14111 || aMachineState == MachineState_Saved)
14112 {
14113 /* the machine has stopped execution
14114 * (or the saved state file was adopted) */
14115 stsFlags |= SaveSTS_StateTimeStamp;
14116 }
14117
14118 if ( ( oldMachineState == MachineState_PoweredOff
14119 || oldMachineState == MachineState_Aborted
14120 || oldMachineState == MachineState_Teleported
14121 )
14122 && aMachineState == MachineState_Saved)
14123 {
14124 /* the saved state file was adopted */
14125 Assert(!mSSData->strStateFilePath.isEmpty());
14126 stsFlags |= SaveSTS_StateFilePath;
14127 }
14128
14129#ifdef VBOX_WITH_GUEST_PROPS
14130 if ( aMachineState == MachineState_PoweredOff
14131 || aMachineState == MachineState_Aborted
14132 || aMachineState == MachineState_Teleported)
14133 {
14134 /* Make sure any transient guest properties get removed from the
14135 * property store on shutdown. */
14136
14137 HWData::GuestPropertyMap::const_iterator it;
14138 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14139 if (!fNeedsSaving)
14140 for (it = mHWData->mGuestProperties.begin();
14141 it != mHWData->mGuestProperties.end(); ++it)
14142 if ( (it->second.mFlags & guestProp::TRANSIENT)
14143 || (it->second.mFlags & guestProp::TRANSRESET))
14144 {
14145 fNeedsSaving = true;
14146 break;
14147 }
14148 if (fNeedsSaving)
14149 {
14150 mData->mCurrentStateModified = TRUE;
14151 stsFlags |= SaveSTS_CurStateModified;
14152 }
14153 }
14154#endif
14155
14156 rc = saveStateSettings(stsFlags);
14157
14158 if ( ( oldMachineState != MachineState_PoweredOff
14159 && oldMachineState != MachineState_Aborted
14160 && oldMachineState != MachineState_Teleported
14161 )
14162 && ( aMachineState == MachineState_PoweredOff
14163 || aMachineState == MachineState_Aborted
14164 || aMachineState == MachineState_Teleported
14165 )
14166 )
14167 {
14168 /* we've been shut down for any reason */
14169 /* no special action so far */
14170 }
14171
14172 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14173 LogFlowThisFuncLeave();
14174 return rc;
14175}
14176
14177/**
14178 * Sends the current machine state value to the VM process.
14179 *
14180 * @note Locks this object for reading, then calls a client process.
14181 */
14182HRESULT SessionMachine::updateMachineStateOnClient()
14183{
14184 AutoCaller autoCaller(this);
14185 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14186
14187 ComPtr<IInternalSessionControl> directControl;
14188 {
14189 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14190 AssertReturn(!!mData, E_FAIL);
14191 directControl = mData->mSession.mDirectControl;
14192
14193 /* directControl may be already set to NULL here in #OnSessionEnd()
14194 * called too early by the direct session process while there is still
14195 * some operation (like deleting the snapshot) in progress. The client
14196 * process in this case is waiting inside Session::close() for the
14197 * "end session" process object to complete, while #uninit() called by
14198 * #checkForDeath() on the Watcher thread is waiting for the pending
14199 * operation to complete. For now, we accept this inconsistent behavior
14200 * and simply do nothing here. */
14201
14202 if (mData->mSession.mState == SessionState_Unlocking)
14203 return S_OK;
14204
14205 AssertReturn(!directControl.isNull(), E_FAIL);
14206 }
14207
14208 return directControl->UpdateMachineState(mData->mMachineState);
14209}
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