VirtualBox

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

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

Main/EncodeAndVideoRecording Module and API implementation: Integrating an independent encoding and video recording module that will serve all the frontends.
Introducing settings settings and API implementation for accessing and modifying video recording parameters:
->target video capture file
->video capture width
->video capture height
->enable video capturing

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 461.5 KB
Line 
1/* $Id: MachineImpl.cpp 42838 2012-08-16 09:21:09Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2012 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 = true;
172
173 mHWVirtExEnabled = true;
174 mHWVirtExNestedPagingEnabled = true;
175#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
176 mHWVirtExLargePagesEnabled = true;
177#else
178 /* Not supported on 32 bits hosts. */
179 mHWVirtExLargePagesEnabled = false;
180#endif
181 mHWVirtExVPIDEnabled = true;
182 mHWVirtExForceEnabled = false;
183#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
184 mHWVirtExExclusive = false;
185#else
186 mHWVirtExExclusive = true;
187#endif
188#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
189 mPAEEnabled = true;
190#else
191 mPAEEnabled = false;
192#endif
193 mSyntheticCpu = false;
194 mHPETEnabled = false;
195
196 /* default boot order: floppy - DVD - HDD */
197 mBootOrder[0] = DeviceType_Floppy;
198 mBootOrder[1] = DeviceType_DVD;
199 mBootOrder[2] = DeviceType_HardDisk;
200 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
201 mBootOrder[i] = DeviceType_Null;
202
203 mClipboardMode = ClipboardMode_Disabled;
204 mDragAndDropMode = DragAndDropMode_Disabled;
205 mGuestPropertyNotificationPatterns = "";
206
207 mFirmwareType = FirmwareType_BIOS;
208 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
209 mPointingHIDType = PointingHIDType_PS2Mouse;
210 mChipsetType = ChipsetType_PIIX3;
211 mEmulatedUSBCardReaderEnabled = FALSE;
212
213 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
214 mCPUAttached[i] = false;
215
216 mIOCacheEnabled = true;
217 mIOCacheSize = 5; /* 5MB */
218
219 /* Maximum CPU execution cap by default. */
220 mCpuExecutionCap = 100;
221}
222
223Machine::HWData::~HWData()
224{
225}
226
227/////////////////////////////////////////////////////////////////////////////
228// Machine::HDData structure
229/////////////////////////////////////////////////////////////////////////////
230
231Machine::MediaData::MediaData()
232{
233}
234
235Machine::MediaData::~MediaData()
236{
237}
238
239/////////////////////////////////////////////////////////////////////////////
240// Machine class
241/////////////////////////////////////////////////////////////////////////////
242
243// constructor / destructor
244/////////////////////////////////////////////////////////////////////////////
245
246Machine::Machine()
247 : mCollectorGuest(NULL),
248 mPeer(NULL),
249 mParent(NULL),
250 mSerialPorts(),
251 mParallelPorts(),
252 uRegistryNeedsSaving(0)
253{}
254
255Machine::~Machine()
256{}
257
258HRESULT Machine::FinalConstruct()
259{
260 LogFlowThisFunc(("\n"));
261 return BaseFinalConstruct();
262}
263
264void Machine::FinalRelease()
265{
266 LogFlowThisFunc(("\n"));
267 uninit();
268 BaseFinalRelease();
269}
270
271/**
272 * Initializes a new machine instance; this init() variant creates a new, empty machine.
273 * This gets called from VirtualBox::CreateMachine().
274 *
275 * @param aParent Associated parent object
276 * @param strConfigFile Local file system path to the VM settings file (can
277 * be relative to the VirtualBox config directory).
278 * @param strName name for the machine
279 * @param llGroups list of groups for the machine
280 * @param aOsType OS Type of this machine or NULL.
281 * @param aId UUID for the new machine.
282 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
283 *
284 * @return Success indicator. if not S_OK, the machine object is invalid
285 */
286HRESULT Machine::init(VirtualBox *aParent,
287 const Utf8Str &strConfigFile,
288 const Utf8Str &strName,
289 const StringsList &llGroups,
290 GuestOSType *aOsType,
291 const Guid &aId,
292 bool fForceOverwrite)
293{
294 LogFlowThisFuncEnter();
295 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
296
297 /* Enclose the state transition NotReady->InInit->Ready */
298 AutoInitSpan autoInitSpan(this);
299 AssertReturn(autoInitSpan.isOk(), E_FAIL);
300
301 HRESULT rc = initImpl(aParent, strConfigFile);
302 if (FAILED(rc)) return rc;
303
304 rc = tryCreateMachineConfigFile(fForceOverwrite);
305 if (FAILED(rc)) return rc;
306
307 if (SUCCEEDED(rc))
308 {
309 // create an empty machine config
310 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
311
312 rc = initDataAndChildObjects();
313 }
314
315 if (SUCCEEDED(rc))
316 {
317 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
318 mData->mAccessible = TRUE;
319
320 unconst(mData->mUuid) = aId;
321
322 mUserData->s.strName = strName;
323
324 mUserData->s.llGroups = llGroups;
325
326 // the "name sync" flag determines whether the machine directory gets renamed along
327 // with the machine file; say so if the settings file name is the same as the
328 // settings file parent directory (machine directory)
329 mUserData->s.fNameSync = isInOwnDir();
330
331 // initialize the default snapshots folder
332 rc = COMSETTER(SnapshotFolder)(NULL);
333 AssertComRC(rc);
334
335 if (aOsType)
336 {
337 /* Store OS type */
338 mUserData->s.strOsType = aOsType->id();
339
340 /* Apply BIOS defaults */
341 mBIOSSettings->applyDefaults(aOsType);
342
343 /* Apply network adapters defaults */
344 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
345 mNetworkAdapters[slot]->applyDefaults(aOsType);
346
347 /* Apply serial port defaults */
348 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
349 mSerialPorts[slot]->applyDefaults(aOsType);
350 }
351
352 /* At this point the changing of the current state modification
353 * flag is allowed. */
354 allowStateModification();
355
356 /* commit all changes made during the initialization */
357 commit();
358 }
359
360 /* Confirm a successful initialization when it's the case */
361 if (SUCCEEDED(rc))
362 {
363 if (mData->mAccessible)
364 autoInitSpan.setSucceeded();
365 else
366 autoInitSpan.setLimited();
367 }
368
369 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
370 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
371 mData->mRegistered,
372 mData->mAccessible,
373 rc));
374
375 LogFlowThisFuncLeave();
376
377 return rc;
378}
379
380/**
381 * Initializes a new instance with data from machine XML (formerly Init_Registered).
382 * Gets called in two modes:
383 *
384 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
385 * UUID is specified and we mark the machine as "registered";
386 *
387 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
388 * and the machine remains unregistered until RegisterMachine() is called.
389 *
390 * @param aParent Associated parent object
391 * @param aConfigFile Local file system path to the VM settings file (can
392 * be relative to the VirtualBox config directory).
393 * @param aId UUID of the machine or NULL (see above).
394 *
395 * @return Success indicator. if not S_OK, the machine object is invalid
396 */
397HRESULT Machine::init(VirtualBox *aParent,
398 const Utf8Str &strConfigFile,
399 const Guid *aId)
400{
401 LogFlowThisFuncEnter();
402 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
403
404 /* Enclose the state transition NotReady->InInit->Ready */
405 AutoInitSpan autoInitSpan(this);
406 AssertReturn(autoInitSpan.isOk(), E_FAIL);
407
408 HRESULT rc = initImpl(aParent, strConfigFile);
409 if (FAILED(rc)) return rc;
410
411 if (aId)
412 {
413 // loading a registered VM:
414 unconst(mData->mUuid) = *aId;
415 mData->mRegistered = TRUE;
416 // now load the settings from XML:
417 rc = registeredInit();
418 // this calls initDataAndChildObjects() and loadSettings()
419 }
420 else
421 {
422 // opening an unregistered VM (VirtualBox::OpenMachine()):
423 rc = initDataAndChildObjects();
424
425 if (SUCCEEDED(rc))
426 {
427 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
428 mData->mAccessible = TRUE;
429
430 try
431 {
432 // load and parse machine XML; this will throw on XML or logic errors
433 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
434
435 // reject VM UUID duplicates, they can happen if someone
436 // tries to register an already known VM config again
437 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
438 true /* fPermitInaccessible */,
439 false /* aDoSetError */,
440 NULL) != VBOX_E_OBJECT_NOT_FOUND)
441 {
442 throw setError(E_FAIL,
443 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
444 mData->m_strConfigFile.c_str());
445 }
446
447 // use UUID from machine config
448 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
449
450 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
451 NULL /* puuidRegistry */);
452 if (FAILED(rc)) throw rc;
453
454 /* At this point the changing of the current state modification
455 * flag is allowed. */
456 allowStateModification();
457
458 commit();
459 }
460 catch (HRESULT err)
461 {
462 /* we assume that error info is set by the thrower */
463 rc = err;
464 }
465 catch (...)
466 {
467 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
468 }
469 }
470 }
471
472 /* Confirm a successful initialization when it's the case */
473 if (SUCCEEDED(rc))
474 {
475 if (mData->mAccessible)
476 autoInitSpan.setSucceeded();
477 else
478 {
479 autoInitSpan.setLimited();
480
481 // uninit media from this machine's media registry, or else
482 // reloading the settings will fail
483 mParent->unregisterMachineMedia(getId());
484 }
485 }
486
487 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
488 "rc=%08X\n",
489 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
490 mData->mRegistered, mData->mAccessible, rc));
491
492 LogFlowThisFuncLeave();
493
494 return rc;
495}
496
497/**
498 * Initializes a new instance from a machine config that is already in memory
499 * (import OVF case). Since we are importing, the UUID in the machine
500 * config is ignored and we always generate a fresh one.
501 *
502 * @param strName Name for the new machine; this overrides what is specified in config and is used
503 * for the settings file as well.
504 * @param config Machine configuration loaded and parsed from XML.
505 *
506 * @return Success indicator. if not S_OK, the machine object is invalid
507 */
508HRESULT Machine::init(VirtualBox *aParent,
509 const Utf8Str &strName,
510 const settings::MachineConfigFile &config)
511{
512 LogFlowThisFuncEnter();
513
514 /* Enclose the state transition NotReady->InInit->Ready */
515 AutoInitSpan autoInitSpan(this);
516 AssertReturn(autoInitSpan.isOk(), E_FAIL);
517
518 Utf8Str strConfigFile;
519 aParent->getDefaultMachineFolder(strConfigFile);
520 strConfigFile.append(RTPATH_DELIMITER);
521 strConfigFile.append(strName);
522 strConfigFile.append(RTPATH_DELIMITER);
523 strConfigFile.append(strName);
524 strConfigFile.append(".vbox");
525
526 HRESULT rc = initImpl(aParent, strConfigFile);
527 if (FAILED(rc)) return rc;
528
529 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
530 if (FAILED(rc)) return rc;
531
532 rc = initDataAndChildObjects();
533
534 if (SUCCEEDED(rc))
535 {
536 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
537 mData->mAccessible = TRUE;
538
539 // create empty machine config for instance data
540 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
541
542 // generate fresh UUID, ignore machine config
543 unconst(mData->mUuid).create();
544
545 rc = loadMachineDataFromSettings(config,
546 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
547
548 // override VM name as well, it may be different
549 mUserData->s.strName = strName;
550
551 if (SUCCEEDED(rc))
552 {
553 /* At this point the changing of the current state modification
554 * flag is allowed. */
555 allowStateModification();
556
557 /* commit all changes made during the initialization */
558 commit();
559 }
560 }
561
562 /* Confirm a successful initialization when it's the case */
563 if (SUCCEEDED(rc))
564 {
565 if (mData->mAccessible)
566 autoInitSpan.setSucceeded();
567 else
568 {
569 autoInitSpan.setLimited();
570
571 // uninit media from this machine's media registry, or else
572 // reloading the settings will fail
573 mParent->unregisterMachineMedia(getId());
574 }
575 }
576
577 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
578 "rc=%08X\n",
579 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
580 mData->mRegistered, mData->mAccessible, rc));
581
582 LogFlowThisFuncLeave();
583
584 return rc;
585}
586
587/**
588 * Shared code between the various init() implementations.
589 * @param aParent
590 * @return
591 */
592HRESULT Machine::initImpl(VirtualBox *aParent,
593 const Utf8Str &strConfigFile)
594{
595 LogFlowThisFuncEnter();
596
597 AssertReturn(aParent, E_INVALIDARG);
598 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
599
600 HRESULT rc = S_OK;
601
602 /* share the parent weakly */
603 unconst(mParent) = aParent;
604
605 /* allocate the essential machine data structure (the rest will be
606 * allocated later by initDataAndChildObjects() */
607 mData.allocate();
608
609 /* memorize the config file name (as provided) */
610 mData->m_strConfigFile = strConfigFile;
611
612 /* get the full file name */
613 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
614 if (RT_FAILURE(vrc1))
615 return setError(VBOX_E_FILE_ERROR,
616 tr("Invalid machine settings file name '%s' (%Rrc)"),
617 strConfigFile.c_str(),
618 vrc1);
619
620 LogFlowThisFuncLeave();
621
622 return rc;
623}
624
625/**
626 * Tries to create a machine settings file in the path stored in the machine
627 * instance data. Used when a new machine is created to fail gracefully if
628 * the settings file could not be written (e.g. because machine dir is read-only).
629 * @return
630 */
631HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
632{
633 HRESULT rc = S_OK;
634
635 // when we create a new machine, we must be able to create the settings file
636 RTFILE f = NIL_RTFILE;
637 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
638 if ( RT_SUCCESS(vrc)
639 || vrc == VERR_SHARING_VIOLATION
640 )
641 {
642 if (RT_SUCCESS(vrc))
643 RTFileClose(f);
644 if (!fForceOverwrite)
645 rc = setError(VBOX_E_FILE_ERROR,
646 tr("Machine settings file '%s' already exists"),
647 mData->m_strConfigFileFull.c_str());
648 else
649 {
650 /* try to delete the config file, as otherwise the creation
651 * of a new settings file will fail. */
652 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
653 if (RT_FAILURE(vrc2))
654 rc = setError(VBOX_E_FILE_ERROR,
655 tr("Could not delete the existing settings file '%s' (%Rrc)"),
656 mData->m_strConfigFileFull.c_str(), vrc2);
657 }
658 }
659 else if ( vrc != VERR_FILE_NOT_FOUND
660 && vrc != VERR_PATH_NOT_FOUND
661 )
662 rc = setError(VBOX_E_FILE_ERROR,
663 tr("Invalid machine settings file name '%s' (%Rrc)"),
664 mData->m_strConfigFileFull.c_str(),
665 vrc);
666 return rc;
667}
668
669/**
670 * Initializes the registered machine by loading the settings file.
671 * This method is separated from #init() in order to make it possible to
672 * retry the operation after VirtualBox startup instead of refusing to
673 * startup the whole VirtualBox server in case if the settings file of some
674 * registered VM is invalid or inaccessible.
675 *
676 * @note Must be always called from this object's write lock
677 * (unless called from #init() that doesn't need any locking).
678 * @note Locks the mUSBController method for writing.
679 * @note Subclasses must not call this method.
680 */
681HRESULT Machine::registeredInit()
682{
683 AssertReturn(!isSessionMachine(), E_FAIL);
684 AssertReturn(!isSnapshotMachine(), E_FAIL);
685 AssertReturn(!mData->mUuid.isEmpty(), E_FAIL);
686 AssertReturn(!mData->mAccessible, E_FAIL);
687
688 HRESULT rc = initDataAndChildObjects();
689
690 if (SUCCEEDED(rc))
691 {
692 /* Temporarily reset the registered flag in order to let setters
693 * potentially called from loadSettings() succeed (isMutable() used in
694 * all setters will return FALSE for a Machine instance if mRegistered
695 * is TRUE). */
696 mData->mRegistered = FALSE;
697
698 try
699 {
700 // load and parse machine XML; this will throw on XML or logic errors
701 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
702
703 if (mData->mUuid != mData->pMachineConfigFile->uuid)
704 throw setError(E_FAIL,
705 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
706 mData->pMachineConfigFile->uuid.raw(),
707 mData->m_strConfigFileFull.c_str(),
708 mData->mUuid.toString().c_str(),
709 mParent->settingsFilePath().c_str());
710
711 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
712 NULL /* const Guid *puuidRegistry */);
713 if (FAILED(rc)) throw rc;
714 }
715 catch (HRESULT err)
716 {
717 /* we assume that error info is set by the thrower */
718 rc = err;
719 }
720 catch (...)
721 {
722 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
723 }
724
725 /* Restore the registered flag (even on failure) */
726 mData->mRegistered = TRUE;
727 }
728
729 if (SUCCEEDED(rc))
730 {
731 /* Set mAccessible to TRUE only if we successfully locked and loaded
732 * the settings file */
733 mData->mAccessible = TRUE;
734
735 /* commit all changes made during loading the settings file */
736 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
737 /// @todo r=klaus for some reason the settings loading logic backs up
738 // the settings, and therefore a commit is needed. Should probably be changed.
739 }
740 else
741 {
742 /* If the machine is registered, then, instead of returning a
743 * failure, we mark it as inaccessible and set the result to
744 * success to give it a try later */
745
746 /* fetch the current error info */
747 mData->mAccessError = com::ErrorInfo();
748 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
749 mData->mUuid.raw(),
750 mData->mAccessError.getText().raw()));
751
752 /* rollback all changes */
753 rollback(false /* aNotify */);
754
755 // uninit media from this machine's media registry, or else
756 // reloading the settings will fail
757 mParent->unregisterMachineMedia(getId());
758
759 /* uninitialize the common part to make sure all data is reset to
760 * default (null) values */
761 uninitDataAndChildObjects();
762
763 rc = S_OK;
764 }
765
766 return rc;
767}
768
769/**
770 * Uninitializes the instance.
771 * Called either from FinalRelease() or by the parent when it gets destroyed.
772 *
773 * @note The caller of this method must make sure that this object
774 * a) doesn't have active callers on the current thread and b) is not locked
775 * by the current thread; otherwise uninit() will hang either a) due to
776 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
777 * a dead-lock caused by this thread waiting for all callers on the other
778 * threads are done but preventing them from doing so by holding a lock.
779 */
780void Machine::uninit()
781{
782 LogFlowThisFuncEnter();
783
784 Assert(!isWriteLockOnCurrentThread());
785
786 Assert(!uRegistryNeedsSaving);
787 if (uRegistryNeedsSaving)
788 {
789 AutoCaller autoCaller(this);
790 if (SUCCEEDED(autoCaller.rc()))
791 {
792 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
793 saveSettings(NULL, Machine::SaveS_Force);
794 }
795 }
796
797 /* Enclose the state transition Ready->InUninit->NotReady */
798 AutoUninitSpan autoUninitSpan(this);
799 if (autoUninitSpan.uninitDone())
800 return;
801
802 Assert(!isSnapshotMachine());
803 Assert(!isSessionMachine());
804 Assert(!!mData);
805
806 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
807 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
808
809 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
810
811 if (!mData->mSession.mMachine.isNull())
812 {
813 /* Theoretically, this can only happen if the VirtualBox server has been
814 * terminated while there were clients running that owned open direct
815 * sessions. Since in this case we are definitely called by
816 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
817 * won't happen on the client watcher thread (because it does
818 * VirtualBox::addCaller() for the duration of the
819 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
820 * cannot happen until the VirtualBox caller is released). This is
821 * important, because SessionMachine::uninit() cannot correctly operate
822 * after we return from this method (it expects the Machine instance is
823 * still valid). We'll call it ourselves below.
824 */
825 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
826 (SessionMachine*)mData->mSession.mMachine));
827
828 if (Global::IsOnlineOrTransient(mData->mMachineState))
829 {
830 LogWarningThisFunc(("Setting state to Aborted!\n"));
831 /* set machine state using SessionMachine reimplementation */
832 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
833 }
834
835 /*
836 * Uninitialize SessionMachine using public uninit() to indicate
837 * an unexpected uninitialization.
838 */
839 mData->mSession.mMachine->uninit();
840 /* SessionMachine::uninit() must set mSession.mMachine to null */
841 Assert(mData->mSession.mMachine.isNull());
842 }
843
844 // uninit media from this machine's media registry, if they're still there
845 Guid uuidMachine(getId());
846
847 /* XXX This will fail with
848 * "cannot be closed because it is still attached to 1 virtual machines"
849 * because at this point we did not call uninitDataAndChildObjects() yet
850 * and therefore also removeBackReference() for all these mediums was not called! */
851 if (!uuidMachine.isEmpty()) // can be empty if we're called from a failure of Machine::init
852 mParent->unregisterMachineMedia(uuidMachine);
853
854 /* the lock is no more necessary (SessionMachine is uninitialized) */
855 alock.release();
856
857 // has machine been modified?
858 if (mData->flModifications)
859 {
860 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
861 rollback(false /* aNotify */);
862 }
863
864 if (mData->mAccessible)
865 uninitDataAndChildObjects();
866
867 /* free the essential data structure last */
868 mData.free();
869
870 LogFlowThisFuncLeave();
871}
872
873// IMachine properties
874/////////////////////////////////////////////////////////////////////////////
875
876STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
877{
878 CheckComArgOutPointerValid(aParent);
879
880 AutoLimitedCaller autoCaller(this);
881 if (FAILED(autoCaller.rc())) return autoCaller.rc();
882
883 /* mParent is constant during life time, no need to lock */
884 ComObjPtr<VirtualBox> pVirtualBox(mParent);
885 pVirtualBox.queryInterfaceTo(aParent);
886
887 return S_OK;
888}
889
890STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
891{
892 CheckComArgOutPointerValid(aAccessible);
893
894 AutoLimitedCaller autoCaller(this);
895 if (FAILED(autoCaller.rc())) return autoCaller.rc();
896
897 LogFlowThisFunc(("ENTER\n"));
898
899 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
900
901 HRESULT rc = S_OK;
902
903 if (!mData->mAccessible)
904 {
905 /* try to initialize the VM once more if not accessible */
906
907 AutoReinitSpan autoReinitSpan(this);
908 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
909
910#ifdef DEBUG
911 LogFlowThisFunc(("Dumping media backreferences\n"));
912 mParent->dumpAllBackRefs();
913#endif
914
915 if (mData->pMachineConfigFile)
916 {
917 // reset the XML file to force loadSettings() (called from registeredInit())
918 // to parse it again; the file might have changed
919 delete mData->pMachineConfigFile;
920 mData->pMachineConfigFile = NULL;
921 }
922
923 rc = registeredInit();
924
925 if (SUCCEEDED(rc) && mData->mAccessible)
926 {
927 autoReinitSpan.setSucceeded();
928
929 /* make sure interesting parties will notice the accessibility
930 * state change */
931 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
932 mParent->onMachineDataChange(mData->mUuid);
933 }
934 }
935
936 if (SUCCEEDED(rc))
937 *aAccessible = mData->mAccessible;
938
939 LogFlowThisFuncLeave();
940
941 return rc;
942}
943
944STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
945{
946 CheckComArgOutPointerValid(aAccessError);
947
948 AutoLimitedCaller autoCaller(this);
949 if (FAILED(autoCaller.rc())) return autoCaller.rc();
950
951 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
952
953 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
954 {
955 /* return shortly */
956 aAccessError = NULL;
957 return S_OK;
958 }
959
960 HRESULT rc = S_OK;
961
962 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
963 rc = errorInfo.createObject();
964 if (SUCCEEDED(rc))
965 {
966 errorInfo->init(mData->mAccessError.getResultCode(),
967 mData->mAccessError.getInterfaceID().ref(),
968 Utf8Str(mData->mAccessError.getComponent()).c_str(),
969 Utf8Str(mData->mAccessError.getText()));
970 rc = errorInfo.queryInterfaceTo(aAccessError);
971 }
972
973 return rc;
974}
975
976STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
977{
978 CheckComArgOutPointerValid(aName);
979
980 AutoCaller autoCaller(this);
981 if (FAILED(autoCaller.rc())) return autoCaller.rc();
982
983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
984
985 mUserData->s.strName.cloneTo(aName);
986
987 return S_OK;
988}
989
990STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
991{
992 CheckComArgStrNotEmptyOrNull(aName);
993
994 AutoCaller autoCaller(this);
995 if (FAILED(autoCaller.rc())) return autoCaller.rc();
996
997 // prohibit setting a UUID only as the machine name, or else it can
998 // never be found by findMachine()
999 Guid test(aName);
1000 if (test.isNotEmpty())
1001 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1002
1003 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1004
1005 HRESULT rc = checkStateDependency(MutableStateDep);
1006 if (FAILED(rc)) return rc;
1007
1008 setModified(IsModified_MachineData);
1009 mUserData.backup();
1010 mUserData->s.strName = aName;
1011
1012 return S_OK;
1013}
1014
1015STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1016{
1017 CheckComArgOutPointerValid(aDescription);
1018
1019 AutoCaller autoCaller(this);
1020 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1021
1022 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1023
1024 mUserData->s.strDescription.cloneTo(aDescription);
1025
1026 return S_OK;
1027}
1028
1029STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1030{
1031 AutoCaller autoCaller(this);
1032 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1033
1034 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1035
1036 // this can be done in principle in any state as it doesn't affect the VM
1037 // significantly, but play safe by not messing around while complex
1038 // activities are going on
1039 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1040 if (FAILED(rc)) return rc;
1041
1042 setModified(IsModified_MachineData);
1043 mUserData.backup();
1044 mUserData->s.strDescription = aDescription;
1045
1046 return S_OK;
1047}
1048
1049STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1050{
1051 CheckComArgOutPointerValid(aId);
1052
1053 AutoLimitedCaller autoCaller(this);
1054 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1055
1056 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1057
1058 mData->mUuid.toUtf16().cloneTo(aId);
1059
1060 return S_OK;
1061}
1062
1063STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1064{
1065 CheckComArgOutSafeArrayPointerValid(aGroups);
1066
1067 AutoCaller autoCaller(this);
1068 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1069
1070 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1071 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1072 size_t i = 0;
1073 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1074 it != mUserData->s.llGroups.end();
1075 ++it, i++)
1076 {
1077 Bstr tmp = *it;
1078 tmp.cloneTo(&groups[i]);
1079 }
1080 groups.detachTo(ComSafeArrayOutArg(aGroups));
1081
1082 return S_OK;
1083}
1084
1085STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1086{
1087 AutoCaller autoCaller(this);
1088 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1089
1090 StringsList llGroups;
1091 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1092 if (FAILED(rc))
1093 return rc;
1094
1095 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1096
1097 // changing machine groups is possible while the VM is offline
1098 rc = checkStateDependency(OfflineStateDep);
1099 if (FAILED(rc)) return rc;
1100
1101 setModified(IsModified_MachineData);
1102 mUserData.backup();
1103 mUserData->s.llGroups = llGroups;
1104
1105 return S_OK;
1106}
1107
1108STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1109{
1110 CheckComArgOutPointerValid(aOSTypeId);
1111
1112 AutoCaller autoCaller(this);
1113 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1114
1115 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1116
1117 mUserData->s.strOsType.cloneTo(aOSTypeId);
1118
1119 return S_OK;
1120}
1121
1122STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1123{
1124 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1125
1126 AutoCaller autoCaller(this);
1127 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1128
1129 /* look up the object by Id to check it is valid */
1130 ComPtr<IGuestOSType> guestOSType;
1131 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1132 if (FAILED(rc)) return rc;
1133
1134 /* when setting, always use the "etalon" value for consistency -- lookup
1135 * by ID is case-insensitive and the input value may have different case */
1136 Bstr osTypeId;
1137 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1138 if (FAILED(rc)) return rc;
1139
1140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1141
1142 rc = checkStateDependency(MutableStateDep);
1143 if (FAILED(rc)) return rc;
1144
1145 setModified(IsModified_MachineData);
1146 mUserData.backup();
1147 mUserData->s.strOsType = osTypeId;
1148
1149 return S_OK;
1150}
1151
1152
1153STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1154{
1155 CheckComArgOutPointerValid(aFirmwareType);
1156
1157 AutoCaller autoCaller(this);
1158 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1159
1160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1161
1162 *aFirmwareType = mHWData->mFirmwareType;
1163
1164 return S_OK;
1165}
1166
1167STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1168{
1169 AutoCaller autoCaller(this);
1170 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1171 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1172
1173 HRESULT rc = checkStateDependency(MutableStateDep);
1174 if (FAILED(rc)) return rc;
1175
1176 setModified(IsModified_MachineData);
1177 mHWData.backup();
1178 mHWData->mFirmwareType = aFirmwareType;
1179
1180 return S_OK;
1181}
1182
1183STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1184{
1185 CheckComArgOutPointerValid(aKeyboardHIDType);
1186
1187 AutoCaller autoCaller(this);
1188 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1189
1190 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1191
1192 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1193
1194 return S_OK;
1195}
1196
1197STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1198{
1199 AutoCaller autoCaller(this);
1200 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1201 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1202
1203 HRESULT rc = checkStateDependency(MutableStateDep);
1204 if (FAILED(rc)) return rc;
1205
1206 setModified(IsModified_MachineData);
1207 mHWData.backup();
1208 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1209
1210 return S_OK;
1211}
1212
1213STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1214{
1215 CheckComArgOutPointerValid(aPointingHIDType);
1216
1217 AutoCaller autoCaller(this);
1218 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1219
1220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1221
1222 *aPointingHIDType = mHWData->mPointingHIDType;
1223
1224 return S_OK;
1225}
1226
1227STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1228{
1229 AutoCaller autoCaller(this);
1230 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1231 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1232
1233 HRESULT rc = checkStateDependency(MutableStateDep);
1234 if (FAILED(rc)) return rc;
1235
1236 setModified(IsModified_MachineData);
1237 mHWData.backup();
1238 mHWData->mPointingHIDType = aPointingHIDType;
1239
1240 return S_OK;
1241}
1242
1243STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1244{
1245 CheckComArgOutPointerValid(aChipsetType);
1246
1247 AutoCaller autoCaller(this);
1248 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1249
1250 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1251
1252 *aChipsetType = mHWData->mChipsetType;
1253
1254 return S_OK;
1255}
1256
1257STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1258{
1259 AutoCaller autoCaller(this);
1260 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1261 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1262
1263 HRESULT rc = checkStateDependency(MutableStateDep);
1264 if (FAILED(rc)) return rc;
1265
1266 if (aChipsetType != mHWData->mChipsetType)
1267 {
1268 setModified(IsModified_MachineData);
1269 mHWData.backup();
1270 mHWData->mChipsetType = aChipsetType;
1271
1272 // Resize network adapter array, to be finalized on commit/rollback.
1273 // We must not throw away entries yet, otherwise settings are lost
1274 // without a way to roll back.
1275 uint32_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1276 uint32_t oldCount = mNetworkAdapters.size();
1277 if (newCount > oldCount)
1278 {
1279 mNetworkAdapters.resize(newCount);
1280 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1281 {
1282 unconst(mNetworkAdapters[slot]).createObject();
1283 mNetworkAdapters[slot]->init(this, slot);
1284 }
1285 }
1286 }
1287
1288 return S_OK;
1289}
1290
1291STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1292{
1293 CheckComArgOutPointerValid(aHWVersion);
1294
1295 AutoCaller autoCaller(this);
1296 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1297
1298 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1299
1300 mHWData->mHWVersion.cloneTo(aHWVersion);
1301
1302 return S_OK;
1303}
1304
1305STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1306{
1307 /* check known version */
1308 Utf8Str hwVersion = aHWVersion;
1309 if ( hwVersion.compare("1") != 0
1310 && hwVersion.compare("2") != 0)
1311 return setError(E_INVALIDARG,
1312 tr("Invalid hardware version: %ls\n"), aHWVersion);
1313
1314 AutoCaller autoCaller(this);
1315 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1316
1317 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1318
1319 HRESULT rc = checkStateDependency(MutableStateDep);
1320 if (FAILED(rc)) return rc;
1321
1322 setModified(IsModified_MachineData);
1323 mHWData.backup();
1324 mHWData->mHWVersion = hwVersion;
1325
1326 return S_OK;
1327}
1328
1329STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1330{
1331 CheckComArgOutPointerValid(aUUID);
1332
1333 AutoCaller autoCaller(this);
1334 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1335
1336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1337
1338 if (!mHWData->mHardwareUUID.isEmpty())
1339 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1340 else
1341 mData->mUuid.toUtf16().cloneTo(aUUID);
1342
1343 return S_OK;
1344}
1345
1346STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1347{
1348 Guid hardwareUUID(aUUID);
1349 if (hardwareUUID.isEmpty())
1350 return E_INVALIDARG;
1351
1352 AutoCaller autoCaller(this);
1353 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1354
1355 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1356
1357 HRESULT rc = checkStateDependency(MutableStateDep);
1358 if (FAILED(rc)) return rc;
1359
1360 setModified(IsModified_MachineData);
1361 mHWData.backup();
1362 if (hardwareUUID == mData->mUuid)
1363 mHWData->mHardwareUUID.clear();
1364 else
1365 mHWData->mHardwareUUID = hardwareUUID;
1366
1367 return S_OK;
1368}
1369
1370STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1371{
1372 CheckComArgOutPointerValid(memorySize);
1373
1374 AutoCaller autoCaller(this);
1375 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1376
1377 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1378
1379 *memorySize = mHWData->mMemorySize;
1380
1381 return S_OK;
1382}
1383
1384STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1385{
1386 /* check RAM limits */
1387 if ( memorySize < MM_RAM_MIN_IN_MB
1388 || memorySize > MM_RAM_MAX_IN_MB
1389 )
1390 return setError(E_INVALIDARG,
1391 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1392 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1393
1394 AutoCaller autoCaller(this);
1395 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1396
1397 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1398
1399 HRESULT rc = checkStateDependency(MutableStateDep);
1400 if (FAILED(rc)) return rc;
1401
1402 setModified(IsModified_MachineData);
1403 mHWData.backup();
1404 mHWData->mMemorySize = memorySize;
1405
1406 return S_OK;
1407}
1408
1409STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1410{
1411 CheckComArgOutPointerValid(CPUCount);
1412
1413 AutoCaller autoCaller(this);
1414 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1415
1416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1417
1418 *CPUCount = mHWData->mCPUCount;
1419
1420 return S_OK;
1421}
1422
1423STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1424{
1425 /* check CPU limits */
1426 if ( CPUCount < SchemaDefs::MinCPUCount
1427 || CPUCount > SchemaDefs::MaxCPUCount
1428 )
1429 return setError(E_INVALIDARG,
1430 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1431 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1432
1433 AutoCaller autoCaller(this);
1434 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1435
1436 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1437
1438 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1439 if (mHWData->mCPUHotPlugEnabled)
1440 {
1441 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1442 {
1443 if (mHWData->mCPUAttached[idx])
1444 return setError(E_INVALIDARG,
1445 tr("There is still a CPU attached to socket %lu."
1446 "Detach the CPU before removing the socket"),
1447 CPUCount, idx+1);
1448 }
1449 }
1450
1451 HRESULT rc = checkStateDependency(MutableStateDep);
1452 if (FAILED(rc)) return rc;
1453
1454 setModified(IsModified_MachineData);
1455 mHWData.backup();
1456 mHWData->mCPUCount = CPUCount;
1457
1458 return S_OK;
1459}
1460
1461STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1462{
1463 CheckComArgOutPointerValid(aExecutionCap);
1464
1465 AutoCaller autoCaller(this);
1466 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1467
1468 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1469
1470 *aExecutionCap = mHWData->mCpuExecutionCap;
1471
1472 return S_OK;
1473}
1474
1475STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1476{
1477 HRESULT rc = S_OK;
1478
1479 /* check throttle limits */
1480 if ( aExecutionCap < 1
1481 || aExecutionCap > 100
1482 )
1483 return setError(E_INVALIDARG,
1484 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1485 aExecutionCap, 1, 100);
1486
1487 AutoCaller autoCaller(this);
1488 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1489
1490 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1491
1492 alock.release();
1493 rc = onCPUExecutionCapChange(aExecutionCap);
1494 alock.acquire();
1495 if (FAILED(rc)) return rc;
1496
1497 setModified(IsModified_MachineData);
1498 mHWData.backup();
1499 mHWData->mCpuExecutionCap = aExecutionCap;
1500
1501 /* Save settings if online - todo why is this required?? */
1502 if (Global::IsOnline(mData->mMachineState))
1503 saveSettings(NULL);
1504
1505 return S_OK;
1506}
1507
1508
1509STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *enabled)
1510{
1511 CheckComArgOutPointerValid(enabled);
1512
1513 AutoCaller autoCaller(this);
1514 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1515
1516 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1517
1518 *enabled = mHWData->mCPUHotPlugEnabled;
1519
1520 return S_OK;
1521}
1522
1523STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled)
1524{
1525 HRESULT rc = S_OK;
1526
1527 AutoCaller autoCaller(this);
1528 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1529
1530 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1531
1532 rc = checkStateDependency(MutableStateDep);
1533 if (FAILED(rc)) return rc;
1534
1535 if (mHWData->mCPUHotPlugEnabled != enabled)
1536 {
1537 if (enabled)
1538 {
1539 setModified(IsModified_MachineData);
1540 mHWData.backup();
1541
1542 /* Add the amount of CPUs currently attached */
1543 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1544 {
1545 mHWData->mCPUAttached[i] = true;
1546 }
1547 }
1548 else
1549 {
1550 /*
1551 * We can disable hotplug only if the amount of maximum CPUs is equal
1552 * to the amount of attached CPUs
1553 */
1554 unsigned cCpusAttached = 0;
1555 unsigned iHighestId = 0;
1556
1557 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1558 {
1559 if (mHWData->mCPUAttached[i])
1560 {
1561 cCpusAttached++;
1562 iHighestId = i;
1563 }
1564 }
1565
1566 if ( (cCpusAttached != mHWData->mCPUCount)
1567 || (iHighestId >= mHWData->mCPUCount))
1568 return setError(E_INVALIDARG,
1569 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1570
1571 setModified(IsModified_MachineData);
1572 mHWData.backup();
1573 }
1574 }
1575
1576 mHWData->mCPUHotPlugEnabled = enabled;
1577
1578 return rc;
1579}
1580
1581STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *enabled)
1582{
1583#ifdef VBOX_WITH_USB_CARDREADER
1584 CheckComArgOutPointerValid(enabled);
1585
1586 AutoCaller autoCaller(this);
1587 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1588
1589 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1590
1591 *enabled = mHWData->mEmulatedUSBCardReaderEnabled;
1592
1593 return S_OK;
1594#else
1595 NOREF(enabled);
1596 return E_NOTIMPL;
1597#endif
1598}
1599
1600STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL enabled)
1601{
1602#ifdef VBOX_WITH_USB_CARDREADER
1603 AutoCaller autoCaller(this);
1604 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1605 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1606
1607 HRESULT rc = checkStateDependency(MutableStateDep);
1608 if (FAILED(rc)) return rc;
1609
1610 setModified(IsModified_MachineData);
1611 mHWData.backup();
1612 mHWData->mEmulatedUSBCardReaderEnabled = enabled;
1613
1614 return S_OK;
1615#else
1616 NOREF(enabled);
1617 return E_NOTIMPL;
1618#endif
1619}
1620
1621STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *enabled)
1622{
1623 NOREF(enabled);
1624 return E_NOTIMPL;
1625}
1626
1627STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL enabled)
1628{
1629 NOREF(enabled);
1630 return E_NOTIMPL;
1631}
1632
1633STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *enabled)
1634{
1635 CheckComArgOutPointerValid(enabled);
1636
1637 AutoCaller autoCaller(this);
1638 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1639 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1640
1641 *enabled = mHWData->mHPETEnabled;
1642
1643 return S_OK;
1644}
1645
1646STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL enabled)
1647{
1648 HRESULT rc = S_OK;
1649
1650 AutoCaller autoCaller(this);
1651 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1652 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1653
1654 rc = checkStateDependency(MutableStateDep);
1655 if (FAILED(rc)) return rc;
1656
1657 setModified(IsModified_MachineData);
1658 mHWData.backup();
1659
1660 mHWData->mHPETEnabled = enabled;
1661
1662 return rc;
1663}
1664
1665STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL * fEnabled)
1666{
1667 AutoCaller autoCaller(this);
1668 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1669
1670 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1671
1672 *fEnabled = mHWData->mVideoCaptureEnabled;
1673 return S_OK;
1674}
1675
1676STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1677{
1678 AutoCaller autoCaller(this);
1679 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1680
1681 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1682 mHWData->mVideoCaptureEnabled = fEnabled;
1683 return S_OK;
1684}
1685
1686STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR * ppChFile)
1687{
1688 AutoCaller autoCaller(this);
1689 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1690
1691 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1692 mHWData->mVideoCaptureFile.cloneTo(ppChFile);
1693 return S_OK;
1694}
1695
1696STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR pChFile)
1697{
1698 AutoCaller autoCaller(this);
1699 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1700
1701 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1702 mHWData->mVideoCaptureFile = pChFile;
1703 return S_OK;
1704}
1705
1706
1707STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(uint32_t *u32HorzRes)
1708{
1709 AutoCaller autoCaller(this);
1710 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1711
1712 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1713 *u32HorzRes = mHWData->mVideoCaptureWidth;
1714 return S_OK;
1715}
1716
1717STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(uint32_t u32HorzRes)
1718{
1719 AutoCaller autoCaller(this);
1720 if (FAILED(autoCaller.rc()))
1721 {
1722 LogFlow(("Autolocked failed\n"));
1723 return autoCaller.rc();
1724 }
1725
1726 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1727 mHWData->mVideoCaptureWidth = u32HorzRes;
1728 return S_OK;
1729}
1730
1731STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(uint32_t *u32VertRes)
1732{
1733 AutoCaller autoCaller(this);
1734 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1735
1736 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1737 *u32VertRes = mHWData->mVideoCaptureHeight;
1738 return S_OK;
1739}
1740
1741STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(uint32_t u32VertRes)
1742{
1743 AutoCaller autoCaller(this);
1744 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1745
1746 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1747 mHWData->mVideoCaptureHeight = u32VertRes;
1748 return S_OK;
1749}
1750
1751STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1752{
1753 CheckComArgOutPointerValid(memorySize);
1754
1755 AutoCaller autoCaller(this);
1756 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1757
1758 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1759
1760 *memorySize = mHWData->mVRAMSize;
1761
1762 return S_OK;
1763}
1764
1765STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1766{
1767 /* check VRAM limits */
1768 if (memorySize < SchemaDefs::MinGuestVRAM ||
1769 memorySize > SchemaDefs::MaxGuestVRAM)
1770 return setError(E_INVALIDARG,
1771 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1772 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1773
1774 AutoCaller autoCaller(this);
1775 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1776
1777 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1778
1779 HRESULT rc = checkStateDependency(MutableStateDep);
1780 if (FAILED(rc)) return rc;
1781
1782 setModified(IsModified_MachineData);
1783 mHWData.backup();
1784 mHWData->mVRAMSize = memorySize;
1785
1786 return S_OK;
1787}
1788
1789/** @todo this method should not be public */
1790STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1791{
1792 CheckComArgOutPointerValid(memoryBalloonSize);
1793
1794 AutoCaller autoCaller(this);
1795 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1796
1797 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1798
1799 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
1800
1801 return S_OK;
1802}
1803
1804/**
1805 * Set the memory balloon size.
1806 *
1807 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1808 * we have to make sure that we never call IGuest from here.
1809 */
1810STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
1811{
1812 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1813#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1814 /* check limits */
1815 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1816 return setError(E_INVALIDARG,
1817 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1818 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1819
1820 AutoCaller autoCaller(this);
1821 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1822
1823 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1824
1825 setModified(IsModified_MachineData);
1826 mHWData.backup();
1827 mHWData->mMemoryBalloonSize = memoryBalloonSize;
1828
1829 return S_OK;
1830#else
1831 NOREF(memoryBalloonSize);
1832 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1833#endif
1834}
1835
1836STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *enabled)
1837{
1838 CheckComArgOutPointerValid(enabled);
1839
1840 AutoCaller autoCaller(this);
1841 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1842
1843 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1844
1845 *enabled = mHWData->mPageFusionEnabled;
1846 return S_OK;
1847}
1848
1849STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL enabled)
1850{
1851#ifdef VBOX_WITH_PAGE_SHARING
1852 AutoCaller autoCaller(this);
1853 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1854
1855 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1856
1857 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1858 setModified(IsModified_MachineData);
1859 mHWData.backup();
1860 mHWData->mPageFusionEnabled = enabled;
1861 return S_OK;
1862#else
1863 NOREF(enabled);
1864 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1865#endif
1866}
1867
1868STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *enabled)
1869{
1870 CheckComArgOutPointerValid(enabled);
1871
1872 AutoCaller autoCaller(this);
1873 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1874
1875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1876
1877 *enabled = mHWData->mAccelerate3DEnabled;
1878
1879 return S_OK;
1880}
1881
1882STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
1883{
1884 AutoCaller autoCaller(this);
1885 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1886
1887 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1888
1889 HRESULT rc = checkStateDependency(MutableStateDep);
1890 if (FAILED(rc)) return rc;
1891
1892 /** @todo check validity! */
1893
1894 setModified(IsModified_MachineData);
1895 mHWData.backup();
1896 mHWData->mAccelerate3DEnabled = enable;
1897
1898 return S_OK;
1899}
1900
1901
1902STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *enabled)
1903{
1904 CheckComArgOutPointerValid(enabled);
1905
1906 AutoCaller autoCaller(this);
1907 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1908
1909 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1910
1911 *enabled = mHWData->mAccelerate2DVideoEnabled;
1912
1913 return S_OK;
1914}
1915
1916STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
1917{
1918 AutoCaller autoCaller(this);
1919 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1920
1921 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1922
1923 HRESULT rc = checkStateDependency(MutableStateDep);
1924 if (FAILED(rc)) return rc;
1925
1926 /** @todo check validity! */
1927
1928 setModified(IsModified_MachineData);
1929 mHWData.backup();
1930 mHWData->mAccelerate2DVideoEnabled = enable;
1931
1932 return S_OK;
1933}
1934
1935STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
1936{
1937 CheckComArgOutPointerValid(monitorCount);
1938
1939 AutoCaller autoCaller(this);
1940 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1941
1942 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1943
1944 *monitorCount = mHWData->mMonitorCount;
1945
1946 return S_OK;
1947}
1948
1949STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
1950{
1951 /* make sure monitor count is a sensible number */
1952 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
1953 return setError(E_INVALIDARG,
1954 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1955 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
1956
1957 AutoCaller autoCaller(this);
1958 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1959
1960 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1961
1962 HRESULT rc = checkStateDependency(MutableStateDep);
1963 if (FAILED(rc)) return rc;
1964
1965 setModified(IsModified_MachineData);
1966 mHWData.backup();
1967 mHWData->mMonitorCount = monitorCount;
1968
1969 return S_OK;
1970}
1971
1972STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
1973{
1974 CheckComArgOutPointerValid(biosSettings);
1975
1976 AutoCaller autoCaller(this);
1977 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1978
1979 /* mBIOSSettings is constant during life time, no need to lock */
1980 mBIOSSettings.queryInterfaceTo(biosSettings);
1981
1982 return S_OK;
1983}
1984
1985STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
1986{
1987 CheckComArgOutPointerValid(aVal);
1988
1989 AutoCaller autoCaller(this);
1990 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1991
1992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1993
1994 switch(property)
1995 {
1996 case CPUPropertyType_PAE:
1997 *aVal = mHWData->mPAEEnabled;
1998 break;
1999
2000 case CPUPropertyType_Synthetic:
2001 *aVal = mHWData->mSyntheticCpu;
2002 break;
2003
2004 default:
2005 return E_INVALIDARG;
2006 }
2007 return S_OK;
2008}
2009
2010STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2011{
2012 AutoCaller autoCaller(this);
2013 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2014
2015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2016
2017 HRESULT rc = checkStateDependency(MutableStateDep);
2018 if (FAILED(rc)) return rc;
2019
2020 switch(property)
2021 {
2022 case CPUPropertyType_PAE:
2023 setModified(IsModified_MachineData);
2024 mHWData.backup();
2025 mHWData->mPAEEnabled = !!aVal;
2026 break;
2027
2028 case CPUPropertyType_Synthetic:
2029 setModified(IsModified_MachineData);
2030 mHWData.backup();
2031 mHWData->mSyntheticCpu = !!aVal;
2032 break;
2033
2034 default:
2035 return E_INVALIDARG;
2036 }
2037 return S_OK;
2038}
2039
2040STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2041{
2042 CheckComArgOutPointerValid(aValEax);
2043 CheckComArgOutPointerValid(aValEbx);
2044 CheckComArgOutPointerValid(aValEcx);
2045 CheckComArgOutPointerValid(aValEdx);
2046
2047 AutoCaller autoCaller(this);
2048 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2049
2050 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2051
2052 switch(aId)
2053 {
2054 case 0x0:
2055 case 0x1:
2056 case 0x2:
2057 case 0x3:
2058 case 0x4:
2059 case 0x5:
2060 case 0x6:
2061 case 0x7:
2062 case 0x8:
2063 case 0x9:
2064 case 0xA:
2065 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2066 return E_INVALIDARG;
2067
2068 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2069 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2070 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2071 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2072 break;
2073
2074 case 0x80000000:
2075 case 0x80000001:
2076 case 0x80000002:
2077 case 0x80000003:
2078 case 0x80000004:
2079 case 0x80000005:
2080 case 0x80000006:
2081 case 0x80000007:
2082 case 0x80000008:
2083 case 0x80000009:
2084 case 0x8000000A:
2085 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2086 return E_INVALIDARG;
2087
2088 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2089 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2090 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2091 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2092 break;
2093
2094 default:
2095 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2096 }
2097 return S_OK;
2098}
2099
2100STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2101{
2102 AutoCaller autoCaller(this);
2103 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2104
2105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2106
2107 HRESULT rc = checkStateDependency(MutableStateDep);
2108 if (FAILED(rc)) return rc;
2109
2110 switch(aId)
2111 {
2112 case 0x0:
2113 case 0x1:
2114 case 0x2:
2115 case 0x3:
2116 case 0x4:
2117 case 0x5:
2118 case 0x6:
2119 case 0x7:
2120 case 0x8:
2121 case 0x9:
2122 case 0xA:
2123 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2124 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2125 setModified(IsModified_MachineData);
2126 mHWData.backup();
2127 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2128 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2129 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2130 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2131 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2132 break;
2133
2134 case 0x80000000:
2135 case 0x80000001:
2136 case 0x80000002:
2137 case 0x80000003:
2138 case 0x80000004:
2139 case 0x80000005:
2140 case 0x80000006:
2141 case 0x80000007:
2142 case 0x80000008:
2143 case 0x80000009:
2144 case 0x8000000A:
2145 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2146 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2147 setModified(IsModified_MachineData);
2148 mHWData.backup();
2149 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2150 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2151 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2152 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2153 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2154 break;
2155
2156 default:
2157 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2158 }
2159 return S_OK;
2160}
2161
2162STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2163{
2164 AutoCaller autoCaller(this);
2165 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2166
2167 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2168
2169 HRESULT rc = checkStateDependency(MutableStateDep);
2170 if (FAILED(rc)) return rc;
2171
2172 switch(aId)
2173 {
2174 case 0x0:
2175 case 0x1:
2176 case 0x2:
2177 case 0x3:
2178 case 0x4:
2179 case 0x5:
2180 case 0x6:
2181 case 0x7:
2182 case 0x8:
2183 case 0x9:
2184 case 0xA:
2185 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2186 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2187 setModified(IsModified_MachineData);
2188 mHWData.backup();
2189 /* Invalidate leaf. */
2190 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2191 break;
2192
2193 case 0x80000000:
2194 case 0x80000001:
2195 case 0x80000002:
2196 case 0x80000003:
2197 case 0x80000004:
2198 case 0x80000005:
2199 case 0x80000006:
2200 case 0x80000007:
2201 case 0x80000008:
2202 case 0x80000009:
2203 case 0x8000000A:
2204 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2205 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2206 setModified(IsModified_MachineData);
2207 mHWData.backup();
2208 /* Invalidate leaf. */
2209 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2210 break;
2211
2212 default:
2213 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2214 }
2215 return S_OK;
2216}
2217
2218STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2219{
2220 AutoCaller autoCaller(this);
2221 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2222
2223 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2224
2225 HRESULT rc = checkStateDependency(MutableStateDep);
2226 if (FAILED(rc)) return rc;
2227
2228 setModified(IsModified_MachineData);
2229 mHWData.backup();
2230
2231 /* Invalidate all standard leafs. */
2232 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2233 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2234
2235 /* Invalidate all extended leafs. */
2236 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2237 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2238
2239 return S_OK;
2240}
2241
2242STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2243{
2244 CheckComArgOutPointerValid(aVal);
2245
2246 AutoCaller autoCaller(this);
2247 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2248
2249 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2250
2251 switch(property)
2252 {
2253 case HWVirtExPropertyType_Enabled:
2254 *aVal = mHWData->mHWVirtExEnabled;
2255 break;
2256
2257 case HWVirtExPropertyType_Exclusive:
2258 *aVal = mHWData->mHWVirtExExclusive;
2259 break;
2260
2261 case HWVirtExPropertyType_VPID:
2262 *aVal = mHWData->mHWVirtExVPIDEnabled;
2263 break;
2264
2265 case HWVirtExPropertyType_NestedPaging:
2266 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2267 break;
2268
2269 case HWVirtExPropertyType_LargePages:
2270 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2271#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2272 *aVal = FALSE;
2273#endif
2274 break;
2275
2276 case HWVirtExPropertyType_Force:
2277 *aVal = mHWData->mHWVirtExForceEnabled;
2278 break;
2279
2280 default:
2281 return E_INVALIDARG;
2282 }
2283 return S_OK;
2284}
2285
2286STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2287{
2288 AutoCaller autoCaller(this);
2289 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2290
2291 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2292
2293 HRESULT rc = checkStateDependency(MutableStateDep);
2294 if (FAILED(rc)) return rc;
2295
2296 switch(property)
2297 {
2298 case HWVirtExPropertyType_Enabled:
2299 setModified(IsModified_MachineData);
2300 mHWData.backup();
2301 mHWData->mHWVirtExEnabled = !!aVal;
2302 break;
2303
2304 case HWVirtExPropertyType_Exclusive:
2305 setModified(IsModified_MachineData);
2306 mHWData.backup();
2307 mHWData->mHWVirtExExclusive = !!aVal;
2308 break;
2309
2310 case HWVirtExPropertyType_VPID:
2311 setModified(IsModified_MachineData);
2312 mHWData.backup();
2313 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2314 break;
2315
2316 case HWVirtExPropertyType_NestedPaging:
2317 setModified(IsModified_MachineData);
2318 mHWData.backup();
2319 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2320 break;
2321
2322 case HWVirtExPropertyType_LargePages:
2323 setModified(IsModified_MachineData);
2324 mHWData.backup();
2325 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2326 break;
2327
2328 case HWVirtExPropertyType_Force:
2329 setModified(IsModified_MachineData);
2330 mHWData.backup();
2331 mHWData->mHWVirtExForceEnabled = !!aVal;
2332 break;
2333
2334 default:
2335 return E_INVALIDARG;
2336 }
2337
2338 return S_OK;
2339}
2340
2341STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2342{
2343 CheckComArgOutPointerValid(aSnapshotFolder);
2344
2345 AutoCaller autoCaller(this);
2346 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2347
2348 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2349
2350 Utf8Str strFullSnapshotFolder;
2351 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2352 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2353
2354 return S_OK;
2355}
2356
2357STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2358{
2359 /* @todo (r=dmik):
2360 * 1. Allow to change the name of the snapshot folder containing snapshots
2361 * 2. Rename the folder on disk instead of just changing the property
2362 * value (to be smart and not to leave garbage). Note that it cannot be
2363 * done here because the change may be rolled back. Thus, the right
2364 * place is #saveSettings().
2365 */
2366
2367 AutoCaller autoCaller(this);
2368 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2369
2370 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2371
2372 HRESULT rc = checkStateDependency(MutableStateDep);
2373 if (FAILED(rc)) return rc;
2374
2375 if (!mData->mCurrentSnapshot.isNull())
2376 return setError(E_FAIL,
2377 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2378
2379 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2380
2381 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2382 if (strSnapshotFolder.isEmpty())
2383 strSnapshotFolder = "Snapshots";
2384 int vrc = calculateFullPath(strSnapshotFolder,
2385 strSnapshotFolder);
2386 if (RT_FAILURE(vrc))
2387 return setError(E_FAIL,
2388 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2389 aSnapshotFolder, vrc);
2390
2391 setModified(IsModified_MachineData);
2392 mUserData.backup();
2393
2394 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2395
2396 return S_OK;
2397}
2398
2399STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2400{
2401 CheckComArgOutSafeArrayPointerValid(aAttachments);
2402
2403 AutoCaller autoCaller(this);
2404 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2405
2406 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2407
2408 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2409 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2410
2411 return S_OK;
2412}
2413
2414STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2415{
2416 CheckComArgOutPointerValid(vrdeServer);
2417
2418 AutoCaller autoCaller(this);
2419 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2420
2421 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2422
2423 Assert(!!mVRDEServer);
2424 mVRDEServer.queryInterfaceTo(vrdeServer);
2425
2426 return S_OK;
2427}
2428
2429STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2430{
2431 CheckComArgOutPointerValid(audioAdapter);
2432
2433 AutoCaller autoCaller(this);
2434 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2435
2436 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2437
2438 mAudioAdapter.queryInterfaceTo(audioAdapter);
2439 return S_OK;
2440}
2441
2442STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2443{
2444#ifdef VBOX_WITH_VUSB
2445 CheckComArgOutPointerValid(aUSBController);
2446
2447 AutoCaller autoCaller(this);
2448 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2449
2450 clearError();
2451 MultiResult rc(S_OK);
2452
2453# ifdef VBOX_WITH_USB
2454 rc = mParent->host()->checkUSBProxyService();
2455 if (FAILED(rc)) return rc;
2456# endif
2457
2458 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2459
2460 return rc = mUSBController.queryInterfaceTo(aUSBController);
2461#else
2462 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2463 * extended error info to indicate that USB is simply not available
2464 * (w/o treating it as a failure), for example, as in OSE */
2465 NOREF(aUSBController);
2466 ReturnComNotImplemented();
2467#endif /* VBOX_WITH_VUSB */
2468}
2469
2470STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2471{
2472 CheckComArgOutPointerValid(aFilePath);
2473
2474 AutoLimitedCaller autoCaller(this);
2475 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2476
2477 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2478
2479 mData->m_strConfigFileFull.cloneTo(aFilePath);
2480 return S_OK;
2481}
2482
2483STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2484{
2485 CheckComArgOutPointerValid(aModified);
2486
2487 AutoCaller autoCaller(this);
2488 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2489
2490 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2491
2492 HRESULT rc = checkStateDependency(MutableStateDep);
2493 if (FAILED(rc)) return rc;
2494
2495 if (!mData->pMachineConfigFile->fileExists())
2496 // this is a new machine, and no config file exists yet:
2497 *aModified = TRUE;
2498 else
2499 *aModified = (mData->flModifications != 0);
2500
2501 return S_OK;
2502}
2503
2504STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2505{
2506 CheckComArgOutPointerValid(aSessionState);
2507
2508 AutoCaller autoCaller(this);
2509 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2510
2511 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2512
2513 *aSessionState = mData->mSession.mState;
2514
2515 return S_OK;
2516}
2517
2518STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2519{
2520 CheckComArgOutPointerValid(aSessionType);
2521
2522 AutoCaller autoCaller(this);
2523 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2524
2525 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2526
2527 mData->mSession.mType.cloneTo(aSessionType);
2528
2529 return S_OK;
2530}
2531
2532STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2533{
2534 CheckComArgOutPointerValid(aSessionPID);
2535
2536 AutoCaller autoCaller(this);
2537 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2538
2539 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2540
2541 *aSessionPID = mData->mSession.mPID;
2542
2543 return S_OK;
2544}
2545
2546STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2547{
2548 CheckComArgOutPointerValid(machineState);
2549
2550 AutoCaller autoCaller(this);
2551 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2552
2553 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2554
2555 *machineState = mData->mMachineState;
2556
2557 return S_OK;
2558}
2559
2560STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2561{
2562 CheckComArgOutPointerValid(aLastStateChange);
2563
2564 AutoCaller autoCaller(this);
2565 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2566
2567 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2568
2569 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2570
2571 return S_OK;
2572}
2573
2574STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2575{
2576 CheckComArgOutPointerValid(aStateFilePath);
2577
2578 AutoCaller autoCaller(this);
2579 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2580
2581 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2582
2583 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2584
2585 return S_OK;
2586}
2587
2588STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2589{
2590 CheckComArgOutPointerValid(aLogFolder);
2591
2592 AutoCaller autoCaller(this);
2593 AssertComRCReturnRC(autoCaller.rc());
2594
2595 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2596
2597 Utf8Str logFolder;
2598 getLogFolder(logFolder);
2599 logFolder.cloneTo(aLogFolder);
2600
2601 return S_OK;
2602}
2603
2604STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2605{
2606 CheckComArgOutPointerValid(aCurrentSnapshot);
2607
2608 AutoCaller autoCaller(this);
2609 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2610
2611 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2612
2613 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2614
2615 return S_OK;
2616}
2617
2618STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2619{
2620 CheckComArgOutPointerValid(aSnapshotCount);
2621
2622 AutoCaller autoCaller(this);
2623 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2624
2625 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2626
2627 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2628 ? 0
2629 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2630
2631 return S_OK;
2632}
2633
2634STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2635{
2636 CheckComArgOutPointerValid(aCurrentStateModified);
2637
2638 AutoCaller autoCaller(this);
2639 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2640
2641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2642
2643 /* Note: for machines with no snapshots, we always return FALSE
2644 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2645 * reasons :) */
2646
2647 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2648 ? FALSE
2649 : mData->mCurrentStateModified;
2650
2651 return S_OK;
2652}
2653
2654STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2655{
2656 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2657
2658 AutoCaller autoCaller(this);
2659 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2660
2661 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2662
2663 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2664 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2665
2666 return S_OK;
2667}
2668
2669STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2670{
2671 CheckComArgOutPointerValid(aClipboardMode);
2672
2673 AutoCaller autoCaller(this);
2674 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2675
2676 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2677
2678 *aClipboardMode = mHWData->mClipboardMode;
2679
2680 return S_OK;
2681}
2682
2683STDMETHODIMP
2684Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2685{
2686 HRESULT rc = S_OK;
2687
2688 AutoCaller autoCaller(this);
2689 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2690
2691 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2692
2693 alock.release();
2694 rc = onClipboardModeChange(aClipboardMode);
2695 alock.acquire();
2696 if (FAILED(rc)) return rc;
2697
2698 setModified(IsModified_MachineData);
2699 mHWData.backup();
2700 mHWData->mClipboardMode = aClipboardMode;
2701
2702 /* Save settings if online - todo why is this required?? */
2703 if (Global::IsOnline(mData->mMachineState))
2704 saveSettings(NULL);
2705
2706 return S_OK;
2707}
2708
2709STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
2710{
2711 CheckComArgOutPointerValid(aDragAndDropMode);
2712
2713 AutoCaller autoCaller(this);
2714 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2715
2716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2717
2718 *aDragAndDropMode = mHWData->mDragAndDropMode;
2719
2720 return S_OK;
2721}
2722
2723STDMETHODIMP
2724Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
2725{
2726 HRESULT rc = S_OK;
2727
2728 AutoCaller autoCaller(this);
2729 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2730
2731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2732
2733 alock.release();
2734 rc = onDragAndDropModeChange(aDragAndDropMode);
2735 alock.acquire();
2736 if (FAILED(rc)) return rc;
2737
2738 setModified(IsModified_MachineData);
2739 mHWData.backup();
2740 mHWData->mDragAndDropMode = aDragAndDropMode;
2741
2742 /* Save settings if online - todo why is this required?? */
2743 if (Global::IsOnline(mData->mMachineState))
2744 saveSettings(NULL);
2745
2746 return S_OK;
2747}
2748
2749STDMETHODIMP
2750Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
2751{
2752 CheckComArgOutPointerValid(aPatterns);
2753
2754 AutoCaller autoCaller(this);
2755 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2756
2757 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2758
2759 try
2760 {
2761 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
2762 }
2763 catch (...)
2764 {
2765 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2766 }
2767
2768 return S_OK;
2769}
2770
2771STDMETHODIMP
2772Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
2773{
2774 AutoCaller autoCaller(this);
2775 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2776
2777 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2778
2779 HRESULT rc = checkStateDependency(MutableStateDep);
2780 if (FAILED(rc)) return rc;
2781
2782 setModified(IsModified_MachineData);
2783 mHWData.backup();
2784 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
2785 return rc;
2786}
2787
2788STDMETHODIMP
2789Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
2790{
2791 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
2792
2793 AutoCaller autoCaller(this);
2794 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2795
2796 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2797
2798 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
2799 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
2800
2801 return S_OK;
2802}
2803
2804STDMETHODIMP
2805Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
2806{
2807 CheckComArgOutPointerValid(aEnabled);
2808
2809 AutoCaller autoCaller(this);
2810 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2811
2812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2813
2814 *aEnabled = mUserData->s.fTeleporterEnabled;
2815
2816 return S_OK;
2817}
2818
2819STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
2820{
2821 AutoCaller autoCaller(this);
2822 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2823
2824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2825
2826 /* Only allow it to be set to true when PoweredOff or Aborted.
2827 (Clearing it is always permitted.) */
2828 if ( aEnabled
2829 && mData->mRegistered
2830 && ( !isSessionMachine()
2831 || ( mData->mMachineState != MachineState_PoweredOff
2832 && mData->mMachineState != MachineState_Teleported
2833 && mData->mMachineState != MachineState_Aborted
2834 )
2835 )
2836 )
2837 return setError(VBOX_E_INVALID_VM_STATE,
2838 tr("The machine is not powered off (state is %s)"),
2839 Global::stringifyMachineState(mData->mMachineState));
2840
2841 setModified(IsModified_MachineData);
2842 mUserData.backup();
2843 mUserData->s.fTeleporterEnabled = !!aEnabled;
2844
2845 return S_OK;
2846}
2847
2848STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
2849{
2850 CheckComArgOutPointerValid(aPort);
2851
2852 AutoCaller autoCaller(this);
2853 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2854
2855 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2856
2857 *aPort = (ULONG)mUserData->s.uTeleporterPort;
2858
2859 return S_OK;
2860}
2861
2862STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
2863{
2864 if (aPort >= _64K)
2865 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
2866
2867 AutoCaller autoCaller(this);
2868 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2869
2870 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2871
2872 HRESULT rc = checkStateDependency(MutableStateDep);
2873 if (FAILED(rc)) return rc;
2874
2875 setModified(IsModified_MachineData);
2876 mUserData.backup();
2877 mUserData->s.uTeleporterPort = (uint32_t)aPort;
2878
2879 return S_OK;
2880}
2881
2882STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
2883{
2884 CheckComArgOutPointerValid(aAddress);
2885
2886 AutoCaller autoCaller(this);
2887 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2888
2889 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2890
2891 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
2892
2893 return S_OK;
2894}
2895
2896STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
2897{
2898 AutoCaller autoCaller(this);
2899 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2900
2901 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2902
2903 HRESULT rc = checkStateDependency(MutableStateDep);
2904 if (FAILED(rc)) return rc;
2905
2906 setModified(IsModified_MachineData);
2907 mUserData.backup();
2908 mUserData->s.strTeleporterAddress = aAddress;
2909
2910 return S_OK;
2911}
2912
2913STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
2914{
2915 CheckComArgOutPointerValid(aPassword);
2916
2917 AutoCaller autoCaller(this);
2918 HRESULT hrc = autoCaller.rc();
2919 if (SUCCEEDED(hrc))
2920 {
2921 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2922 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
2923 }
2924
2925 return hrc;
2926}
2927
2928STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
2929{
2930 /*
2931 * Hash the password first.
2932 */
2933 Utf8Str strPassword(aPassword);
2934 if (!strPassword.isEmpty())
2935 {
2936 if (VBoxIsPasswordHashed(&strPassword))
2937 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2938 VBoxHashPassword(&strPassword);
2939 }
2940
2941 /*
2942 * Do the update.
2943 */
2944 AutoCaller autoCaller(this);
2945 HRESULT hrc = autoCaller.rc();
2946 if (SUCCEEDED(hrc))
2947 {
2948 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2949 hrc = checkStateDependency(MutableStateDep);
2950 if (SUCCEEDED(hrc))
2951 {
2952 setModified(IsModified_MachineData);
2953 mUserData.backup();
2954 mUserData->s.strTeleporterPassword = strPassword;
2955 }
2956 }
2957
2958 return hrc;
2959}
2960
2961STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
2962{
2963 CheckComArgOutPointerValid(aState);
2964
2965 AutoCaller autoCaller(this);
2966 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2967
2968 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2969
2970 *aState = mUserData->s.enmFaultToleranceState;
2971 return S_OK;
2972}
2973
2974STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
2975{
2976 AutoCaller autoCaller(this);
2977 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2978
2979 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2980
2981 /* @todo deal with running state change. */
2982 HRESULT rc = checkStateDependency(MutableStateDep);
2983 if (FAILED(rc)) return rc;
2984
2985 setModified(IsModified_MachineData);
2986 mUserData.backup();
2987 mUserData->s.enmFaultToleranceState = aState;
2988 return S_OK;
2989}
2990
2991STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
2992{
2993 CheckComArgOutPointerValid(aAddress);
2994
2995 AutoCaller autoCaller(this);
2996 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2997
2998 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2999
3000 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3001 return S_OK;
3002}
3003
3004STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3005{
3006 AutoCaller autoCaller(this);
3007 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3008
3009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3010
3011 /* @todo deal with running state change. */
3012 HRESULT rc = checkStateDependency(MutableStateDep);
3013 if (FAILED(rc)) return rc;
3014
3015 setModified(IsModified_MachineData);
3016 mUserData.backup();
3017 mUserData->s.strFaultToleranceAddress = aAddress;
3018 return S_OK;
3019}
3020
3021STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3022{
3023 CheckComArgOutPointerValid(aPort);
3024
3025 AutoCaller autoCaller(this);
3026 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3027
3028 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3029
3030 *aPort = mUserData->s.uFaultTolerancePort;
3031 return S_OK;
3032}
3033
3034STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3035{
3036 AutoCaller autoCaller(this);
3037 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3038
3039 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3040
3041 /* @todo deal with running state change. */
3042 HRESULT rc = checkStateDependency(MutableStateDep);
3043 if (FAILED(rc)) return rc;
3044
3045 setModified(IsModified_MachineData);
3046 mUserData.backup();
3047 mUserData->s.uFaultTolerancePort = aPort;
3048 return S_OK;
3049}
3050
3051STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3052{
3053 CheckComArgOutPointerValid(aPassword);
3054
3055 AutoCaller autoCaller(this);
3056 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3057
3058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3059
3060 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3061
3062 return S_OK;
3063}
3064
3065STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3066{
3067 AutoCaller autoCaller(this);
3068 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3069
3070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3071
3072 /* @todo deal with running state change. */
3073 HRESULT rc = checkStateDependency(MutableStateDep);
3074 if (FAILED(rc)) return rc;
3075
3076 setModified(IsModified_MachineData);
3077 mUserData.backup();
3078 mUserData->s.strFaultTolerancePassword = aPassword;
3079
3080 return S_OK;
3081}
3082
3083STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3084{
3085 CheckComArgOutPointerValid(aInterval);
3086
3087 AutoCaller autoCaller(this);
3088 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3089
3090 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3091
3092 *aInterval = mUserData->s.uFaultToleranceInterval;
3093 return S_OK;
3094}
3095
3096STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3097{
3098 AutoCaller autoCaller(this);
3099 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3100
3101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3102
3103 /* @todo deal with running state change. */
3104 HRESULT rc = checkStateDependency(MutableStateDep);
3105 if (FAILED(rc)) return rc;
3106
3107 setModified(IsModified_MachineData);
3108 mUserData.backup();
3109 mUserData->s.uFaultToleranceInterval = aInterval;
3110 return S_OK;
3111}
3112
3113STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3114{
3115 CheckComArgOutPointerValid(aEnabled);
3116
3117 AutoCaller autoCaller(this);
3118 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3119
3120 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3121
3122 *aEnabled = mUserData->s.fRTCUseUTC;
3123
3124 return S_OK;
3125}
3126
3127STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3128{
3129 AutoCaller autoCaller(this);
3130 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3131
3132 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3133
3134 /* Only allow it to be set to true when PoweredOff or Aborted.
3135 (Clearing it is always permitted.) */
3136 if ( aEnabled
3137 && mData->mRegistered
3138 && ( !isSessionMachine()
3139 || ( mData->mMachineState != MachineState_PoweredOff
3140 && mData->mMachineState != MachineState_Teleported
3141 && mData->mMachineState != MachineState_Aborted
3142 )
3143 )
3144 )
3145 return setError(VBOX_E_INVALID_VM_STATE,
3146 tr("The machine is not powered off (state is %s)"),
3147 Global::stringifyMachineState(mData->mMachineState));
3148
3149 setModified(IsModified_MachineData);
3150 mUserData.backup();
3151 mUserData->s.fRTCUseUTC = !!aEnabled;
3152
3153 return S_OK;
3154}
3155
3156STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3157{
3158 CheckComArgOutPointerValid(aEnabled);
3159
3160 AutoCaller autoCaller(this);
3161 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3162
3163 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3164
3165 *aEnabled = mHWData->mIOCacheEnabled;
3166
3167 return S_OK;
3168}
3169
3170STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3171{
3172 AutoCaller autoCaller(this);
3173 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3174
3175 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3176
3177 HRESULT rc = checkStateDependency(MutableStateDep);
3178 if (FAILED(rc)) return rc;
3179
3180 setModified(IsModified_MachineData);
3181 mHWData.backup();
3182 mHWData->mIOCacheEnabled = aEnabled;
3183
3184 return S_OK;
3185}
3186
3187STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3188{
3189 CheckComArgOutPointerValid(aIOCacheSize);
3190
3191 AutoCaller autoCaller(this);
3192 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3193
3194 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3195
3196 *aIOCacheSize = mHWData->mIOCacheSize;
3197
3198 return S_OK;
3199}
3200
3201STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3202{
3203 AutoCaller autoCaller(this);
3204 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3205
3206 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3207
3208 HRESULT rc = checkStateDependency(MutableStateDep);
3209 if (FAILED(rc)) return rc;
3210
3211 setModified(IsModified_MachineData);
3212 mHWData.backup();
3213 mHWData->mIOCacheSize = aIOCacheSize;
3214
3215 return S_OK;
3216}
3217
3218
3219/**
3220 * @note Locks objects!
3221 */
3222STDMETHODIMP Machine::LockMachine(ISession *aSession,
3223 LockType_T lockType)
3224{
3225 CheckComArgNotNull(aSession);
3226
3227 AutoCaller autoCaller(this);
3228 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3229
3230 /* check the session state */
3231 SessionState_T state;
3232 HRESULT rc = aSession->COMGETTER(State)(&state);
3233 if (FAILED(rc)) return rc;
3234
3235 if (state != SessionState_Unlocked)
3236 return setError(VBOX_E_INVALID_OBJECT_STATE,
3237 tr("The given session is busy"));
3238
3239 // get the client's IInternalSessionControl interface
3240 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3241 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3242 E_INVALIDARG);
3243
3244 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3245
3246 if (!mData->mRegistered)
3247 return setError(E_UNEXPECTED,
3248 tr("The machine '%s' is not registered"),
3249 mUserData->s.strName.c_str());
3250
3251 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3252
3253 SessionState_T oldState = mData->mSession.mState;
3254 /* Hack: in case the session is closing and there is a progress object
3255 * which allows waiting for the session to be closed, take the opportunity
3256 * and do a limited wait (max. 1 second). This helps a lot when the system
3257 * is busy and thus session closing can take a little while. */
3258 if ( mData->mSession.mState == SessionState_Unlocking
3259 && mData->mSession.mProgress)
3260 {
3261 alock.release();
3262 mData->mSession.mProgress->WaitForCompletion(1000);
3263 alock.acquire();
3264 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3265 }
3266
3267 // try again now
3268 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3269 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3270 )
3271 {
3272 // OK, share the session... we are now dealing with three processes:
3273 // 1) VBoxSVC (where this code runs);
3274 // 2) process C: the caller's client process (who wants a shared session);
3275 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3276
3277 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3278 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3279 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3280 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3281 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3282
3283 /*
3284 * Release the lock before calling the client process. It's safe here
3285 * since the only thing to do after we get the lock again is to add
3286 * the remote control to the list (which doesn't directly influence
3287 * anything).
3288 */
3289 alock.release();
3290
3291 // get the console of the session holding the write lock (this is a remote call)
3292 ComPtr<IConsole> pConsoleW;
3293 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3294 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3295 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3296 if (FAILED(rc))
3297 // the failure may occur w/o any error info (from RPC), so provide one
3298 return setError(VBOX_E_VM_ERROR,
3299 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3300
3301 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3302
3303 // share the session machine and W's console with the caller's session
3304 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3305 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3306 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3307
3308 if (FAILED(rc))
3309 // the failure may occur w/o any error info (from RPC), so provide one
3310 return setError(VBOX_E_VM_ERROR,
3311 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3312 alock.acquire();
3313
3314 // need to revalidate the state after acquiring the lock again
3315 if (mData->mSession.mState != SessionState_Locked)
3316 {
3317 pSessionControl->Uninitialize();
3318 return setError(VBOX_E_INVALID_SESSION_STATE,
3319 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3320 mUserData->s.strName.c_str());
3321 }
3322
3323 // add the caller's session to the list
3324 mData->mSession.mRemoteControls.push_back(pSessionControl);
3325 }
3326 else if ( mData->mSession.mState == SessionState_Locked
3327 || mData->mSession.mState == SessionState_Unlocking
3328 )
3329 {
3330 // sharing not permitted, or machine still unlocking:
3331 return setError(VBOX_E_INVALID_OBJECT_STATE,
3332 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3333 mUserData->s.strName.c_str());
3334 }
3335 else
3336 {
3337 // machine is not locked: then write-lock the machine (create the session machine)
3338
3339 // must not be busy
3340 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3341
3342 // get the caller's session PID
3343 RTPROCESS pid = NIL_RTPROCESS;
3344 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3345 pSessionControl->GetPID((ULONG*)&pid);
3346 Assert(pid != NIL_RTPROCESS);
3347
3348 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3349
3350 if (fLaunchingVMProcess)
3351 {
3352 // this machine is awaiting for a spawning session to be opened:
3353 // then the calling process must be the one that got started by
3354 // LaunchVMProcess()
3355
3356 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3357 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3358
3359 if (mData->mSession.mPID != pid)
3360 return setError(E_ACCESSDENIED,
3361 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3362 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3363 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3364 }
3365
3366 // create the mutable SessionMachine from the current machine
3367 ComObjPtr<SessionMachine> sessionMachine;
3368 sessionMachine.createObject();
3369 rc = sessionMachine->init(this);
3370 AssertComRC(rc);
3371
3372 /* NOTE: doing return from this function after this point but
3373 * before the end is forbidden since it may call SessionMachine::uninit()
3374 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3375 * lock while still holding the Machine lock in alock so that a deadlock
3376 * is possible due to the wrong lock order. */
3377
3378 if (SUCCEEDED(rc))
3379 {
3380 /*
3381 * Set the session state to Spawning to protect against subsequent
3382 * attempts to open a session and to unregister the machine after
3383 * we release the lock.
3384 */
3385 SessionState_T origState = mData->mSession.mState;
3386 mData->mSession.mState = SessionState_Spawning;
3387
3388 /*
3389 * Release the lock before calling the client process -- it will call
3390 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3391 * because the state is Spawning, so that LaunchVMProcess() and
3392 * LockMachine() calls will fail. This method, called before we
3393 * acquire the lock again, will fail because of the wrong PID.
3394 *
3395 * Note that mData->mSession.mRemoteControls accessed outside
3396 * the lock may not be modified when state is Spawning, so it's safe.
3397 */
3398 alock.release();
3399
3400 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3401 rc = pSessionControl->AssignMachine(sessionMachine, lockType);
3402 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3403
3404 /* The failure may occur w/o any error info (from RPC), so provide one */
3405 if (FAILED(rc))
3406 setError(VBOX_E_VM_ERROR,
3407 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3408
3409 if ( SUCCEEDED(rc)
3410 && fLaunchingVMProcess
3411 )
3412 {
3413 /* complete the remote session initialization */
3414
3415 /* get the console from the direct session */
3416 ComPtr<IConsole> console;
3417 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3418 ComAssertComRC(rc);
3419
3420 if (SUCCEEDED(rc) && !console)
3421 {
3422 ComAssert(!!console);
3423 rc = E_FAIL;
3424 }
3425
3426 /* assign machine & console to the remote session */
3427 if (SUCCEEDED(rc))
3428 {
3429 /*
3430 * after LaunchVMProcess(), the first and the only
3431 * entry in remoteControls is that remote session
3432 */
3433 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3434 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3435 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3436
3437 /* The failure may occur w/o any error info (from RPC), so provide one */
3438 if (FAILED(rc))
3439 setError(VBOX_E_VM_ERROR,
3440 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3441 }
3442
3443 if (FAILED(rc))
3444 pSessionControl->Uninitialize();
3445 }
3446
3447 /* acquire the lock again */
3448 alock.acquire();
3449
3450 /* Restore the session state */
3451 mData->mSession.mState = origState;
3452 }
3453
3454 // finalize spawning anyway (this is why we don't return on errors above)
3455 if (fLaunchingVMProcess)
3456 {
3457 /* Note that the progress object is finalized later */
3458 /** @todo Consider checking mData->mSession.mProgress for cancellation
3459 * around here. */
3460
3461 /* We don't reset mSession.mPID here because it is necessary for
3462 * SessionMachine::uninit() to reap the child process later. */
3463
3464 if (FAILED(rc))
3465 {
3466 /* Close the remote session, remove the remote control from the list
3467 * and reset session state to Closed (@note keep the code in sync
3468 * with the relevant part in openSession()). */
3469
3470 Assert(mData->mSession.mRemoteControls.size() == 1);
3471 if (mData->mSession.mRemoteControls.size() == 1)
3472 {
3473 ErrorInfoKeeper eik;
3474 mData->mSession.mRemoteControls.front()->Uninitialize();
3475 }
3476
3477 mData->mSession.mRemoteControls.clear();
3478 mData->mSession.mState = SessionState_Unlocked;
3479 }
3480 }
3481 else
3482 {
3483 /* memorize PID of the directly opened session */
3484 if (SUCCEEDED(rc))
3485 mData->mSession.mPID = pid;
3486 }
3487
3488 if (SUCCEEDED(rc))
3489 {
3490 /* memorize the direct session control and cache IUnknown for it */
3491 mData->mSession.mDirectControl = pSessionControl;
3492 mData->mSession.mState = SessionState_Locked;
3493 /* associate the SessionMachine with this Machine */
3494 mData->mSession.mMachine = sessionMachine;
3495
3496 /* request an IUnknown pointer early from the remote party for later
3497 * identity checks (it will be internally cached within mDirectControl
3498 * at least on XPCOM) */
3499 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3500 NOREF(unk);
3501 }
3502
3503 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3504 * would break the lock order */
3505 alock.release();
3506
3507 /* uninitialize the created session machine on failure */
3508 if (FAILED(rc))
3509 sessionMachine->uninit();
3510
3511 }
3512
3513 if (SUCCEEDED(rc))
3514 {
3515 /*
3516 * tell the client watcher thread to update the set of
3517 * machines that have open sessions
3518 */
3519 mParent->updateClientWatcher();
3520
3521 if (oldState != SessionState_Locked)
3522 /* fire an event */
3523 mParent->onSessionStateChange(getId(), SessionState_Locked);
3524 }
3525
3526 return rc;
3527}
3528
3529/**
3530 * @note Locks objects!
3531 */
3532STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3533 IN_BSTR aType,
3534 IN_BSTR aEnvironment,
3535 IProgress **aProgress)
3536{
3537 CheckComArgStrNotEmptyOrNull(aType);
3538 Utf8Str strType(aType);
3539 Utf8Str strEnvironment(aEnvironment);
3540 /* "emergencystop" doesn't need the session, so skip the checks/interface
3541 * retrieval. This code doesn't quite fit in here, but introducing a
3542 * special API method would be even more effort, and would require explicit
3543 * support by every API client. It's better to hide the feature a bit. */
3544 if (strType != "emergencystop")
3545 CheckComArgNotNull(aSession);
3546 CheckComArgOutPointerValid(aProgress);
3547
3548 AutoCaller autoCaller(this);
3549 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3550
3551 ComPtr<IInternalSessionControl> control;
3552 HRESULT rc = S_OK;
3553
3554 if (strType != "emergencystop")
3555 {
3556 /* check the session state */
3557 SessionState_T state;
3558 rc = aSession->COMGETTER(State)(&state);
3559 if (FAILED(rc))
3560 return rc;
3561
3562 if (state != SessionState_Unlocked)
3563 return setError(VBOX_E_INVALID_OBJECT_STATE,
3564 tr("The given session is busy"));
3565
3566 /* get the IInternalSessionControl interface */
3567 control = aSession;
3568 ComAssertMsgRet(!control.isNull(),
3569 ("No IInternalSessionControl interface"),
3570 E_INVALIDARG);
3571 }
3572
3573 /* get the teleporter enable state for the progress object init. */
3574 BOOL fTeleporterEnabled;
3575 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3576 if (FAILED(rc))
3577 return rc;
3578
3579 /* create a progress object */
3580 if (strType != "emergencystop")
3581 {
3582 ComObjPtr<ProgressProxy> progress;
3583 progress.createObject();
3584 rc = progress->init(mParent,
3585 static_cast<IMachine*>(this),
3586 Bstr(tr("Starting VM")).raw(),
3587 TRUE /* aCancelable */,
3588 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3589 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strType.c_str()).raw(),
3590 2 /* uFirstOperationWeight */,
3591 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3592
3593 if (SUCCEEDED(rc))
3594 {
3595 rc = launchVMProcess(control, strType, strEnvironment, progress);
3596 if (SUCCEEDED(rc))
3597 {
3598 progress.queryInterfaceTo(aProgress);
3599
3600 /* signal the client watcher thread */
3601 mParent->updateClientWatcher();
3602
3603 /* fire an event */
3604 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3605 }
3606 }
3607 }
3608 else
3609 {
3610 /* no progress object - either instant success or failure */
3611 *aProgress = NULL;
3612
3613 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3614
3615 if (mData->mSession.mState != SessionState_Locked)
3616 return setError(VBOX_E_INVALID_OBJECT_STATE,
3617 tr("The machine '%s' is not locked by a session"),
3618 mUserData->s.strName.c_str());
3619
3620 /* must have a VM process associated - do not kill normal API clients
3621 * with an open session */
3622 if (!Global::IsOnline(mData->mMachineState))
3623 return setError(VBOX_E_INVALID_OBJECT_STATE,
3624 tr("The machine '%s' does not have a VM process"),
3625 mUserData->s.strName.c_str());
3626
3627 /* forcibly terminate the VM process */
3628 if (mData->mSession.mPID != NIL_RTPROCESS)
3629 RTProcTerminate(mData->mSession.mPID);
3630
3631 /* signal the client watcher thread, as most likely the client has
3632 * been terminated */
3633 mParent->updateClientWatcher();
3634 }
3635
3636 return rc;
3637}
3638
3639STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3640{
3641 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3642 return setError(E_INVALIDARG,
3643 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3644 aPosition, SchemaDefs::MaxBootPosition);
3645
3646 if (aDevice == DeviceType_USB)
3647 return setError(E_NOTIMPL,
3648 tr("Booting from USB device is currently not supported"));
3649
3650 AutoCaller autoCaller(this);
3651 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3652
3653 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3654
3655 HRESULT rc = checkStateDependency(MutableStateDep);
3656 if (FAILED(rc)) return rc;
3657
3658 setModified(IsModified_MachineData);
3659 mHWData.backup();
3660 mHWData->mBootOrder[aPosition - 1] = aDevice;
3661
3662 return S_OK;
3663}
3664
3665STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3666{
3667 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3668 return setError(E_INVALIDARG,
3669 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3670 aPosition, SchemaDefs::MaxBootPosition);
3671
3672 AutoCaller autoCaller(this);
3673 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3674
3675 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3676
3677 *aDevice = mHWData->mBootOrder[aPosition - 1];
3678
3679 return S_OK;
3680}
3681
3682STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
3683 LONG aControllerPort,
3684 LONG aDevice,
3685 DeviceType_T aType,
3686 IMedium *aMedium)
3687{
3688 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3689 aControllerName, aControllerPort, aDevice, aType, aMedium));
3690
3691 CheckComArgStrNotEmptyOrNull(aControllerName);
3692
3693 AutoCaller autoCaller(this);
3694 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3695
3696 // request the host lock first, since might be calling Host methods for getting host drives;
3697 // next, protect the media tree all the while we're in here, as well as our member variables
3698 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
3699 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3700
3701 HRESULT rc = checkStateDependency(MutableStateDep);
3702 if (FAILED(rc)) return rc;
3703
3704 /// @todo NEWMEDIA implicit machine registration
3705 if (!mData->mRegistered)
3706 return setError(VBOX_E_INVALID_OBJECT_STATE,
3707 tr("Cannot attach storage devices to an unregistered machine"));
3708
3709 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3710
3711 /* Check for an existing controller. */
3712 ComObjPtr<StorageController> ctl;
3713 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
3714 if (FAILED(rc)) return rc;
3715
3716 StorageControllerType_T ctrlType;
3717 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3718 if (FAILED(rc))
3719 return setError(E_FAIL,
3720 tr("Could not get type of controller '%ls'"),
3721 aControllerName);
3722
3723 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3724 bool fHotplug = false;
3725 if (Global::IsOnlineOrTransient(mData->mMachineState))
3726 fHotplug = true;
3727
3728 if (fHotplug && !isControllerHotplugCapable(ctrlType))
3729 return setError(VBOX_E_INVALID_VM_STATE,
3730 tr("Controller '%ls' does not support hotplugging"),
3731 aControllerName);
3732
3733 // check that the port and device are not out of range
3734 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
3735 if (FAILED(rc)) return rc;
3736
3737 /* check if the device slot is already busy */
3738 MediumAttachment *pAttachTemp;
3739 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
3740 aControllerName,
3741 aControllerPort,
3742 aDevice)))
3743 {
3744 Medium *pMedium = pAttachTemp->getMedium();
3745 if (pMedium)
3746 {
3747 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3748 return setError(VBOX_E_OBJECT_IN_USE,
3749 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3750 pMedium->getLocationFull().c_str(),
3751 aControllerPort,
3752 aDevice,
3753 aControllerName);
3754 }
3755 else
3756 return setError(VBOX_E_OBJECT_IN_USE,
3757 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3758 aControllerPort, aDevice, aControllerName);
3759 }
3760
3761 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
3762 if (aMedium && medium.isNull())
3763 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3764
3765 AutoCaller mediumCaller(medium);
3766 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3767
3768 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3769
3770 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
3771 && !medium.isNull()
3772 )
3773 return setError(VBOX_E_OBJECT_IN_USE,
3774 tr("Medium '%s' is already attached to this virtual machine"),
3775 medium->getLocationFull().c_str());
3776
3777 if (!medium.isNull())
3778 {
3779 MediumType_T mtype = medium->getType();
3780 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3781 // For DVDs it's not written to the config file, so needs no global config
3782 // version bump. For floppies it's a new attribute "type", which is ignored
3783 // by older VirtualBox version, so needs no global config version bump either.
3784 // For hard disks this type is not accepted.
3785 if (mtype == MediumType_MultiAttach)
3786 {
3787 // This type is new with VirtualBox 4.0 and therefore requires settings
3788 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3789 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3790 // two reasons: The medium type is a property of the media registry tree, which
3791 // can reside in the global config file (for pre-4.0 media); we would therefore
3792 // possibly need to bump the global config version. We don't want to do that though
3793 // because that might make downgrading to pre-4.0 impossible.
3794 // As a result, we can only use these two new types if the medium is NOT in the
3795 // global registry:
3796 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
3797 if ( medium->isInRegistry(uuidGlobalRegistry)
3798 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3799 )
3800 return setError(VBOX_E_INVALID_OBJECT_STATE,
3801 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3802 "to machines that were created with VirtualBox 4.0 or later"),
3803 medium->getLocationFull().c_str());
3804 }
3805 }
3806
3807 bool fIndirect = false;
3808 if (!medium.isNull())
3809 fIndirect = medium->isReadOnly();
3810 bool associate = true;
3811
3812 do
3813 {
3814 if ( aType == DeviceType_HardDisk
3815 && mMediaData.isBackedUp())
3816 {
3817 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3818
3819 /* check if the medium was attached to the VM before we started
3820 * changing attachments in which case the attachment just needs to
3821 * be restored */
3822 if ((pAttachTemp = findAttachment(oldAtts, medium)))
3823 {
3824 AssertReturn(!fIndirect, E_FAIL);
3825
3826 /* see if it's the same bus/channel/device */
3827 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
3828 {
3829 /* the simplest case: restore the whole attachment
3830 * and return, nothing else to do */
3831 mMediaData->mAttachments.push_back(pAttachTemp);
3832 return S_OK;
3833 }
3834
3835 /* bus/channel/device differ; we need a new attachment object,
3836 * but don't try to associate it again */
3837 associate = false;
3838 break;
3839 }
3840 }
3841
3842 /* go further only if the attachment is to be indirect */
3843 if (!fIndirect)
3844 break;
3845
3846 /* perform the so called smart attachment logic for indirect
3847 * attachments. Note that smart attachment is only applicable to base
3848 * hard disks. */
3849
3850 if (medium->getParent().isNull())
3851 {
3852 /* first, investigate the backup copy of the current hard disk
3853 * attachments to make it possible to re-attach existing diffs to
3854 * another device slot w/o losing their contents */
3855 if (mMediaData.isBackedUp())
3856 {
3857 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3858
3859 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3860 uint32_t foundLevel = 0;
3861
3862 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
3863 it != oldAtts.end();
3864 ++it)
3865 {
3866 uint32_t level = 0;
3867 MediumAttachment *pAttach = *it;
3868 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3869 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3870 if (pMedium.isNull())
3871 continue;
3872
3873 if (pMedium->getBase(&level) == medium)
3874 {
3875 /* skip the hard disk if its currently attached (we
3876 * cannot attach the same hard disk twice) */
3877 if (findAttachment(mMediaData->mAttachments,
3878 pMedium))
3879 continue;
3880
3881 /* matched device, channel and bus (i.e. attached to the
3882 * same place) will win and immediately stop the search;
3883 * otherwise the attachment that has the youngest
3884 * descendant of medium will be used
3885 */
3886 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
3887 {
3888 /* the simplest case: restore the whole attachment
3889 * and return, nothing else to do */
3890 mMediaData->mAttachments.push_back(*it);
3891 return S_OK;
3892 }
3893 else if ( foundIt == oldAtts.end()
3894 || level > foundLevel /* prefer younger */
3895 )
3896 {
3897 foundIt = it;
3898 foundLevel = level;
3899 }
3900 }
3901 }
3902
3903 if (foundIt != oldAtts.end())
3904 {
3905 /* use the previously attached hard disk */
3906 medium = (*foundIt)->getMedium();
3907 mediumCaller.attach(medium);
3908 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3909 mediumLock.attach(medium);
3910 /* not implicit, doesn't require association with this VM */
3911 fIndirect = false;
3912 associate = false;
3913 /* go right to the MediumAttachment creation */
3914 break;
3915 }
3916 }
3917
3918 /* must give up the medium lock and medium tree lock as below we
3919 * go over snapshots, which needs a lock with higher lock order. */
3920 mediumLock.release();
3921 treeLock.release();
3922
3923 /* then, search through snapshots for the best diff in the given
3924 * hard disk's chain to base the new diff on */
3925
3926 ComObjPtr<Medium> base;
3927 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3928 while (snap)
3929 {
3930 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3931
3932 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
3933
3934 MediumAttachment *pAttachFound = NULL;
3935 uint32_t foundLevel = 0;
3936
3937 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
3938 it != snapAtts.end();
3939 ++it)
3940 {
3941 MediumAttachment *pAttach = *it;
3942 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3943 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3944 if (pMedium.isNull())
3945 continue;
3946
3947 uint32_t level = 0;
3948 if (pMedium->getBase(&level) == medium)
3949 {
3950 /* matched device, channel and bus (i.e. attached to the
3951 * same place) will win and immediately stop the search;
3952 * otherwise the attachment that has the youngest
3953 * descendant of medium will be used
3954 */
3955 if ( pAttach->getDevice() == aDevice
3956 && pAttach->getPort() == aControllerPort
3957 && pAttach->getControllerName() == aControllerName
3958 )
3959 {
3960 pAttachFound = pAttach;
3961 break;
3962 }
3963 else if ( !pAttachFound
3964 || level > foundLevel /* prefer younger */
3965 )
3966 {
3967 pAttachFound = pAttach;
3968 foundLevel = level;
3969 }
3970 }
3971 }
3972
3973 if (pAttachFound)
3974 {
3975 base = pAttachFound->getMedium();
3976 break;
3977 }
3978
3979 snap = snap->getParent();
3980 }
3981
3982 /* re-lock medium tree and the medium, as we need it below */
3983 treeLock.acquire();
3984 mediumLock.acquire();
3985
3986 /* found a suitable diff, use it as a base */
3987 if (!base.isNull())
3988 {
3989 medium = base;
3990 mediumCaller.attach(medium);
3991 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3992 mediumLock.attach(medium);
3993 }
3994 }
3995
3996 Utf8Str strFullSnapshotFolder;
3997 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3998
3999 ComObjPtr<Medium> diff;
4000 diff.createObject();
4001 // store this diff in the same registry as the parent
4002 Guid uuidRegistryParent;
4003 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4004 {
4005 // parent image has no registry: this can happen if we're attaching a new immutable
4006 // image that has not yet been attached (medium then points to the base and we're
4007 // creating the diff image for the immutable, and the parent is not yet registered);
4008 // put the parent in the machine registry then
4009 mediumLock.release();
4010 treeLock.release();
4011 alock.release();
4012 addMediumToRegistry(medium);
4013 alock.acquire();
4014 treeLock.acquire();
4015 mediumLock.acquire();
4016 medium->getFirstRegistryMachineId(uuidRegistryParent);
4017 }
4018 rc = diff->init(mParent,
4019 medium->getPreferredDiffFormat(),
4020 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4021 uuidRegistryParent);
4022 if (FAILED(rc)) return rc;
4023
4024 /* Apply the normal locking logic to the entire chain. */
4025 MediumLockList *pMediumLockList(new MediumLockList());
4026 mediumLock.release();
4027 treeLock.release();
4028 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4029 true /* fMediumLockWrite */,
4030 medium,
4031 *pMediumLockList);
4032 treeLock.acquire();
4033 mediumLock.acquire();
4034 if (SUCCEEDED(rc))
4035 {
4036 mediumLock.release();
4037 treeLock.release();
4038 rc = pMediumLockList->Lock();
4039 treeLock.acquire();
4040 mediumLock.acquire();
4041 if (FAILED(rc))
4042 setError(rc,
4043 tr("Could not lock medium when creating diff '%s'"),
4044 diff->getLocationFull().c_str());
4045 else
4046 {
4047 /* will release the lock before the potentially lengthy
4048 * operation, so protect with the special state */
4049 MachineState_T oldState = mData->mMachineState;
4050 setMachineState(MachineState_SettingUp);
4051
4052 mediumLock.release();
4053 treeLock.release();
4054 alock.release();
4055
4056 rc = medium->createDiffStorage(diff,
4057 MediumVariant_Standard,
4058 pMediumLockList,
4059 NULL /* aProgress */,
4060 true /* aWait */);
4061
4062 alock.acquire();
4063 treeLock.acquire();
4064 mediumLock.acquire();
4065
4066 setMachineState(oldState);
4067 }
4068 }
4069
4070 /* Unlock the media and free the associated memory. */
4071 delete pMediumLockList;
4072
4073 if (FAILED(rc)) return rc;
4074
4075 /* use the created diff for the actual attachment */
4076 medium = diff;
4077 mediumCaller.attach(medium);
4078 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4079 mediumLock.attach(medium);
4080 }
4081 while (0);
4082
4083 ComObjPtr<MediumAttachment> attachment;
4084 attachment.createObject();
4085 rc = attachment->init(this,
4086 medium,
4087 aControllerName,
4088 aControllerPort,
4089 aDevice,
4090 aType,
4091 fIndirect,
4092 false /* fPassthrough */,
4093 false /* fTempEject */,
4094 false /* fNonRotational */,
4095 false /* fDiscard */,
4096 Utf8Str::Empty);
4097 if (FAILED(rc)) return rc;
4098
4099 if (associate && !medium.isNull())
4100 {
4101 // as the last step, associate the medium to the VM
4102 rc = medium->addBackReference(mData->mUuid);
4103 // here we can fail because of Deleting, or being in process of creating a Diff
4104 if (FAILED(rc)) return rc;
4105
4106 mediumLock.release();
4107 treeLock.release();
4108 alock.release();
4109 addMediumToRegistry(medium);
4110 alock.acquire();
4111 treeLock.acquire();
4112 mediumLock.acquire();
4113 }
4114
4115 /* success: finally remember the attachment */
4116 setModified(IsModified_Storage);
4117 mMediaData.backup();
4118 mMediaData->mAttachments.push_back(attachment);
4119
4120 mediumLock.release();
4121 treeLock.release();
4122 alock.release();
4123
4124 if (fHotplug)
4125 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */);
4126
4127 mParent->saveModifiedRegistries();
4128
4129 return rc;
4130}
4131
4132STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4133 LONG aDevice)
4134{
4135 CheckComArgStrNotEmptyOrNull(aControllerName);
4136
4137 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4138 aControllerName, aControllerPort, aDevice));
4139
4140 AutoCaller autoCaller(this);
4141 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4142
4143 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4144
4145 HRESULT rc = checkStateDependency(MutableStateDep);
4146 if (FAILED(rc)) return rc;
4147
4148 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4149
4150 /* Check for an existing controller. */
4151 ComObjPtr<StorageController> ctl;
4152 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4153 if (FAILED(rc)) return rc;
4154
4155 StorageControllerType_T ctrlType;
4156 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4157 if (FAILED(rc))
4158 return setError(E_FAIL,
4159 tr("Could not get type of controller '%ls'"),
4160 aControllerName);
4161
4162 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4163 bool fHotplug = false;
4164 if (Global::IsOnlineOrTransient(mData->mMachineState))
4165 fHotplug = true;
4166
4167 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4168 return setError(VBOX_E_INVALID_VM_STATE,
4169 tr("Controller '%ls' does not support hotplugging"),
4170 aControllerName);
4171
4172 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4173 aControllerName,
4174 aControllerPort,
4175 aDevice);
4176 if (!pAttach)
4177 return setError(VBOX_E_OBJECT_NOT_FOUND,
4178 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4179 aDevice, aControllerPort, aControllerName);
4180
4181 /*
4182 * The VM has to detach the device before we delete any implicit diffs.
4183 * If this fails we can roll back without loosing data.
4184 */
4185 if (fHotplug)
4186 {
4187 alock.release();
4188 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */);
4189 alock.acquire();
4190 }
4191 if (FAILED(rc)) return rc;
4192
4193 /* If we are here everything went well and we can delete the implicit now. */
4194 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4195
4196 alock.release();
4197
4198 mParent->saveModifiedRegistries();
4199
4200 return rc;
4201}
4202
4203STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4204 LONG aDevice, BOOL aPassthrough)
4205{
4206 CheckComArgStrNotEmptyOrNull(aControllerName);
4207
4208 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4209 aControllerName, aControllerPort, aDevice, aPassthrough));
4210
4211 AutoCaller autoCaller(this);
4212 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4213
4214 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4215
4216 HRESULT rc = checkStateDependency(MutableStateDep);
4217 if (FAILED(rc)) return rc;
4218
4219 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4220
4221 if (Global::IsOnlineOrTransient(mData->mMachineState))
4222 return setError(VBOX_E_INVALID_VM_STATE,
4223 tr("Invalid machine state: %s"),
4224 Global::stringifyMachineState(mData->mMachineState));
4225
4226 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4227 aControllerName,
4228 aControllerPort,
4229 aDevice);
4230 if (!pAttach)
4231 return setError(VBOX_E_OBJECT_NOT_FOUND,
4232 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4233 aDevice, aControllerPort, aControllerName);
4234
4235
4236 setModified(IsModified_Storage);
4237 mMediaData.backup();
4238
4239 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4240
4241 if (pAttach->getType() != DeviceType_DVD)
4242 return setError(E_INVALIDARG,
4243 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4244 aDevice, aControllerPort, aControllerName);
4245 pAttach->updatePassthrough(!!aPassthrough);
4246
4247 return S_OK;
4248}
4249
4250STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4251 LONG aDevice, BOOL aTemporaryEject)
4252{
4253 CheckComArgStrNotEmptyOrNull(aControllerName);
4254
4255 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4256 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4257
4258 AutoCaller autoCaller(this);
4259 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4260
4261 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4262
4263 HRESULT rc = checkStateDependency(MutableStateDep);
4264 if (FAILED(rc)) return rc;
4265
4266 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4267 aControllerName,
4268 aControllerPort,
4269 aDevice);
4270 if (!pAttach)
4271 return setError(VBOX_E_OBJECT_NOT_FOUND,
4272 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4273 aDevice, aControllerPort, aControllerName);
4274
4275
4276 setModified(IsModified_Storage);
4277 mMediaData.backup();
4278
4279 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4280
4281 if (pAttach->getType() != DeviceType_DVD)
4282 return setError(E_INVALIDARG,
4283 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4284 aDevice, aControllerPort, aControllerName);
4285 pAttach->updateTempEject(!!aTemporaryEject);
4286
4287 return S_OK;
4288}
4289
4290STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4291 LONG aDevice, BOOL aNonRotational)
4292{
4293 CheckComArgStrNotEmptyOrNull(aControllerName);
4294
4295 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4296 aControllerName, aControllerPort, aDevice, aNonRotational));
4297
4298 AutoCaller autoCaller(this);
4299 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4300
4301 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4302
4303 HRESULT rc = checkStateDependency(MutableStateDep);
4304 if (FAILED(rc)) return rc;
4305
4306 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4307
4308 if (Global::IsOnlineOrTransient(mData->mMachineState))
4309 return setError(VBOX_E_INVALID_VM_STATE,
4310 tr("Invalid machine state: %s"),
4311 Global::stringifyMachineState(mData->mMachineState));
4312
4313 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4314 aControllerName,
4315 aControllerPort,
4316 aDevice);
4317 if (!pAttach)
4318 return setError(VBOX_E_OBJECT_NOT_FOUND,
4319 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4320 aDevice, aControllerPort, aControllerName);
4321
4322
4323 setModified(IsModified_Storage);
4324 mMediaData.backup();
4325
4326 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4327
4328 if (pAttach->getType() != DeviceType_HardDisk)
4329 return setError(E_INVALIDARG,
4330 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"),
4331 aDevice, aControllerPort, aControllerName);
4332 pAttach->updateNonRotational(!!aNonRotational);
4333
4334 return S_OK;
4335}
4336
4337STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4338 LONG aDevice, BOOL aDiscard)
4339{
4340 CheckComArgStrNotEmptyOrNull(aControllerName);
4341
4342 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4343 aControllerName, aControllerPort, aDevice, aDiscard));
4344
4345 AutoCaller autoCaller(this);
4346 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4347
4348 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4349
4350 HRESULT rc = checkStateDependency(MutableStateDep);
4351 if (FAILED(rc)) return rc;
4352
4353 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4354
4355 if (Global::IsOnlineOrTransient(mData->mMachineState))
4356 return setError(VBOX_E_INVALID_VM_STATE,
4357 tr("Invalid machine state: %s"),
4358 Global::stringifyMachineState(mData->mMachineState));
4359
4360 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4361 aControllerName,
4362 aControllerPort,
4363 aDevice);
4364 if (!pAttach)
4365 return setError(VBOX_E_OBJECT_NOT_FOUND,
4366 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4367 aDevice, aControllerPort, aControllerName);
4368
4369
4370 setModified(IsModified_Storage);
4371 mMediaData.backup();
4372
4373 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4374
4375 if (pAttach->getType() != DeviceType_HardDisk)
4376 return setError(E_INVALIDARG,
4377 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"),
4378 aDevice, aControllerPort, aControllerName);
4379 pAttach->updateDiscard(!!aDiscard);
4380
4381 return S_OK;
4382}
4383
4384STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4385 LONG aDevice)
4386{
4387 int rc = S_OK;
4388 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4389 aControllerName, aControllerPort, aDevice));
4390
4391 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4392
4393 return rc;
4394}
4395
4396STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4397 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4398{
4399 CheckComArgStrNotEmptyOrNull(aControllerName);
4400
4401 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4402 aControllerName, aControllerPort, aDevice));
4403
4404 AutoCaller autoCaller(this);
4405 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4406
4407 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4408
4409 HRESULT rc = checkStateDependency(MutableStateDep);
4410 if (FAILED(rc)) return rc;
4411
4412 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4413
4414 if (Global::IsOnlineOrTransient(mData->mMachineState))
4415 return setError(VBOX_E_INVALID_VM_STATE,
4416 tr("Invalid machine state: %s"),
4417 Global::stringifyMachineState(mData->mMachineState));
4418
4419 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4420 aControllerName,
4421 aControllerPort,
4422 aDevice);
4423 if (!pAttach)
4424 return setError(VBOX_E_OBJECT_NOT_FOUND,
4425 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4426 aDevice, aControllerPort, aControllerName);
4427
4428
4429 setModified(IsModified_Storage);
4430 mMediaData.backup();
4431
4432 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4433 if (aBandwidthGroup && group.isNull())
4434 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4435
4436 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4437
4438 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4439 if (strBandwidthGroupOld.isNotEmpty())
4440 {
4441 /* Get the bandwidth group object and release it - this must not fail. */
4442 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4443 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4444 Assert(SUCCEEDED(rc));
4445
4446 pBandwidthGroupOld->release();
4447 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4448 }
4449
4450 if (!group.isNull())
4451 {
4452 group->reference();
4453 pAttach->updateBandwidthGroup(group->getName());
4454 }
4455
4456 return S_OK;
4457}
4458
4459STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4460 LONG aControllerPort,
4461 LONG aDevice,
4462 DeviceType_T aType)
4463{
4464 HRESULT rc = S_OK;
4465
4466 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4467 aControllerName, aControllerPort, aDevice, aType));
4468
4469 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4470
4471 return rc;
4472}
4473
4474
4475
4476STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4477 LONG aControllerPort,
4478 LONG aDevice,
4479 BOOL aForce)
4480{
4481 int rc = S_OK;
4482 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4483 aControllerName, aControllerPort, aForce));
4484
4485 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4486
4487 return rc;
4488}
4489
4490STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4491 LONG aControllerPort,
4492 LONG aDevice,
4493 IMedium *aMedium,
4494 BOOL aForce)
4495{
4496 int rc = S_OK;
4497 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4498 aControllerName, aControllerPort, aDevice, aForce));
4499
4500 CheckComArgStrNotEmptyOrNull(aControllerName);
4501
4502 AutoCaller autoCaller(this);
4503 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4504
4505 // request the host lock first, since might be calling Host methods for getting host drives;
4506 // next, protect the media tree all the while we're in here, as well as our member variables
4507 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4508 this->lockHandle(),
4509 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4510
4511 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4512 aControllerName,
4513 aControllerPort,
4514 aDevice);
4515 if (pAttach.isNull())
4516 return setError(VBOX_E_OBJECT_NOT_FOUND,
4517 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4518 aDevice, aControllerPort, aControllerName);
4519
4520 /* Remember previously mounted medium. The medium before taking the
4521 * backup is not necessarily the same thing. */
4522 ComObjPtr<Medium> oldmedium;
4523 oldmedium = pAttach->getMedium();
4524
4525 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4526 if (aMedium && pMedium.isNull())
4527 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4528
4529 AutoCaller mediumCaller(pMedium);
4530 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4531
4532 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4533 if (pMedium)
4534 {
4535 DeviceType_T mediumType = pAttach->getType();
4536 switch (mediumType)
4537 {
4538 case DeviceType_DVD:
4539 case DeviceType_Floppy:
4540 break;
4541
4542 default:
4543 return setError(VBOX_E_INVALID_OBJECT_STATE,
4544 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
4545 aControllerPort,
4546 aDevice,
4547 aControllerName);
4548 }
4549 }
4550
4551 setModified(IsModified_Storage);
4552 mMediaData.backup();
4553
4554 {
4555 // The backup operation makes the pAttach reference point to the
4556 // old settings. Re-get the correct reference.
4557 pAttach = findAttachment(mMediaData->mAttachments,
4558 aControllerName,
4559 aControllerPort,
4560 aDevice);
4561 if (!oldmedium.isNull())
4562 oldmedium->removeBackReference(mData->mUuid);
4563 if (!pMedium.isNull())
4564 {
4565 pMedium->addBackReference(mData->mUuid);
4566
4567 mediumLock.release();
4568 multiLock.release();
4569 addMediumToRegistry(pMedium);
4570 multiLock.acquire();
4571 mediumLock.acquire();
4572 }
4573
4574 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4575 pAttach->updateMedium(pMedium);
4576 }
4577
4578 setModified(IsModified_Storage);
4579
4580 mediumLock.release();
4581 multiLock.release();
4582 rc = onMediumChange(pAttach, aForce);
4583 multiLock.acquire();
4584 mediumLock.acquire();
4585
4586 /* On error roll back this change only. */
4587 if (FAILED(rc))
4588 {
4589 if (!pMedium.isNull())
4590 pMedium->removeBackReference(mData->mUuid);
4591 pAttach = findAttachment(mMediaData->mAttachments,
4592 aControllerName,
4593 aControllerPort,
4594 aDevice);
4595 /* If the attachment is gone in the meantime, bail out. */
4596 if (pAttach.isNull())
4597 return rc;
4598 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4599 if (!oldmedium.isNull())
4600 oldmedium->addBackReference(mData->mUuid);
4601 pAttach->updateMedium(oldmedium);
4602 }
4603
4604 mediumLock.release();
4605 multiLock.release();
4606
4607 mParent->saveModifiedRegistries();
4608
4609 return rc;
4610}
4611
4612STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
4613 LONG aControllerPort,
4614 LONG aDevice,
4615 IMedium **aMedium)
4616{
4617 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4618 aControllerName, aControllerPort, aDevice));
4619
4620 CheckComArgStrNotEmptyOrNull(aControllerName);
4621 CheckComArgOutPointerValid(aMedium);
4622
4623 AutoCaller autoCaller(this);
4624 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4625
4626 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4627
4628 *aMedium = NULL;
4629
4630 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4631 aControllerName,
4632 aControllerPort,
4633 aDevice);
4634 if (pAttach.isNull())
4635 return setError(VBOX_E_OBJECT_NOT_FOUND,
4636 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4637 aDevice, aControllerPort, aControllerName);
4638
4639 pAttach->getMedium().queryInterfaceTo(aMedium);
4640
4641 return S_OK;
4642}
4643
4644STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
4645{
4646 CheckComArgOutPointerValid(port);
4647 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
4648
4649 AutoCaller autoCaller(this);
4650 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4651
4652 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4653
4654 mSerialPorts[slot].queryInterfaceTo(port);
4655
4656 return S_OK;
4657}
4658
4659STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
4660{
4661 CheckComArgOutPointerValid(port);
4662 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
4663
4664 AutoCaller autoCaller(this);
4665 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4666
4667 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4668
4669 mParallelPorts[slot].queryInterfaceTo(port);
4670
4671 return S_OK;
4672}
4673
4674STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
4675{
4676 CheckComArgOutPointerValid(adapter);
4677 CheckComArgExpr(slot, slot < mNetworkAdapters.size());
4678
4679 AutoCaller autoCaller(this);
4680 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4681
4682 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4683
4684 mNetworkAdapters[slot].queryInterfaceTo(adapter);
4685
4686 return S_OK;
4687}
4688
4689STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
4690{
4691 CheckComArgOutSafeArrayPointerValid(aKeys);
4692
4693 AutoCaller autoCaller(this);
4694 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4695
4696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4697
4698 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4699 int i = 0;
4700 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4701 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4702 ++it, ++i)
4703 {
4704 const Utf8Str &strKey = it->first;
4705 strKey.cloneTo(&saKeys[i]);
4706 }
4707 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
4708
4709 return S_OK;
4710 }
4711
4712 /**
4713 * @note Locks this object for reading.
4714 */
4715STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
4716 BSTR *aValue)
4717{
4718 CheckComArgStrNotEmptyOrNull(aKey);
4719 CheckComArgOutPointerValid(aValue);
4720
4721 AutoCaller autoCaller(this);
4722 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4723
4724 /* start with nothing found */
4725 Bstr bstrResult("");
4726
4727 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4728
4729 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
4730 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4731 // found:
4732 bstrResult = it->second; // source is a Utf8Str
4733
4734 /* return the result to caller (may be empty) */
4735 bstrResult.cloneTo(aValue);
4736
4737 return S_OK;
4738}
4739
4740 /**
4741 * @note Locks mParent for writing + this object for writing.
4742 */
4743STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
4744{
4745 CheckComArgStrNotEmptyOrNull(aKey);
4746
4747 AutoCaller autoCaller(this);
4748 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4749
4750 Utf8Str strKey(aKey);
4751 Utf8Str strValue(aValue);
4752 Utf8Str strOldValue; // empty
4753
4754 // locking note: we only hold the read lock briefly to look up the old value,
4755 // then release it and call the onExtraCanChange callbacks. There is a small
4756 // chance of a race insofar as the callback might be called twice if two callers
4757 // change the same key at the same time, but that's a much better solution
4758 // than the deadlock we had here before. The actual changing of the extradata
4759 // is then performed under the write lock and race-free.
4760
4761 // look up the old value first; if nothing has changed then we need not do anything
4762 {
4763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4764 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
4765 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4766 strOldValue = it->second;
4767 }
4768
4769 bool fChanged;
4770 if ((fChanged = (strOldValue != strValue)))
4771 {
4772 // ask for permission from all listeners outside the locks;
4773 // onExtraDataCanChange() only briefly requests the VirtualBox
4774 // lock to copy the list of callbacks to invoke
4775 Bstr error;
4776 Bstr bstrValue(aValue);
4777
4778 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
4779 {
4780 const char *sep = error.isEmpty() ? "" : ": ";
4781 CBSTR err = error.raw();
4782 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4783 sep, err));
4784 return setError(E_ACCESSDENIED,
4785 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
4786 aKey,
4787 bstrValue.raw(),
4788 sep,
4789 err);
4790 }
4791
4792 // data is changing and change not vetoed: then write it out under the lock
4793 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4794
4795 if (isSnapshotMachine())
4796 {
4797 HRESULT rc = checkStateDependency(MutableStateDep);
4798 if (FAILED(rc)) return rc;
4799 }
4800
4801 if (strValue.isEmpty())
4802 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
4803 else
4804 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
4805 // creates a new key if needed
4806
4807 bool fNeedsGlobalSaveSettings = false;
4808 saveSettings(&fNeedsGlobalSaveSettings);
4809
4810 if (fNeedsGlobalSaveSettings)
4811 {
4812 // save the global settings; for that we should hold only the VirtualBox lock
4813 alock.release();
4814 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4815 mParent->saveSettings();
4816 }
4817 }
4818
4819 // fire notification outside the lock
4820 if (fChanged)
4821 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
4822
4823 return S_OK;
4824}
4825
4826STDMETHODIMP Machine::SaveSettings()
4827{
4828 AutoCaller autoCaller(this);
4829 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4830
4831 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4832
4833 /* when there was auto-conversion, we want to save the file even if
4834 * the VM is saved */
4835 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
4836 if (FAILED(rc)) return rc;
4837
4838 /* the settings file path may never be null */
4839 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4840
4841 /* save all VM data excluding snapshots */
4842 bool fNeedsGlobalSaveSettings = false;
4843 rc = saveSettings(&fNeedsGlobalSaveSettings);
4844 mlock.release();
4845
4846 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4847 {
4848 // save the global settings; for that we should hold only the VirtualBox lock
4849 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4850 rc = mParent->saveSettings();
4851 }
4852
4853 return rc;
4854}
4855
4856STDMETHODIMP Machine::DiscardSettings()
4857{
4858 AutoCaller autoCaller(this);
4859 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4860
4861 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4862
4863 HRESULT rc = checkStateDependency(MutableStateDep);
4864 if (FAILED(rc)) return rc;
4865
4866 /*
4867 * during this rollback, the session will be notified if data has
4868 * been actually changed
4869 */
4870 rollback(true /* aNotify */);
4871
4872 return S_OK;
4873}
4874
4875/** @note Locks objects! */
4876STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
4877 ComSafeArrayOut(IMedium*, aMedia))
4878{
4879 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
4880 AutoLimitedCaller autoCaller(this);
4881 AssertComRCReturnRC(autoCaller.rc());
4882
4883 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4884
4885 Guid id(getId());
4886
4887 if (mData->mSession.mState != SessionState_Unlocked)
4888 return setError(VBOX_E_INVALID_OBJECT_STATE,
4889 tr("Cannot unregister the machine '%s' while it is locked"),
4890 mUserData->s.strName.c_str());
4891
4892 // wait for state dependents to drop to zero
4893 ensureNoStateDependencies();
4894
4895 if (!mData->mAccessible)
4896 {
4897 // inaccessible maschines can only be unregistered; uninitialize ourselves
4898 // here because currently there may be no unregistered that are inaccessible
4899 // (this state combination is not supported). Note releasing the caller and
4900 // leaving the lock before calling uninit()
4901 alock.release();
4902 autoCaller.release();
4903
4904 uninit();
4905
4906 mParent->unregisterMachine(this, id);
4907 // calls VirtualBox::saveSettings()
4908
4909 return S_OK;
4910 }
4911
4912 HRESULT rc = S_OK;
4913
4914 // discard saved state
4915 if (mData->mMachineState == MachineState_Saved)
4916 {
4917 // add the saved state file to the list of files the caller should delete
4918 Assert(!mSSData->strStateFilePath.isEmpty());
4919 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4920
4921 mSSData->strStateFilePath.setNull();
4922
4923 // unconditionally set the machine state to powered off, we now
4924 // know no session has locked the machine
4925 mData->mMachineState = MachineState_PoweredOff;
4926 }
4927
4928 size_t cSnapshots = 0;
4929 if (mData->mFirstSnapshot)
4930 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
4931 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
4932 // fail now before we start detaching media
4933 return setError(VBOX_E_INVALID_OBJECT_STATE,
4934 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
4935 mUserData->s.strName.c_str(), cSnapshots);
4936
4937 // This list collects the medium objects from all medium attachments
4938 // which we will detach from the machine and its snapshots, in a specific
4939 // order which allows for closing all media without getting "media in use"
4940 // errors, simply by going through the list from the front to the back:
4941 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4942 // and must be closed before the parent media from the snapshots, or closing the parents
4943 // will fail because they still have children);
4944 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4945 // the root ("first") snapshot of the machine.
4946 MediaList llMedia;
4947
4948 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
4949 && mMediaData->mAttachments.size()
4950 )
4951 {
4952 // we have media attachments: detach them all and add the Medium objects to our list
4953 if (cleanupMode != CleanupMode_UnregisterOnly)
4954 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
4955 else
4956 return setError(VBOX_E_INVALID_OBJECT_STATE,
4957 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
4958 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
4959 }
4960
4961 if (cSnapshots)
4962 {
4963 // autoCleanup must be true here, or we would have failed above
4964
4965 // add the media from the medium attachments of the snapshots to llMedia
4966 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4967 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4968 // into the children first
4969
4970 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4971 MachineState_T oldState = mData->mMachineState;
4972 mData->mMachineState = MachineState_DeletingSnapshot;
4973
4974 // make a copy of the first snapshot so the refcount does not drop to 0
4975 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
4976 // because of the AutoCaller voodoo)
4977 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4978
4979 // GO!
4980 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
4981
4982 mData->mMachineState = oldState;
4983 }
4984
4985 if (FAILED(rc))
4986 {
4987 rollbackMedia();
4988 return rc;
4989 }
4990
4991 // commit all the media changes made above
4992 commitMedia();
4993
4994 mData->mRegistered = false;
4995
4996 // machine lock no longer needed
4997 alock.release();
4998
4999 // return media to caller
5000 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5001 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5002
5003 mParent->unregisterMachine(this, id);
5004 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5005
5006 return S_OK;
5007}
5008
5009struct Machine::DeleteTask
5010{
5011 ComObjPtr<Machine> pMachine;
5012 RTCList<ComPtr<IMedium> > llMediums;
5013 StringsList llFilesToDelete;
5014 ComObjPtr<Progress> pProgress;
5015};
5016
5017STDMETHODIMP Machine::Delete(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5018{
5019 LogFlowFuncEnter();
5020
5021 AutoCaller autoCaller(this);
5022 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5023
5024 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5025
5026 HRESULT rc = checkStateDependency(MutableStateDep);
5027 if (FAILED(rc)) return rc;
5028
5029 if (mData->mRegistered)
5030 return setError(VBOX_E_INVALID_VM_STATE,
5031 tr("Cannot delete settings of a registered machine"));
5032
5033 DeleteTask *pTask = new DeleteTask;
5034 pTask->pMachine = this;
5035 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5036
5037 // collect files to delete
5038 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5039
5040 for (size_t i = 0; i < sfaMedia.size(); ++i)
5041 {
5042 IMedium *pIMedium(sfaMedia[i]);
5043 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5044 if (pMedium.isNull())
5045 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5046 SafeArray<BSTR> ids;
5047 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5048 if (FAILED(rc)) return rc;
5049 /* At this point the medium should not have any back references
5050 * anymore. If it has it is attached to another VM and *must* not
5051 * deleted. */
5052 if (ids.size() < 1)
5053 pTask->llMediums.append(pMedium);
5054 }
5055 if (mData->pMachineConfigFile->fileExists())
5056 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5057
5058 pTask->pProgress.createObject();
5059 pTask->pProgress->init(getVirtualBox(),
5060 static_cast<IMachine*>(this) /* aInitiator */,
5061 Bstr(tr("Deleting files")).raw(),
5062 true /* fCancellable */,
5063 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5064 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5065
5066 int vrc = RTThreadCreate(NULL,
5067 Machine::deleteThread,
5068 (void*)pTask,
5069 0,
5070 RTTHREADTYPE_MAIN_WORKER,
5071 0,
5072 "MachineDelete");
5073
5074 pTask->pProgress.queryInterfaceTo(aProgress);
5075
5076 if (RT_FAILURE(vrc))
5077 {
5078 delete pTask;
5079 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5080 }
5081
5082 LogFlowFuncLeave();
5083
5084 return S_OK;
5085}
5086
5087/**
5088 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5089 * calls Machine::deleteTaskWorker() on the actual machine object.
5090 * @param Thread
5091 * @param pvUser
5092 * @return
5093 */
5094/*static*/
5095DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5096{
5097 LogFlowFuncEnter();
5098
5099 DeleteTask *pTask = (DeleteTask*)pvUser;
5100 Assert(pTask);
5101 Assert(pTask->pMachine);
5102 Assert(pTask->pProgress);
5103
5104 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5105 pTask->pProgress->notifyComplete(rc);
5106
5107 delete pTask;
5108
5109 LogFlowFuncLeave();
5110
5111 NOREF(Thread);
5112
5113 return VINF_SUCCESS;
5114}
5115
5116/**
5117 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5118 * @param task
5119 * @return
5120 */
5121HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5122{
5123 AutoCaller autoCaller(this);
5124 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5125
5126 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5127
5128 HRESULT rc = S_OK;
5129
5130 try
5131 {
5132 ULONG uLogHistoryCount = 3;
5133 ComPtr<ISystemProperties> systemProperties;
5134 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5135 if (FAILED(rc)) throw rc;
5136
5137 if (!systemProperties.isNull())
5138 {
5139 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5140 if (FAILED(rc)) throw rc;
5141 }
5142
5143 MachineState_T oldState = mData->mMachineState;
5144 setMachineState(MachineState_SettingUp);
5145 alock.release();
5146 for (size_t i = 0; i < task.llMediums.size(); ++i)
5147 {
5148 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5149 {
5150 AutoCaller mac(pMedium);
5151 if (FAILED(mac.rc())) throw mac.rc();
5152 Utf8Str strLocation = pMedium->getLocationFull();
5153 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5154 if (FAILED(rc)) throw rc;
5155 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5156 }
5157 ComPtr<IProgress> pProgress2;
5158 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5159 if (FAILED(rc)) throw rc;
5160 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5161 if (FAILED(rc)) throw rc;
5162 /* Check the result of the asynchrony process. */
5163 LONG iRc;
5164 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5165 if (FAILED(rc)) throw rc;
5166 /* If the thread of the progress object has an error, then
5167 * retrieve the error info from there, or it'll be lost. */
5168 if (FAILED(iRc))
5169 throw setError(ProgressErrorInfo(pProgress2));
5170 }
5171 setMachineState(oldState);
5172 alock.acquire();
5173
5174 // delete the files pushed on the task list by Machine::Delete()
5175 // (this includes saved states of the machine and snapshots and
5176 // medium storage files from the IMedium list passed in, and the
5177 // machine XML file)
5178 StringsList::const_iterator it = task.llFilesToDelete.begin();
5179 while (it != task.llFilesToDelete.end())
5180 {
5181 const Utf8Str &strFile = *it;
5182 LogFunc(("Deleting file %s\n", strFile.c_str()));
5183 int vrc = RTFileDelete(strFile.c_str());
5184 if (RT_FAILURE(vrc))
5185 throw setError(VBOX_E_IPRT_ERROR,
5186 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5187
5188 ++it;
5189 if (it == task.llFilesToDelete.end())
5190 {
5191 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5192 if (FAILED(rc)) throw rc;
5193 break;
5194 }
5195
5196 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5197 if (FAILED(rc)) throw rc;
5198 }
5199
5200 /* delete the settings only when the file actually exists */
5201 if (mData->pMachineConfigFile->fileExists())
5202 {
5203 /* Delete any backup or uncommitted XML files. Ignore failures.
5204 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5205 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5206 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5207 RTFileDelete(otherXml.c_str());
5208 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5209 RTFileDelete(otherXml.c_str());
5210
5211 /* delete the Logs folder, nothing important should be left
5212 * there (we don't check for errors because the user might have
5213 * some private files there that we don't want to delete) */
5214 Utf8Str logFolder;
5215 getLogFolder(logFolder);
5216 Assert(logFolder.length());
5217 if (RTDirExists(logFolder.c_str()))
5218 {
5219 /* Delete all VBox.log[.N] files from the Logs folder
5220 * (this must be in sync with the rotation logic in
5221 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5222 * files that may have been created by the GUI. */
5223 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5224 logFolder.c_str(), RTPATH_DELIMITER);
5225 RTFileDelete(log.c_str());
5226 log = Utf8StrFmt("%s%cVBox.png",
5227 logFolder.c_str(), RTPATH_DELIMITER);
5228 RTFileDelete(log.c_str());
5229 for (int i = uLogHistoryCount; i > 0; i--)
5230 {
5231 log = Utf8StrFmt("%s%cVBox.log.%d",
5232 logFolder.c_str(), RTPATH_DELIMITER, i);
5233 RTFileDelete(log.c_str());
5234 log = Utf8StrFmt("%s%cVBox.png.%d",
5235 logFolder.c_str(), RTPATH_DELIMITER, i);
5236 RTFileDelete(log.c_str());
5237 }
5238
5239 RTDirRemove(logFolder.c_str());
5240 }
5241
5242 /* delete the Snapshots folder, nothing important should be left
5243 * there (we don't check for errors because the user might have
5244 * some private files there that we don't want to delete) */
5245 Utf8Str strFullSnapshotFolder;
5246 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5247 Assert(!strFullSnapshotFolder.isEmpty());
5248 if (RTDirExists(strFullSnapshotFolder.c_str()))
5249 RTDirRemove(strFullSnapshotFolder.c_str());
5250
5251 // delete the directory that contains the settings file, but only
5252 // if it matches the VM name
5253 Utf8Str settingsDir;
5254 if (isInOwnDir(&settingsDir))
5255 RTDirRemove(settingsDir.c_str());
5256 }
5257
5258 alock.release();
5259
5260 mParent->saveModifiedRegistries();
5261 }
5262 catch (HRESULT aRC) { rc = aRC; }
5263
5264 return rc;
5265}
5266
5267STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5268{
5269 CheckComArgOutPointerValid(aSnapshot);
5270
5271 AutoCaller autoCaller(this);
5272 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5273
5274 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5275
5276 ComObjPtr<Snapshot> pSnapshot;
5277 HRESULT rc;
5278
5279 if (!aNameOrId || !*aNameOrId)
5280 // null case (caller wants root snapshot): findSnapshotById() handles this
5281 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5282 else
5283 {
5284 Guid uuid(aNameOrId);
5285 if (!uuid.isEmpty())
5286 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5287 else
5288 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5289 }
5290 pSnapshot.queryInterfaceTo(aSnapshot);
5291
5292 return rc;
5293}
5294
5295STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5296{
5297 CheckComArgStrNotEmptyOrNull(aName);
5298 CheckComArgStrNotEmptyOrNull(aHostPath);
5299
5300 AutoCaller autoCaller(this);
5301 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5302
5303 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5304
5305 HRESULT rc = checkStateDependency(MutableStateDep);
5306 if (FAILED(rc)) return rc;
5307
5308 Utf8Str strName(aName);
5309
5310 ComObjPtr<SharedFolder> sharedFolder;
5311 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5312 if (SUCCEEDED(rc))
5313 return setError(VBOX_E_OBJECT_IN_USE,
5314 tr("Shared folder named '%s' already exists"),
5315 strName.c_str());
5316
5317 sharedFolder.createObject();
5318 rc = sharedFolder->init(getMachine(),
5319 strName,
5320 aHostPath,
5321 !!aWritable,
5322 !!aAutoMount,
5323 true /* fFailOnError */);
5324 if (FAILED(rc)) return rc;
5325
5326 setModified(IsModified_SharedFolders);
5327 mHWData.backup();
5328 mHWData->mSharedFolders.push_back(sharedFolder);
5329
5330 /* inform the direct session if any */
5331 alock.release();
5332 onSharedFolderChange();
5333
5334 return S_OK;
5335}
5336
5337STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5338{
5339 CheckComArgStrNotEmptyOrNull(aName);
5340
5341 AutoCaller autoCaller(this);
5342 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5343
5344 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5345
5346 HRESULT rc = checkStateDependency(MutableStateDep);
5347 if (FAILED(rc)) return rc;
5348
5349 ComObjPtr<SharedFolder> sharedFolder;
5350 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5351 if (FAILED(rc)) return rc;
5352
5353 setModified(IsModified_SharedFolders);
5354 mHWData.backup();
5355 mHWData->mSharedFolders.remove(sharedFolder);
5356
5357 /* inform the direct session if any */
5358 alock.release();
5359 onSharedFolderChange();
5360
5361 return S_OK;
5362}
5363
5364STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5365{
5366 CheckComArgOutPointerValid(aCanShow);
5367
5368 /* start with No */
5369 *aCanShow = FALSE;
5370
5371 AutoCaller autoCaller(this);
5372 AssertComRCReturnRC(autoCaller.rc());
5373
5374 ComPtr<IInternalSessionControl> directControl;
5375 {
5376 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5377
5378 if (mData->mSession.mState != SessionState_Locked)
5379 return setError(VBOX_E_INVALID_VM_STATE,
5380 tr("Machine is not locked for session (session state: %s)"),
5381 Global::stringifySessionState(mData->mSession.mState));
5382
5383 directControl = mData->mSession.mDirectControl;
5384 }
5385
5386 /* ignore calls made after #OnSessionEnd() is called */
5387 if (!directControl)
5388 return S_OK;
5389
5390 LONG64 dummy;
5391 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5392}
5393
5394STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5395{
5396 CheckComArgOutPointerValid(aWinId);
5397
5398 AutoCaller autoCaller(this);
5399 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5400
5401 ComPtr<IInternalSessionControl> directControl;
5402 {
5403 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5404
5405 if (mData->mSession.mState != SessionState_Locked)
5406 return setError(E_FAIL,
5407 tr("Machine is not locked for session (session state: %s)"),
5408 Global::stringifySessionState(mData->mSession.mState));
5409
5410 directControl = mData->mSession.mDirectControl;
5411 }
5412
5413 /* ignore calls made after #OnSessionEnd() is called */
5414 if (!directControl)
5415 return S_OK;
5416
5417 BOOL dummy;
5418 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5419}
5420
5421#ifdef VBOX_WITH_GUEST_PROPS
5422/**
5423 * Look up a guest property in VBoxSVC's internal structures.
5424 */
5425HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5426 BSTR *aValue,
5427 LONG64 *aTimestamp,
5428 BSTR *aFlags) const
5429{
5430 using namespace guestProp;
5431
5432 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5433 Utf8Str strName(aName);
5434 HWData::GuestPropertyList::const_iterator it;
5435
5436 for (it = mHWData->mGuestProperties.begin();
5437 it != mHWData->mGuestProperties.end(); ++it)
5438 {
5439 if (it->strName == strName)
5440 {
5441 char szFlags[MAX_FLAGS_LEN + 1];
5442 it->strValue.cloneTo(aValue);
5443 *aTimestamp = it->mTimestamp;
5444 writeFlags(it->mFlags, szFlags);
5445 Bstr(szFlags).cloneTo(aFlags);
5446 break;
5447 }
5448 }
5449 return S_OK;
5450}
5451
5452/**
5453 * Query the VM that a guest property belongs to for the property.
5454 * @returns E_ACCESSDENIED if the VM process is not available or not
5455 * currently handling queries and the lookup should then be done in
5456 * VBoxSVC.
5457 */
5458HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5459 BSTR *aValue,
5460 LONG64 *aTimestamp,
5461 BSTR *aFlags) const
5462{
5463 HRESULT rc;
5464 ComPtr<IInternalSessionControl> directControl;
5465 directControl = mData->mSession.mDirectControl;
5466
5467 /* fail if we were called after #OnSessionEnd() is called. This is a
5468 * silly race condition. */
5469
5470 if (!directControl)
5471 rc = E_ACCESSDENIED;
5472 else
5473 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5474 false /* isSetter */,
5475 aValue, aTimestamp, aFlags);
5476 return rc;
5477}
5478#endif // VBOX_WITH_GUEST_PROPS
5479
5480STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5481 BSTR *aValue,
5482 LONG64 *aTimestamp,
5483 BSTR *aFlags)
5484{
5485#ifndef VBOX_WITH_GUEST_PROPS
5486 ReturnComNotImplemented();
5487#else // VBOX_WITH_GUEST_PROPS
5488 CheckComArgStrNotEmptyOrNull(aName);
5489 CheckComArgOutPointerValid(aValue);
5490 CheckComArgOutPointerValid(aTimestamp);
5491 CheckComArgOutPointerValid(aFlags);
5492
5493 AutoCaller autoCaller(this);
5494 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5495
5496 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5497 if (rc == E_ACCESSDENIED)
5498 /* The VM is not running or the service is not (yet) accessible */
5499 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5500 return rc;
5501#endif // VBOX_WITH_GUEST_PROPS
5502}
5503
5504STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5505{
5506 LONG64 dummyTimestamp;
5507 Bstr dummyFlags;
5508 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5509}
5510
5511STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5512{
5513 Bstr dummyValue;
5514 Bstr dummyFlags;
5515 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5516}
5517
5518#ifdef VBOX_WITH_GUEST_PROPS
5519/**
5520 * Set a guest property in VBoxSVC's internal structures.
5521 */
5522HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5523 IN_BSTR aFlags)
5524{
5525 using namespace guestProp;
5526
5527 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5528 HRESULT rc = S_OK;
5529 HWData::GuestProperty property;
5530 property.mFlags = NILFLAG;
5531 bool found = false;
5532
5533 rc = checkStateDependency(MutableStateDep);
5534 if (FAILED(rc)) return rc;
5535
5536 try
5537 {
5538 Utf8Str utf8Name(aName);
5539 Utf8Str utf8Flags(aFlags);
5540 uint32_t fFlags = NILFLAG;
5541 if ( (aFlags != NULL)
5542 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags))
5543 )
5544 return setError(E_INVALIDARG,
5545 tr("Invalid flag values: '%ls'"),
5546 aFlags);
5547
5548 /** @todo r=bird: see efficiency rant in PushGuestProperty. (Yeah, I
5549 * know, this is simple and do an OK job atm.) */
5550 HWData::GuestPropertyList::iterator it;
5551 for (it = mHWData->mGuestProperties.begin();
5552 it != mHWData->mGuestProperties.end(); ++it)
5553 if (it->strName == utf8Name)
5554 {
5555 property = *it;
5556 if (it->mFlags & (RDONLYHOST))
5557 rc = setError(E_ACCESSDENIED,
5558 tr("The property '%ls' cannot be changed by the host"),
5559 aName);
5560 else
5561 {
5562 setModified(IsModified_MachineData);
5563 mHWData.backup(); // @todo r=dj backup in a loop?!?
5564
5565 /* The backup() operation invalidates our iterator, so
5566 * get a new one. */
5567 for (it = mHWData->mGuestProperties.begin();
5568 it->strName != utf8Name;
5569 ++it)
5570 ;
5571 mHWData->mGuestProperties.erase(it);
5572 }
5573 found = true;
5574 break;
5575 }
5576 if (found && SUCCEEDED(rc))
5577 {
5578 if (aValue)
5579 {
5580 RTTIMESPEC time;
5581 property.strValue = aValue;
5582 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5583 if (aFlags != NULL)
5584 property.mFlags = fFlags;
5585 mHWData->mGuestProperties.push_back(property);
5586 }
5587 }
5588 else if (SUCCEEDED(rc) && aValue)
5589 {
5590 RTTIMESPEC time;
5591 setModified(IsModified_MachineData);
5592 mHWData.backup();
5593 property.strName = aName;
5594 property.strValue = aValue;
5595 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5596 property.mFlags = fFlags;
5597 mHWData->mGuestProperties.push_back(property);
5598 }
5599 if ( SUCCEEDED(rc)
5600 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5601 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5602 RTSTR_MAX,
5603 utf8Name.c_str(),
5604 RTSTR_MAX,
5605 NULL)
5606 )
5607 )
5608 {
5609 /** @todo r=bird: Why aren't we leaving the lock here? The
5610 * same code in PushGuestProperty does... */
5611 mParent->onGuestPropertyChange(mData->mUuid, aName,
5612 aValue ? aValue : Bstr("").raw(),
5613 aFlags ? aFlags : Bstr("").raw());
5614 }
5615 }
5616 catch (std::bad_alloc &)
5617 {
5618 rc = E_OUTOFMEMORY;
5619 }
5620
5621 return rc;
5622}
5623
5624/**
5625 * Set a property on the VM that that property belongs to.
5626 * @returns E_ACCESSDENIED if the VM process is not available or not
5627 * currently handling queries and the setting should then be done in
5628 * VBoxSVC.
5629 */
5630HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
5631 IN_BSTR aFlags)
5632{
5633 HRESULT rc;
5634
5635 try
5636 {
5637 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5638
5639 BSTR dummy = NULL; /* will not be changed (setter) */
5640 LONG64 dummy64;
5641 if (!directControl)
5642 rc = E_ACCESSDENIED;
5643 else
5644 /** @todo Fix when adding DeleteGuestProperty(),
5645 see defect. */
5646 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
5647 true /* isSetter */,
5648 &dummy, &dummy64, &dummy);
5649 }
5650 catch (std::bad_alloc &)
5651 {
5652 rc = E_OUTOFMEMORY;
5653 }
5654
5655 return rc;
5656}
5657#endif // VBOX_WITH_GUEST_PROPS
5658
5659STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
5660 IN_BSTR aFlags)
5661{
5662#ifndef VBOX_WITH_GUEST_PROPS
5663 ReturnComNotImplemented();
5664#else // VBOX_WITH_GUEST_PROPS
5665 CheckComArgStrNotEmptyOrNull(aName);
5666 CheckComArgMaybeNull(aFlags);
5667 CheckComArgMaybeNull(aValue);
5668
5669 AutoCaller autoCaller(this);
5670 if (FAILED(autoCaller.rc()))
5671 return autoCaller.rc();
5672
5673 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
5674 if (rc == E_ACCESSDENIED)
5675 /* The VM is not running or the service is not (yet) accessible */
5676 rc = setGuestPropertyToService(aName, aValue, aFlags);
5677 return rc;
5678#endif // VBOX_WITH_GUEST_PROPS
5679}
5680
5681STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
5682{
5683 return SetGuestProperty(aName, aValue, NULL);
5684}
5685
5686STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
5687{
5688 return SetGuestProperty(aName, NULL, NULL);
5689}
5690
5691#ifdef VBOX_WITH_GUEST_PROPS
5692/**
5693 * Enumerate the guest properties in VBoxSVC's internal structures.
5694 */
5695HRESULT Machine::enumerateGuestPropertiesInService
5696 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5697 ComSafeArrayOut(BSTR, aValues),
5698 ComSafeArrayOut(LONG64, aTimestamps),
5699 ComSafeArrayOut(BSTR, aFlags))
5700{
5701 using namespace guestProp;
5702
5703 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5704 Utf8Str strPatterns(aPatterns);
5705
5706 /*
5707 * Look for matching patterns and build up a list.
5708 */
5709 HWData::GuestPropertyList propList;
5710 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
5711 it != mHWData->mGuestProperties.end();
5712 ++it)
5713 if ( strPatterns.isEmpty()
5714 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5715 RTSTR_MAX,
5716 it->strName.c_str(),
5717 RTSTR_MAX,
5718 NULL)
5719 )
5720 propList.push_back(*it);
5721
5722 /*
5723 * And build up the arrays for returning the property information.
5724 */
5725 size_t cEntries = propList.size();
5726 SafeArray<BSTR> names(cEntries);
5727 SafeArray<BSTR> values(cEntries);
5728 SafeArray<LONG64> timestamps(cEntries);
5729 SafeArray<BSTR> flags(cEntries);
5730 size_t iProp = 0;
5731 for (HWData::GuestPropertyList::iterator it = propList.begin();
5732 it != propList.end();
5733 ++it)
5734 {
5735 char szFlags[MAX_FLAGS_LEN + 1];
5736 it->strName.cloneTo(&names[iProp]);
5737 it->strValue.cloneTo(&values[iProp]);
5738 timestamps[iProp] = it->mTimestamp;
5739 writeFlags(it->mFlags, szFlags);
5740 Bstr(szFlags).cloneTo(&flags[iProp]);
5741 ++iProp;
5742 }
5743 names.detachTo(ComSafeArrayOutArg(aNames));
5744 values.detachTo(ComSafeArrayOutArg(aValues));
5745 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
5746 flags.detachTo(ComSafeArrayOutArg(aFlags));
5747 return S_OK;
5748}
5749
5750/**
5751 * Enumerate the properties managed by a VM.
5752 * @returns E_ACCESSDENIED if the VM process is not available or not
5753 * currently handling queries and the setting should then be done in
5754 * VBoxSVC.
5755 */
5756HRESULT Machine::enumerateGuestPropertiesOnVM
5757 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5758 ComSafeArrayOut(BSTR, aValues),
5759 ComSafeArrayOut(LONG64, aTimestamps),
5760 ComSafeArrayOut(BSTR, aFlags))
5761{
5762 HRESULT rc;
5763 ComPtr<IInternalSessionControl> directControl;
5764 directControl = mData->mSession.mDirectControl;
5765
5766 if (!directControl)
5767 rc = E_ACCESSDENIED;
5768 else
5769 rc = directControl->EnumerateGuestProperties
5770 (aPatterns, ComSafeArrayOutArg(aNames),
5771 ComSafeArrayOutArg(aValues),
5772 ComSafeArrayOutArg(aTimestamps),
5773 ComSafeArrayOutArg(aFlags));
5774 return rc;
5775}
5776#endif // VBOX_WITH_GUEST_PROPS
5777
5778STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
5779 ComSafeArrayOut(BSTR, aNames),
5780 ComSafeArrayOut(BSTR, aValues),
5781 ComSafeArrayOut(LONG64, aTimestamps),
5782 ComSafeArrayOut(BSTR, aFlags))
5783{
5784#ifndef VBOX_WITH_GUEST_PROPS
5785 ReturnComNotImplemented();
5786#else // VBOX_WITH_GUEST_PROPS
5787 CheckComArgMaybeNull(aPatterns);
5788 CheckComArgOutSafeArrayPointerValid(aNames);
5789 CheckComArgOutSafeArrayPointerValid(aValues);
5790 CheckComArgOutSafeArrayPointerValid(aTimestamps);
5791 CheckComArgOutSafeArrayPointerValid(aFlags);
5792
5793 AutoCaller autoCaller(this);
5794 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5795
5796 HRESULT rc = enumerateGuestPropertiesOnVM
5797 (aPatterns, ComSafeArrayOutArg(aNames),
5798 ComSafeArrayOutArg(aValues),
5799 ComSafeArrayOutArg(aTimestamps),
5800 ComSafeArrayOutArg(aFlags));
5801 if (rc == E_ACCESSDENIED)
5802 /* The VM is not running or the service is not (yet) accessible */
5803 rc = enumerateGuestPropertiesInService
5804 (aPatterns, ComSafeArrayOutArg(aNames),
5805 ComSafeArrayOutArg(aValues),
5806 ComSafeArrayOutArg(aTimestamps),
5807 ComSafeArrayOutArg(aFlags));
5808 return rc;
5809#endif // VBOX_WITH_GUEST_PROPS
5810}
5811
5812STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
5813 ComSafeArrayOut(IMediumAttachment*, aAttachments))
5814{
5815 MediaData::AttachmentList atts;
5816
5817 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
5818 if (FAILED(rc)) return rc;
5819
5820 SafeIfaceArray<IMediumAttachment> attachments(atts);
5821 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
5822
5823 return S_OK;
5824}
5825
5826STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
5827 LONG aControllerPort,
5828 LONG aDevice,
5829 IMediumAttachment **aAttachment)
5830{
5831 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5832 aControllerName, aControllerPort, aDevice));
5833
5834 CheckComArgStrNotEmptyOrNull(aControllerName);
5835 CheckComArgOutPointerValid(aAttachment);
5836
5837 AutoCaller autoCaller(this);
5838 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5839
5840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5841
5842 *aAttachment = NULL;
5843
5844 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5845 aControllerName,
5846 aControllerPort,
5847 aDevice);
5848 if (pAttach.isNull())
5849 return setError(VBOX_E_OBJECT_NOT_FOUND,
5850 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5851 aDevice, aControllerPort, aControllerName);
5852
5853 pAttach.queryInterfaceTo(aAttachment);
5854
5855 return S_OK;
5856}
5857
5858STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
5859 StorageBus_T aConnectionType,
5860 IStorageController **controller)
5861{
5862 CheckComArgStrNotEmptyOrNull(aName);
5863
5864 if ( (aConnectionType <= StorageBus_Null)
5865 || (aConnectionType > StorageBus_SAS))
5866 return setError(E_INVALIDARG,
5867 tr("Invalid connection type: %d"),
5868 aConnectionType);
5869
5870 AutoCaller autoCaller(this);
5871 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5872
5873 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5874
5875 HRESULT rc = checkStateDependency(MutableStateDep);
5876 if (FAILED(rc)) return rc;
5877
5878 /* try to find one with the name first. */
5879 ComObjPtr<StorageController> ctrl;
5880
5881 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
5882 if (SUCCEEDED(rc))
5883 return setError(VBOX_E_OBJECT_IN_USE,
5884 tr("Storage controller named '%ls' already exists"),
5885 aName);
5886
5887 ctrl.createObject();
5888
5889 /* get a new instance number for the storage controller */
5890 ULONG ulInstance = 0;
5891 bool fBootable = true;
5892 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5893 it != mStorageControllers->end();
5894 ++it)
5895 {
5896 if ((*it)->getStorageBus() == aConnectionType)
5897 {
5898 ULONG ulCurInst = (*it)->getInstance();
5899
5900 if (ulCurInst >= ulInstance)
5901 ulInstance = ulCurInst + 1;
5902
5903 /* Only one controller of each type can be marked as bootable. */
5904 if ((*it)->getBootable())
5905 fBootable = false;
5906 }
5907 }
5908
5909 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5910 if (FAILED(rc)) return rc;
5911
5912 setModified(IsModified_Storage);
5913 mStorageControllers.backup();
5914 mStorageControllers->push_back(ctrl);
5915
5916 ctrl.queryInterfaceTo(controller);
5917
5918 /* inform the direct session if any */
5919 alock.release();
5920 onStorageControllerChange();
5921
5922 return S_OK;
5923}
5924
5925STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
5926 IStorageController **aStorageController)
5927{
5928 CheckComArgStrNotEmptyOrNull(aName);
5929
5930 AutoCaller autoCaller(this);
5931 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5932
5933 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5934
5935 ComObjPtr<StorageController> ctrl;
5936
5937 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5938 if (SUCCEEDED(rc))
5939 ctrl.queryInterfaceTo(aStorageController);
5940
5941 return rc;
5942}
5943
5944STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
5945 IStorageController **aStorageController)
5946{
5947 AutoCaller autoCaller(this);
5948 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5949
5950 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5951
5952 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5953 it != mStorageControllers->end();
5954 ++it)
5955 {
5956 if ((*it)->getInstance() == aInstance)
5957 {
5958 (*it).queryInterfaceTo(aStorageController);
5959 return S_OK;
5960 }
5961 }
5962
5963 return setError(VBOX_E_OBJECT_NOT_FOUND,
5964 tr("Could not find a storage controller with instance number '%lu'"),
5965 aInstance);
5966}
5967
5968STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
5969{
5970 AutoCaller autoCaller(this);
5971 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5972
5973 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5974
5975 HRESULT rc = checkStateDependency(MutableStateDep);
5976 if (FAILED(rc)) return rc;
5977
5978 ComObjPtr<StorageController> ctrl;
5979
5980 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5981 if (SUCCEEDED(rc))
5982 {
5983 /* Ensure that only one controller of each type is marked as bootable. */
5984 if (fBootable == TRUE)
5985 {
5986 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5987 it != mStorageControllers->end();
5988 ++it)
5989 {
5990 ComObjPtr<StorageController> aCtrl = (*it);
5991
5992 if ( (aCtrl->getName() != Utf8Str(aName))
5993 && aCtrl->getBootable() == TRUE
5994 && aCtrl->getStorageBus() == ctrl->getStorageBus()
5995 && aCtrl->getControllerType() == ctrl->getControllerType())
5996 {
5997 aCtrl->setBootable(FALSE);
5998 break;
5999 }
6000 }
6001 }
6002
6003 if (SUCCEEDED(rc))
6004 {
6005 ctrl->setBootable(fBootable);
6006 setModified(IsModified_Storage);
6007 }
6008 }
6009
6010 if (SUCCEEDED(rc))
6011 {
6012 /* inform the direct session if any */
6013 alock.release();
6014 onStorageControllerChange();
6015 }
6016
6017 return rc;
6018}
6019
6020STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6021{
6022 CheckComArgStrNotEmptyOrNull(aName);
6023
6024 AutoCaller autoCaller(this);
6025 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6026
6027 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6028
6029 HRESULT rc = checkStateDependency(MutableStateDep);
6030 if (FAILED(rc)) return rc;
6031
6032 ComObjPtr<StorageController> ctrl;
6033 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6034 if (FAILED(rc)) return rc;
6035
6036 {
6037 /* find all attached devices to the appropriate storage controller and detach them all */
6038 // make a temporary list because detachDevice invalidates iterators into
6039 // mMediaData->mAttachments
6040 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6041
6042 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6043 it != llAttachments2.end();
6044 ++it)
6045 {
6046 MediumAttachment *pAttachTemp = *it;
6047
6048 AutoCaller localAutoCaller(pAttachTemp);
6049 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6050
6051 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6052
6053 if (pAttachTemp->getControllerName() == aName)
6054 {
6055 rc = detachDevice(pAttachTemp, alock, NULL);
6056 if (FAILED(rc)) return rc;
6057 }
6058 }
6059 }
6060
6061 /* We can remove it now. */
6062 setModified(IsModified_Storage);
6063 mStorageControllers.backup();
6064
6065 ctrl->unshare();
6066
6067 mStorageControllers->remove(ctrl);
6068
6069 /* inform the direct session if any */
6070 alock.release();
6071 onStorageControllerChange();
6072
6073 return S_OK;
6074}
6075
6076STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6077 ULONG *puOriginX,
6078 ULONG *puOriginY,
6079 ULONG *puWidth,
6080 ULONG *puHeight,
6081 BOOL *pfEnabled)
6082{
6083 LogFlowThisFunc(("\n"));
6084
6085 CheckComArgNotNull(puOriginX);
6086 CheckComArgNotNull(puOriginY);
6087 CheckComArgNotNull(puWidth);
6088 CheckComArgNotNull(puHeight);
6089 CheckComArgNotNull(pfEnabled);
6090
6091 uint32_t u32OriginX= 0;
6092 uint32_t u32OriginY= 0;
6093 uint32_t u32Width = 0;
6094 uint32_t u32Height = 0;
6095 uint16_t u16Flags = 0;
6096
6097 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6098 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6099 if (RT_FAILURE(vrc))
6100 {
6101#ifdef RT_OS_WINDOWS
6102 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6103 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6104 * So just assign fEnable to TRUE again.
6105 * The right fix would be to change GUI API wrappers to make sure that parameters
6106 * are changed only if API succeeds.
6107 */
6108 *pfEnabled = TRUE;
6109#endif
6110 return setError(VBOX_E_IPRT_ERROR,
6111 tr("Saved guest size is not available (%Rrc)"),
6112 vrc);
6113 }
6114
6115 *puOriginX = u32OriginX;
6116 *puOriginY = u32OriginY;
6117 *puWidth = u32Width;
6118 *puHeight = u32Height;
6119 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6120
6121 return S_OK;
6122}
6123
6124STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6125{
6126 LogFlowThisFunc(("\n"));
6127
6128 CheckComArgNotNull(aSize);
6129 CheckComArgNotNull(aWidth);
6130 CheckComArgNotNull(aHeight);
6131
6132 if (aScreenId != 0)
6133 return E_NOTIMPL;
6134
6135 AutoCaller autoCaller(this);
6136 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6137
6138 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6139
6140 uint8_t *pu8Data = NULL;
6141 uint32_t cbData = 0;
6142 uint32_t u32Width = 0;
6143 uint32_t u32Height = 0;
6144
6145 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6146
6147 if (RT_FAILURE(vrc))
6148 return setError(VBOX_E_IPRT_ERROR,
6149 tr("Saved screenshot data is not available (%Rrc)"),
6150 vrc);
6151
6152 *aSize = cbData;
6153 *aWidth = u32Width;
6154 *aHeight = u32Height;
6155
6156 freeSavedDisplayScreenshot(pu8Data);
6157
6158 return S_OK;
6159}
6160
6161STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6162{
6163 LogFlowThisFunc(("\n"));
6164
6165 CheckComArgNotNull(aWidth);
6166 CheckComArgNotNull(aHeight);
6167 CheckComArgOutSafeArrayPointerValid(aData);
6168
6169 if (aScreenId != 0)
6170 return E_NOTIMPL;
6171
6172 AutoCaller autoCaller(this);
6173 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6174
6175 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6176
6177 uint8_t *pu8Data = NULL;
6178 uint32_t cbData = 0;
6179 uint32_t u32Width = 0;
6180 uint32_t u32Height = 0;
6181
6182 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6183
6184 if (RT_FAILURE(vrc))
6185 return setError(VBOX_E_IPRT_ERROR,
6186 tr("Saved screenshot data is not available (%Rrc)"),
6187 vrc);
6188
6189 *aWidth = u32Width;
6190 *aHeight = u32Height;
6191
6192 com::SafeArray<BYTE> bitmap(cbData);
6193 /* Convert pixels to format expected by the API caller. */
6194 if (aBGR)
6195 {
6196 /* [0] B, [1] G, [2] R, [3] A. */
6197 for (unsigned i = 0; i < cbData; i += 4)
6198 {
6199 bitmap[i] = pu8Data[i];
6200 bitmap[i + 1] = pu8Data[i + 1];
6201 bitmap[i + 2] = pu8Data[i + 2];
6202 bitmap[i + 3] = 0xff;
6203 }
6204 }
6205 else
6206 {
6207 /* [0] R, [1] G, [2] B, [3] A. */
6208 for (unsigned i = 0; i < cbData; i += 4)
6209 {
6210 bitmap[i] = pu8Data[i + 2];
6211 bitmap[i + 1] = pu8Data[i + 1];
6212 bitmap[i + 2] = pu8Data[i];
6213 bitmap[i + 3] = 0xff;
6214 }
6215 }
6216 bitmap.detachTo(ComSafeArrayOutArg(aData));
6217
6218 freeSavedDisplayScreenshot(pu8Data);
6219
6220 return S_OK;
6221}
6222
6223
6224STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6225{
6226 LogFlowThisFunc(("\n"));
6227
6228 CheckComArgNotNull(aWidth);
6229 CheckComArgNotNull(aHeight);
6230 CheckComArgOutSafeArrayPointerValid(aData);
6231
6232 if (aScreenId != 0)
6233 return E_NOTIMPL;
6234
6235 AutoCaller autoCaller(this);
6236 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6237
6238 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6239
6240 uint8_t *pu8Data = NULL;
6241 uint32_t cbData = 0;
6242 uint32_t u32Width = 0;
6243 uint32_t u32Height = 0;
6244
6245 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6246
6247 if (RT_FAILURE(vrc))
6248 return setError(VBOX_E_IPRT_ERROR,
6249 tr("Saved screenshot data is not available (%Rrc)"),
6250 vrc);
6251
6252 *aWidth = u32Width;
6253 *aHeight = u32Height;
6254
6255 HRESULT rc = S_OK;
6256 uint8_t *pu8PNG = NULL;
6257 uint32_t cbPNG = 0;
6258 uint32_t cxPNG = 0;
6259 uint32_t cyPNG = 0;
6260
6261 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6262
6263 if (RT_SUCCESS(vrc))
6264 {
6265 com::SafeArray<BYTE> screenData(cbPNG);
6266 screenData.initFrom(pu8PNG, cbPNG);
6267 if (pu8PNG)
6268 RTMemFree(pu8PNG);
6269 screenData.detachTo(ComSafeArrayOutArg(aData));
6270 }
6271 else
6272 {
6273 if (pu8PNG)
6274 RTMemFree(pu8PNG);
6275 return setError(VBOX_E_IPRT_ERROR,
6276 tr("Could not convert screenshot to PNG (%Rrc)"),
6277 vrc);
6278 }
6279
6280 freeSavedDisplayScreenshot(pu8Data);
6281
6282 return rc;
6283}
6284
6285STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6286{
6287 LogFlowThisFunc(("\n"));
6288
6289 CheckComArgNotNull(aSize);
6290 CheckComArgNotNull(aWidth);
6291 CheckComArgNotNull(aHeight);
6292
6293 if (aScreenId != 0)
6294 return E_NOTIMPL;
6295
6296 AutoCaller autoCaller(this);
6297 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6298
6299 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6300
6301 uint8_t *pu8Data = NULL;
6302 uint32_t cbData = 0;
6303 uint32_t u32Width = 0;
6304 uint32_t u32Height = 0;
6305
6306 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6307
6308 if (RT_FAILURE(vrc))
6309 return setError(VBOX_E_IPRT_ERROR,
6310 tr("Saved screenshot data is not available (%Rrc)"),
6311 vrc);
6312
6313 *aSize = cbData;
6314 *aWidth = u32Width;
6315 *aHeight = u32Height;
6316
6317 freeSavedDisplayScreenshot(pu8Data);
6318
6319 return S_OK;
6320}
6321
6322STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6323{
6324 LogFlowThisFunc(("\n"));
6325
6326 CheckComArgNotNull(aWidth);
6327 CheckComArgNotNull(aHeight);
6328 CheckComArgOutSafeArrayPointerValid(aData);
6329
6330 if (aScreenId != 0)
6331 return E_NOTIMPL;
6332
6333 AutoCaller autoCaller(this);
6334 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6335
6336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6337
6338 uint8_t *pu8Data = NULL;
6339 uint32_t cbData = 0;
6340 uint32_t u32Width = 0;
6341 uint32_t u32Height = 0;
6342
6343 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6344
6345 if (RT_FAILURE(vrc))
6346 return setError(VBOX_E_IPRT_ERROR,
6347 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6348 vrc);
6349
6350 *aWidth = u32Width;
6351 *aHeight = u32Height;
6352
6353 com::SafeArray<BYTE> png(cbData);
6354 png.initFrom(pu8Data, cbData);
6355 png.detachTo(ComSafeArrayOutArg(aData));
6356
6357 freeSavedDisplayScreenshot(pu8Data);
6358
6359 return S_OK;
6360}
6361
6362STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6363{
6364 HRESULT rc = S_OK;
6365 LogFlowThisFunc(("\n"));
6366
6367 AutoCaller autoCaller(this);
6368 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6369
6370 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6371
6372 if (!mHWData->mCPUHotPlugEnabled)
6373 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6374
6375 if (aCpu >= mHWData->mCPUCount)
6376 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6377
6378 if (mHWData->mCPUAttached[aCpu])
6379 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6380
6381 alock.release();
6382 rc = onCPUChange(aCpu, false);
6383 alock.acquire();
6384 if (FAILED(rc)) return rc;
6385
6386 setModified(IsModified_MachineData);
6387 mHWData.backup();
6388 mHWData->mCPUAttached[aCpu] = true;
6389
6390 /* Save settings if online */
6391 if (Global::IsOnline(mData->mMachineState))
6392 saveSettings(NULL);
6393
6394 return S_OK;
6395}
6396
6397STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6398{
6399 HRESULT rc = S_OK;
6400 LogFlowThisFunc(("\n"));
6401
6402 AutoCaller autoCaller(this);
6403 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6404
6405 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6406
6407 if (!mHWData->mCPUHotPlugEnabled)
6408 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6409
6410 if (aCpu >= SchemaDefs::MaxCPUCount)
6411 return setError(E_INVALIDARG,
6412 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6413 SchemaDefs::MaxCPUCount);
6414
6415 if (!mHWData->mCPUAttached[aCpu])
6416 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6417
6418 /* CPU 0 can't be detached */
6419 if (aCpu == 0)
6420 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6421
6422 alock.release();
6423 rc = onCPUChange(aCpu, true);
6424 alock.acquire();
6425 if (FAILED(rc)) return rc;
6426
6427 setModified(IsModified_MachineData);
6428 mHWData.backup();
6429 mHWData->mCPUAttached[aCpu] = false;
6430
6431 /* Save settings if online */
6432 if (Global::IsOnline(mData->mMachineState))
6433 saveSettings(NULL);
6434
6435 return S_OK;
6436}
6437
6438STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6439{
6440 LogFlowThisFunc(("\n"));
6441
6442 CheckComArgNotNull(aCpuAttached);
6443
6444 *aCpuAttached = false;
6445
6446 AutoCaller autoCaller(this);
6447 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6448
6449 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6450
6451 /* If hotplug is enabled the CPU is always enabled. */
6452 if (!mHWData->mCPUHotPlugEnabled)
6453 {
6454 if (aCpu < mHWData->mCPUCount)
6455 *aCpuAttached = true;
6456 }
6457 else
6458 {
6459 if (aCpu < SchemaDefs::MaxCPUCount)
6460 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6461 }
6462
6463 return S_OK;
6464}
6465
6466STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6467{
6468 CheckComArgOutPointerValid(aName);
6469
6470 AutoCaller autoCaller(this);
6471 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6472
6473 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6474
6475 Utf8Str log = queryLogFilename(aIdx);
6476 if (!RTFileExists(log.c_str()))
6477 log.setNull();
6478 log.cloneTo(aName);
6479
6480 return S_OK;
6481}
6482
6483STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6484{
6485 LogFlowThisFunc(("\n"));
6486 CheckComArgOutSafeArrayPointerValid(aData);
6487 if (aSize < 0)
6488 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6489
6490 AutoCaller autoCaller(this);
6491 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6492
6493 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6494
6495 HRESULT rc = S_OK;
6496 Utf8Str log = queryLogFilename(aIdx);
6497
6498 /* do not unnecessarily hold the lock while doing something which does
6499 * not need the lock and potentially takes a long time. */
6500 alock.release();
6501
6502 /* Limit the chunk size to 32K for now, as that gives better performance
6503 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6504 * One byte expands to approx. 25 bytes of breathtaking XML. */
6505 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6506 com::SafeArray<BYTE> logData(cbData);
6507
6508 RTFILE LogFile;
6509 int vrc = RTFileOpen(&LogFile, log.c_str(),
6510 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6511 if (RT_SUCCESS(vrc))
6512 {
6513 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6514 if (RT_SUCCESS(vrc))
6515 logData.resize(cbData);
6516 else
6517 rc = setError(VBOX_E_IPRT_ERROR,
6518 tr("Could not read log file '%s' (%Rrc)"),
6519 log.c_str(), vrc);
6520 RTFileClose(LogFile);
6521 }
6522 else
6523 rc = setError(VBOX_E_IPRT_ERROR,
6524 tr("Could not open log file '%s' (%Rrc)"),
6525 log.c_str(), vrc);
6526
6527 if (FAILED(rc))
6528 logData.resize(0);
6529 logData.detachTo(ComSafeArrayOutArg(aData));
6530
6531 return rc;
6532}
6533
6534
6535/**
6536 * Currently this method doesn't attach device to the running VM,
6537 * just makes sure it's plugged on next VM start.
6538 */
6539STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6540{
6541 AutoCaller autoCaller(this);
6542 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6543
6544 // lock scope
6545 {
6546 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6547
6548 HRESULT rc = checkStateDependency(MutableStateDep);
6549 if (FAILED(rc)) return rc;
6550
6551 ChipsetType_T aChipset = ChipsetType_PIIX3;
6552 COMGETTER(ChipsetType)(&aChipset);
6553
6554 if (aChipset != ChipsetType_ICH9)
6555 {
6556 return setError(E_INVALIDARG,
6557 tr("Host PCI attachment only supported with ICH9 chipset"));
6558 }
6559
6560 // check if device with this host PCI address already attached
6561 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6562 it != mHWData->mPCIDeviceAssignments.end();
6563 ++it)
6564 {
6565 LONG iHostAddress = -1;
6566 ComPtr<PCIDeviceAttachment> pAttach;
6567 pAttach = *it;
6568 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6569 if (iHostAddress == hostAddress)
6570 return setError(E_INVALIDARG,
6571 tr("Device with host PCI address already attached to this VM"));
6572 }
6573
6574 ComObjPtr<PCIDeviceAttachment> pda;
6575 char name[32];
6576
6577 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6578 Bstr bname(name);
6579 pda.createObject();
6580 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
6581 setModified(IsModified_MachineData);
6582 mHWData.backup();
6583 mHWData->mPCIDeviceAssignments.push_back(pda);
6584 }
6585
6586 return S_OK;
6587}
6588
6589/**
6590 * Currently this method doesn't detach device from the running VM,
6591 * just makes sure it's not plugged on next VM start.
6592 */
6593STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
6594{
6595 AutoCaller autoCaller(this);
6596 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6597
6598 ComObjPtr<PCIDeviceAttachment> pAttach;
6599 bool fRemoved = false;
6600 HRESULT rc;
6601
6602 // lock scope
6603 {
6604 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6605
6606 rc = checkStateDependency(MutableStateDep);
6607 if (FAILED(rc)) return rc;
6608
6609 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6610 it != mHWData->mPCIDeviceAssignments.end();
6611 ++it)
6612 {
6613 LONG iHostAddress = -1;
6614 pAttach = *it;
6615 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6616 if (iHostAddress != -1 && iHostAddress == hostAddress)
6617 {
6618 setModified(IsModified_MachineData);
6619 mHWData.backup();
6620 mHWData->mPCIDeviceAssignments.remove(pAttach);
6621 fRemoved = true;
6622 break;
6623 }
6624 }
6625 }
6626
6627
6628 /* Fire event outside of the lock */
6629 if (fRemoved)
6630 {
6631 Assert(!pAttach.isNull());
6632 ComPtr<IEventSource> es;
6633 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6634 Assert(SUCCEEDED(rc));
6635 Bstr mid;
6636 rc = this->COMGETTER(Id)(mid.asOutParam());
6637 Assert(SUCCEEDED(rc));
6638 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6639 }
6640
6641 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6642 tr("No host PCI device %08x attached"),
6643 hostAddress
6644 );
6645}
6646
6647STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
6648{
6649 CheckComArgOutSafeArrayPointerValid(aAssignments);
6650
6651 AutoCaller autoCaller(this);
6652 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6653
6654 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6655
6656 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
6657 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
6658
6659 return S_OK;
6660}
6661
6662STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
6663{
6664 CheckComArgOutPointerValid(aBandwidthControl);
6665
6666 AutoCaller autoCaller(this);
6667 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6668
6669 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
6670
6671 return S_OK;
6672}
6673
6674STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
6675{
6676 CheckComArgOutPointerValid(pfEnabled);
6677 AutoCaller autoCaller(this);
6678 HRESULT hrc = autoCaller.rc();
6679 if (SUCCEEDED(hrc))
6680 {
6681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6682 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
6683 }
6684 return hrc;
6685}
6686
6687STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
6688{
6689 AutoCaller autoCaller(this);
6690 HRESULT hrc = autoCaller.rc();
6691 if (SUCCEEDED(hrc))
6692 {
6693 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6694 hrc = checkStateDependency(MutableStateDep);
6695 if (SUCCEEDED(hrc))
6696 {
6697 hrc = mHWData.backupEx();
6698 if (SUCCEEDED(hrc))
6699 {
6700 setModified(IsModified_MachineData);
6701 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
6702 }
6703 }
6704 }
6705 return hrc;
6706}
6707
6708STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
6709{
6710 CheckComArgOutPointerValid(pbstrConfig);
6711 AutoCaller autoCaller(this);
6712 HRESULT hrc = autoCaller.rc();
6713 if (SUCCEEDED(hrc))
6714 {
6715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6716 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
6717 }
6718 return hrc;
6719}
6720
6721STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
6722{
6723 CheckComArgStr(bstrConfig);
6724 AutoCaller autoCaller(this);
6725 HRESULT hrc = autoCaller.rc();
6726 if (SUCCEEDED(hrc))
6727 {
6728 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6729 hrc = checkStateDependency(MutableStateDep);
6730 if (SUCCEEDED(hrc))
6731 {
6732 hrc = mHWData.backupEx();
6733 if (SUCCEEDED(hrc))
6734 {
6735 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
6736 if (SUCCEEDED(hrc))
6737 setModified(IsModified_MachineData);
6738 }
6739 }
6740 }
6741 return hrc;
6742
6743}
6744
6745STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
6746{
6747 CheckComArgOutPointerValid(pfAllow);
6748 AutoCaller autoCaller(this);
6749 HRESULT hrc = autoCaller.rc();
6750 if (SUCCEEDED(hrc))
6751 {
6752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6753 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
6754 }
6755 return hrc;
6756}
6757
6758STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
6759{
6760 AutoCaller autoCaller(this);
6761 HRESULT hrc = autoCaller.rc();
6762 if (SUCCEEDED(hrc))
6763 {
6764 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6765 hrc = checkStateDependency(MutableStateDep);
6766 if (SUCCEEDED(hrc))
6767 {
6768 hrc = mHWData.backupEx();
6769 if (SUCCEEDED(hrc))
6770 {
6771 setModified(IsModified_MachineData);
6772 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
6773 }
6774 }
6775 }
6776 return hrc;
6777}
6778
6779STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
6780{
6781 CheckComArgOutPointerValid(pfEnabled);
6782 AutoCaller autoCaller(this);
6783 HRESULT hrc = autoCaller.rc();
6784 if (SUCCEEDED(hrc))
6785 {
6786 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6787 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
6788 }
6789 return hrc;
6790}
6791
6792STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
6793{
6794 AutoCaller autoCaller(this);
6795 HRESULT hrc = autoCaller.rc();
6796 if (SUCCEEDED(hrc))
6797 {
6798 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6799 hrc = checkStateDependency(MutableStateDep);
6800 if ( SUCCEEDED(hrc)
6801 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
6802 {
6803 AutostartDb *autostartDb = mParent->getAutostartDb();
6804 int vrc;
6805
6806 if (fEnabled)
6807 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6808 else
6809 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6810
6811 if (RT_SUCCESS(vrc))
6812 {
6813 hrc = mHWData.backupEx();
6814 if (SUCCEEDED(hrc))
6815 {
6816 setModified(IsModified_MachineData);
6817 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
6818 }
6819 }
6820 else if (vrc == VERR_NOT_SUPPORTED)
6821 hrc = setError(VBOX_E_NOT_SUPPORTED,
6822 tr("The VM autostart feature is not supported on this platform"));
6823 else if (vrc == VERR_PATH_NOT_FOUND)
6824 hrc = setError(E_FAIL,
6825 tr("The path to the autostart database is not set"));
6826 else
6827 hrc = setError(E_UNEXPECTED,
6828 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6829 fEnabled ? "Adding" : "Removing",
6830 mUserData->s.strName.c_str(), vrc);
6831 }
6832 }
6833 return hrc;
6834}
6835
6836STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
6837{
6838 CheckComArgOutPointerValid(puDelay);
6839 AutoCaller autoCaller(this);
6840 HRESULT hrc = autoCaller.rc();
6841 if (SUCCEEDED(hrc))
6842 {
6843 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6844 *puDelay = mHWData->mAutostart.uAutostartDelay;
6845 }
6846 return hrc;
6847}
6848
6849STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
6850{
6851 AutoCaller autoCaller(this);
6852 HRESULT hrc = autoCaller.rc();
6853 if (SUCCEEDED(hrc))
6854 {
6855 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6856 hrc = checkStateDependency(MutableStateDep);
6857 if (SUCCEEDED(hrc))
6858 {
6859 hrc = mHWData.backupEx();
6860 if (SUCCEEDED(hrc))
6861 {
6862 setModified(IsModified_MachineData);
6863 mHWData->mAutostart.uAutostartDelay = uDelay;
6864 }
6865 }
6866 }
6867 return hrc;
6868}
6869
6870STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
6871{
6872 CheckComArgOutPointerValid(penmAutostopType);
6873 AutoCaller autoCaller(this);
6874 HRESULT hrc = autoCaller.rc();
6875 if (SUCCEEDED(hrc))
6876 {
6877 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6878 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
6879 }
6880 return hrc;
6881}
6882
6883STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
6884{
6885 AutoCaller autoCaller(this);
6886 HRESULT hrc = autoCaller.rc();
6887 if (SUCCEEDED(hrc))
6888 {
6889 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6890 hrc = checkStateDependency(MutableStateDep);
6891 if ( SUCCEEDED(hrc)
6892 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
6893 {
6894 AutostartDb *autostartDb = mParent->getAutostartDb();
6895 int vrc;
6896
6897 if (enmAutostopType != AutostopType_Disabled)
6898 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6899 else
6900 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6901
6902 if (RT_SUCCESS(vrc))
6903 {
6904 hrc = mHWData.backupEx();
6905 if (SUCCEEDED(hrc))
6906 {
6907 setModified(IsModified_MachineData);
6908 mHWData->mAutostart.enmAutostopType = enmAutostopType;
6909 }
6910 }
6911 else if (vrc == VERR_NOT_SUPPORTED)
6912 hrc = setError(VBOX_E_NOT_SUPPORTED,
6913 tr("The VM autostop feature is not supported on this platform"));
6914 else if (vrc == VERR_PATH_NOT_FOUND)
6915 hrc = setError(E_FAIL,
6916 tr("The path to the autostart database is not set"));
6917 else
6918 hrc = setError(E_UNEXPECTED,
6919 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6920 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6921 mUserData->s.strName.c_str(), vrc);
6922 }
6923 }
6924 return hrc;
6925}
6926
6927
6928STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
6929{
6930 LogFlowFuncEnter();
6931
6932 CheckComArgNotNull(pTarget);
6933 CheckComArgOutPointerValid(pProgress);
6934
6935 /* Convert the options. */
6936 RTCList<CloneOptions_T> optList;
6937 if (options != NULL)
6938 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
6939
6940 if (optList.contains(CloneOptions_Link))
6941 {
6942 if (!isSnapshotMachine())
6943 return setError(E_INVALIDARG,
6944 tr("Linked clone can only be created from a snapshot"));
6945 if (mode != CloneMode_MachineState)
6946 return setError(E_INVALIDARG,
6947 tr("Linked clone can only be created for a single machine state"));
6948 }
6949 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6950
6951 AutoCaller autoCaller(this);
6952 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6953
6954
6955 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
6956
6957 HRESULT rc = pWorker->start(pProgress);
6958
6959 LogFlowFuncLeave();
6960
6961 return rc;
6962}
6963
6964// public methods for internal purposes
6965/////////////////////////////////////////////////////////////////////////////
6966
6967/**
6968 * Adds the given IsModified_* flag to the dirty flags of the machine.
6969 * This must be called either during loadSettings or under the machine write lock.
6970 * @param fl
6971 */
6972void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
6973{
6974 mData->flModifications |= fl;
6975 if (fAllowStateModification && isStateModificationAllowed())
6976 mData->mCurrentStateModified = true;
6977}
6978
6979/**
6980 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
6981 * care of the write locking.
6982 *
6983 * @param fModifications The flag to add.
6984 */
6985void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
6986{
6987 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6988 setModified(fModification, fAllowStateModification);
6989}
6990
6991/**
6992 * Saves the registry entry of this machine to the given configuration node.
6993 *
6994 * @param aEntryNode Node to save the registry entry to.
6995 *
6996 * @note locks this object for reading.
6997 */
6998HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
6999{
7000 AutoLimitedCaller autoCaller(this);
7001 AssertComRCReturnRC(autoCaller.rc());
7002
7003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7004
7005 data.uuid = mData->mUuid;
7006 data.strSettingsFile = mData->m_strConfigFile;
7007
7008 return S_OK;
7009}
7010
7011/**
7012 * Calculates the absolute path of the given path taking the directory of the
7013 * machine settings file as the current directory.
7014 *
7015 * @param aPath Path to calculate the absolute path for.
7016 * @param aResult Where to put the result (used only on success, can be the
7017 * same Utf8Str instance as passed in @a aPath).
7018 * @return IPRT result.
7019 *
7020 * @note Locks this object for reading.
7021 */
7022int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7023{
7024 AutoCaller autoCaller(this);
7025 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7026
7027 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7028
7029 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7030
7031 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7032
7033 strSettingsDir.stripFilename();
7034 char folder[RTPATH_MAX];
7035 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7036 if (RT_SUCCESS(vrc))
7037 aResult = folder;
7038
7039 return vrc;
7040}
7041
7042/**
7043 * Copies strSource to strTarget, making it relative to the machine folder
7044 * if it is a subdirectory thereof, or simply copying it otherwise.
7045 *
7046 * @param strSource Path to evaluate and copy.
7047 * @param strTarget Buffer to receive target path.
7048 *
7049 * @note Locks this object for reading.
7050 */
7051void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7052 Utf8Str &strTarget)
7053{
7054 AutoCaller autoCaller(this);
7055 AssertComRCReturn(autoCaller.rc(), (void)0);
7056
7057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7058
7059 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7060 // use strTarget as a temporary buffer to hold the machine settings dir
7061 strTarget = mData->m_strConfigFileFull;
7062 strTarget.stripFilename();
7063 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7064 {
7065 // is relative: then append what's left
7066 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7067 // for empty paths (only possible for subdirs) use "." to avoid
7068 // triggering default settings for not present config attributes.
7069 if (strTarget.isEmpty())
7070 strTarget = ".";
7071 }
7072 else
7073 // is not relative: then overwrite
7074 strTarget = strSource;
7075}
7076
7077/**
7078 * Returns the full path to the machine's log folder in the
7079 * \a aLogFolder argument.
7080 */
7081void Machine::getLogFolder(Utf8Str &aLogFolder)
7082{
7083 AutoCaller autoCaller(this);
7084 AssertComRCReturnVoid(autoCaller.rc());
7085
7086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7087
7088 char szTmp[RTPATH_MAX];
7089 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7090 if (RT_SUCCESS(vrc))
7091 {
7092 if (szTmp[0] && !mUserData.isNull())
7093 {
7094 char szTmp2[RTPATH_MAX];
7095 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7096 if (RT_SUCCESS(vrc))
7097 aLogFolder = BstrFmt("%s%c%s",
7098 szTmp2,
7099 RTPATH_DELIMITER,
7100 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7101 }
7102 else
7103 vrc = VERR_PATH_IS_RELATIVE;
7104 }
7105
7106 if (RT_FAILURE(vrc))
7107 {
7108 // fallback if VBOX_USER_LOGHOME is not set or invalid
7109 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7110 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7111 aLogFolder.append(RTPATH_DELIMITER);
7112 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7113 }
7114}
7115
7116/**
7117 * Returns the full path to the machine's log file for an given index.
7118 */
7119Utf8Str Machine::queryLogFilename(ULONG idx)
7120{
7121 Utf8Str logFolder;
7122 getLogFolder(logFolder);
7123 Assert(logFolder.length());
7124 Utf8Str log;
7125 if (idx == 0)
7126 log = Utf8StrFmt("%s%cVBox.log",
7127 logFolder.c_str(), RTPATH_DELIMITER);
7128 else
7129 log = Utf8StrFmt("%s%cVBox.log.%d",
7130 logFolder.c_str(), RTPATH_DELIMITER, idx);
7131 return log;
7132}
7133
7134/**
7135 * Composes a unique saved state filename based on the current system time. The filename is
7136 * granular to the second so this will work so long as no more than one snapshot is taken on
7137 * a machine per second.
7138 *
7139 * Before version 4.1, we used this formula for saved state files:
7140 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7141 * which no longer works because saved state files can now be shared between the saved state of the
7142 * "saved" machine and an online snapshot, and the following would cause problems:
7143 * 1) save machine
7144 * 2) create online snapshot from that machine state --> reusing saved state file
7145 * 3) save machine again --> filename would be reused, breaking the online snapshot
7146 *
7147 * So instead we now use a timestamp.
7148 *
7149 * @param str
7150 */
7151void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7152{
7153 AutoCaller autoCaller(this);
7154 AssertComRCReturnVoid(autoCaller.rc());
7155
7156 {
7157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7158 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7159 }
7160
7161 RTTIMESPEC ts;
7162 RTTimeNow(&ts);
7163 RTTIME time;
7164 RTTimeExplode(&time, &ts);
7165
7166 strStateFilePath += RTPATH_DELIMITER;
7167 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7168 time.i32Year, time.u8Month, time.u8MonthDay,
7169 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7170}
7171
7172/**
7173 * @note Locks this object for writing, calls the client process
7174 * (inside the lock).
7175 */
7176HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7177 const Utf8Str &strType,
7178 const Utf8Str &strEnvironment,
7179 ProgressProxy *aProgress)
7180{
7181 LogFlowThisFuncEnter();
7182
7183 AssertReturn(aControl, E_FAIL);
7184 AssertReturn(aProgress, E_FAIL);
7185
7186 AutoCaller autoCaller(this);
7187 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7188
7189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7190
7191 if (!mData->mRegistered)
7192 return setError(E_UNEXPECTED,
7193 tr("The machine '%s' is not registered"),
7194 mUserData->s.strName.c_str());
7195
7196 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7197
7198 if ( mData->mSession.mState == SessionState_Locked
7199 || mData->mSession.mState == SessionState_Spawning
7200 || mData->mSession.mState == SessionState_Unlocking)
7201 return setError(VBOX_E_INVALID_OBJECT_STATE,
7202 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7203 mUserData->s.strName.c_str());
7204
7205 /* may not be busy */
7206 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7207
7208 /* get the path to the executable */
7209 char szPath[RTPATH_MAX];
7210 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7211 size_t sz = strlen(szPath);
7212 szPath[sz++] = RTPATH_DELIMITER;
7213 szPath[sz] = 0;
7214 char *cmd = szPath + sz;
7215 sz = RTPATH_MAX - sz;
7216
7217 int vrc = VINF_SUCCESS;
7218 RTPROCESS pid = NIL_RTPROCESS;
7219
7220 RTENV env = RTENV_DEFAULT;
7221
7222 if (!strEnvironment.isEmpty())
7223 {
7224 char *newEnvStr = NULL;
7225
7226 do
7227 {
7228 /* clone the current environment */
7229 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7230 AssertRCBreakStmt(vrc2, vrc = vrc2);
7231
7232 newEnvStr = RTStrDup(strEnvironment.c_str());
7233 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7234
7235 /* put new variables to the environment
7236 * (ignore empty variable names here since RTEnv API
7237 * intentionally doesn't do that) */
7238 char *var = newEnvStr;
7239 for (char *p = newEnvStr; *p; ++p)
7240 {
7241 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7242 {
7243 *p = '\0';
7244 if (*var)
7245 {
7246 char *val = strchr(var, '=');
7247 if (val)
7248 {
7249 *val++ = '\0';
7250 vrc2 = RTEnvSetEx(env, var, val);
7251 }
7252 else
7253 vrc2 = RTEnvUnsetEx(env, var);
7254 if (RT_FAILURE(vrc2))
7255 break;
7256 }
7257 var = p + 1;
7258 }
7259 }
7260 if (RT_SUCCESS(vrc2) && *var)
7261 vrc2 = RTEnvPutEx(env, var);
7262
7263 AssertRCBreakStmt(vrc2, vrc = vrc2);
7264 }
7265 while (0);
7266
7267 if (newEnvStr != NULL)
7268 RTStrFree(newEnvStr);
7269 }
7270
7271 /* Qt is default */
7272#ifdef VBOX_WITH_QTGUI
7273 if (strType == "gui" || strType == "GUI/Qt")
7274 {
7275# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7276 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7277# else
7278 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7279# endif
7280 Assert(sz >= sizeof(VirtualBox_exe));
7281 strcpy(cmd, VirtualBox_exe);
7282
7283 Utf8Str idStr = mData->mUuid.toString();
7284 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7285 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7286 }
7287#else /* !VBOX_WITH_QTGUI */
7288 if (0)
7289 ;
7290#endif /* VBOX_WITH_QTGUI */
7291
7292 else
7293
7294#ifdef VBOX_WITH_VBOXSDL
7295 if (strType == "sdl" || strType == "GUI/SDL")
7296 {
7297 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7298 Assert(sz >= sizeof(VBoxSDL_exe));
7299 strcpy(cmd, VBoxSDL_exe);
7300
7301 Utf8Str idStr = mData->mUuid.toString();
7302 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7303 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7304 }
7305#else /* !VBOX_WITH_VBOXSDL */
7306 if (0)
7307 ;
7308#endif /* !VBOX_WITH_VBOXSDL */
7309
7310 else
7311
7312#ifdef VBOX_WITH_HEADLESS
7313 if ( strType == "headless"
7314 || strType == "capture"
7315 || strType == "vrdp" /* Deprecated. Same as headless. */
7316 )
7317 {
7318 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7319 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7320 * and a VM works even if the server has not been installed.
7321 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7322 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7323 * differently in 4.0 and 3.x.
7324 */
7325 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7326 Assert(sz >= sizeof(VBoxHeadless_exe));
7327 strcpy(cmd, VBoxHeadless_exe);
7328
7329 Utf8Str idStr = mData->mUuid.toString();
7330 /* Leave space for "--capture" arg. */
7331 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7332 "--startvm", idStr.c_str(),
7333 "--vrde", "config",
7334 0, /* For "--capture". */
7335 0 };
7336 if (strType == "capture")
7337 {
7338 unsigned pos = RT_ELEMENTS(args) - 2;
7339 args[pos] = "--capture";
7340 }
7341 vrc = RTProcCreate(szPath, args, env,
7342#ifdef RT_OS_WINDOWS
7343 RTPROC_FLAGS_NO_WINDOW
7344#else
7345 0
7346#endif
7347 , &pid);
7348 }
7349#else /* !VBOX_WITH_HEADLESS */
7350 if (0)
7351 ;
7352#endif /* !VBOX_WITH_HEADLESS */
7353 else
7354 {
7355 RTEnvDestroy(env);
7356 return setError(E_INVALIDARG,
7357 tr("Invalid session type: '%s'"),
7358 strType.c_str());
7359 }
7360
7361 RTEnvDestroy(env);
7362
7363 if (RT_FAILURE(vrc))
7364 return setError(VBOX_E_IPRT_ERROR,
7365 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7366 mUserData->s.strName.c_str(), vrc);
7367
7368 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7369
7370 /*
7371 * Note that we don't release the lock here before calling the client,
7372 * because it doesn't need to call us back if called with a NULL argument.
7373 * Releasing the lock here is dangerous because we didn't prepare the
7374 * launch data yet, but the client we've just started may happen to be
7375 * too fast and call openSession() that will fail (because of PID, etc.),
7376 * so that the Machine will never get out of the Spawning session state.
7377 */
7378
7379 /* inform the session that it will be a remote one */
7380 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7381 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
7382 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7383
7384 if (FAILED(rc))
7385 {
7386 /* restore the session state */
7387 mData->mSession.mState = SessionState_Unlocked;
7388 /* The failure may occur w/o any error info (from RPC), so provide one */
7389 return setError(VBOX_E_VM_ERROR,
7390 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7391 }
7392
7393 /* attach launch data to the machine */
7394 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7395 mData->mSession.mRemoteControls.push_back(aControl);
7396 mData->mSession.mProgress = aProgress;
7397 mData->mSession.mPID = pid;
7398 mData->mSession.mState = SessionState_Spawning;
7399 mData->mSession.mType = strType;
7400
7401 LogFlowThisFuncLeave();
7402 return S_OK;
7403}
7404
7405/**
7406 * Returns @c true if the given machine has an open direct session and returns
7407 * the session machine instance and additional session data (on some platforms)
7408 * if so.
7409 *
7410 * Note that when the method returns @c false, the arguments remain unchanged.
7411 *
7412 * @param aMachine Session machine object.
7413 * @param aControl Direct session control object (optional).
7414 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7415 *
7416 * @note locks this object for reading.
7417 */
7418#if defined(RT_OS_WINDOWS)
7419bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7420 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7421 HANDLE *aIPCSem /*= NULL*/,
7422 bool aAllowClosing /*= false*/)
7423#elif defined(RT_OS_OS2)
7424bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7425 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7426 HMTX *aIPCSem /*= NULL*/,
7427 bool aAllowClosing /*= false*/)
7428#else
7429bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7430 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7431 bool aAllowClosing /*= false*/)
7432#endif
7433{
7434 AutoLimitedCaller autoCaller(this);
7435 AssertComRCReturn(autoCaller.rc(), false);
7436
7437 /* just return false for inaccessible machines */
7438 if (autoCaller.state() != Ready)
7439 return false;
7440
7441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7442
7443 if ( mData->mSession.mState == SessionState_Locked
7444 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7445 )
7446 {
7447 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7448
7449 aMachine = mData->mSession.mMachine;
7450
7451 if (aControl != NULL)
7452 *aControl = mData->mSession.mDirectControl;
7453
7454#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7455 /* Additional session data */
7456 if (aIPCSem != NULL)
7457 *aIPCSem = aMachine->mIPCSem;
7458#endif
7459 return true;
7460 }
7461
7462 return false;
7463}
7464
7465/**
7466 * Returns @c true if the given machine has an spawning direct session and
7467 * returns and additional session data (on some platforms) if so.
7468 *
7469 * Note that when the method returns @c false, the arguments remain unchanged.
7470 *
7471 * @param aPID PID of the spawned direct session process.
7472 *
7473 * @note locks this object for reading.
7474 */
7475#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7476bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7477#else
7478bool Machine::isSessionSpawning()
7479#endif
7480{
7481 AutoLimitedCaller autoCaller(this);
7482 AssertComRCReturn(autoCaller.rc(), false);
7483
7484 /* just return false for inaccessible machines */
7485 if (autoCaller.state() != Ready)
7486 return false;
7487
7488 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7489
7490 if (mData->mSession.mState == SessionState_Spawning)
7491 {
7492#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7493 /* Additional session data */
7494 if (aPID != NULL)
7495 {
7496 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
7497 *aPID = mData->mSession.mPID;
7498 }
7499#endif
7500 return true;
7501 }
7502
7503 return false;
7504}
7505
7506/**
7507 * Called from the client watcher thread to check for unexpected client process
7508 * death during Session_Spawning state (e.g. before it successfully opened a
7509 * direct session).
7510 *
7511 * On Win32 and on OS/2, this method is called only when we've got the
7512 * direct client's process termination notification, so it always returns @c
7513 * true.
7514 *
7515 * On other platforms, this method returns @c true if the client process is
7516 * terminated and @c false if it's still alive.
7517 *
7518 * @note Locks this object for writing.
7519 */
7520bool Machine::checkForSpawnFailure()
7521{
7522 AutoCaller autoCaller(this);
7523 if (!autoCaller.isOk())
7524 {
7525 /* nothing to do */
7526 LogFlowThisFunc(("Already uninitialized!\n"));
7527 return true;
7528 }
7529
7530 /* VirtualBox::addProcessToReap() needs a write lock */
7531 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
7532
7533 if (mData->mSession.mState != SessionState_Spawning)
7534 {
7535 /* nothing to do */
7536 LogFlowThisFunc(("Not spawning any more!\n"));
7537 return true;
7538 }
7539
7540 HRESULT rc = S_OK;
7541
7542#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7543
7544 /* the process was already unexpectedly terminated, we just need to set an
7545 * error and finalize session spawning */
7546 rc = setError(E_FAIL,
7547 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
7548 getName().c_str());
7549#else
7550
7551 /* PID not yet initialized, skip check. */
7552 if (mData->mSession.mPID == NIL_RTPROCESS)
7553 return false;
7554
7555 RTPROCSTATUS status;
7556 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
7557 &status);
7558
7559 if (vrc != VERR_PROCESS_RUNNING)
7560 {
7561 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7562 rc = setError(E_FAIL,
7563 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7564 getName().c_str(), status.iStatus);
7565 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7566 rc = setError(E_FAIL,
7567 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7568 getName().c_str(), status.iStatus);
7569 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7570 rc = setError(E_FAIL,
7571 tr("The virtual machine '%s' has terminated abnormally"),
7572 getName().c_str(), status.iStatus);
7573 else
7574 rc = setError(E_FAIL,
7575 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7576 getName().c_str(), rc);
7577 }
7578
7579#endif
7580
7581 if (FAILED(rc))
7582 {
7583 /* Close the remote session, remove the remote control from the list
7584 * and reset session state to Closed (@note keep the code in sync with
7585 * the relevant part in checkForSpawnFailure()). */
7586
7587 Assert(mData->mSession.mRemoteControls.size() == 1);
7588 if (mData->mSession.mRemoteControls.size() == 1)
7589 {
7590 ErrorInfoKeeper eik;
7591 mData->mSession.mRemoteControls.front()->Uninitialize();
7592 }
7593
7594 mData->mSession.mRemoteControls.clear();
7595 mData->mSession.mState = SessionState_Unlocked;
7596
7597 /* finalize the progress after setting the state */
7598 if (!mData->mSession.mProgress.isNull())
7599 {
7600 mData->mSession.mProgress->notifyComplete(rc);
7601 mData->mSession.mProgress.setNull();
7602 }
7603
7604 mParent->addProcessToReap(mData->mSession.mPID);
7605 mData->mSession.mPID = NIL_RTPROCESS;
7606
7607 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7608 return true;
7609 }
7610
7611 return false;
7612}
7613
7614/**
7615 * Checks whether the machine can be registered. If so, commits and saves
7616 * all settings.
7617 *
7618 * @note Must be called from mParent's write lock. Locks this object and
7619 * children for writing.
7620 */
7621HRESULT Machine::prepareRegister()
7622{
7623 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7624
7625 AutoLimitedCaller autoCaller(this);
7626 AssertComRCReturnRC(autoCaller.rc());
7627
7628 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7629
7630 /* wait for state dependents to drop to zero */
7631 ensureNoStateDependencies();
7632
7633 if (!mData->mAccessible)
7634 return setError(VBOX_E_INVALID_OBJECT_STATE,
7635 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7636 mUserData->s.strName.c_str(),
7637 mData->mUuid.toString().c_str());
7638
7639 AssertReturn(autoCaller.state() == Ready, E_FAIL);
7640
7641 if (mData->mRegistered)
7642 return setError(VBOX_E_INVALID_OBJECT_STATE,
7643 tr("The machine '%s' with UUID {%s} is already registered"),
7644 mUserData->s.strName.c_str(),
7645 mData->mUuid.toString().c_str());
7646
7647 HRESULT rc = S_OK;
7648
7649 // Ensure the settings are saved. If we are going to be registered and
7650 // no config file exists yet, create it by calling saveSettings() too.
7651 if ( (mData->flModifications)
7652 || (!mData->pMachineConfigFile->fileExists())
7653 )
7654 {
7655 rc = saveSettings(NULL);
7656 // no need to check whether VirtualBox.xml needs saving too since
7657 // we can't have a machine XML file rename pending
7658 if (FAILED(rc)) return rc;
7659 }
7660
7661 /* more config checking goes here */
7662
7663 if (SUCCEEDED(rc))
7664 {
7665 /* we may have had implicit modifications we want to fix on success */
7666 commit();
7667
7668 mData->mRegistered = true;
7669 }
7670 else
7671 {
7672 /* we may have had implicit modifications we want to cancel on failure*/
7673 rollback(false /* aNotify */);
7674 }
7675
7676 return rc;
7677}
7678
7679/**
7680 * Increases the number of objects dependent on the machine state or on the
7681 * registered state. Guarantees that these two states will not change at least
7682 * until #releaseStateDependency() is called.
7683 *
7684 * Depending on the @a aDepType value, additional state checks may be made.
7685 * These checks will set extended error info on failure. See
7686 * #checkStateDependency() for more info.
7687 *
7688 * If this method returns a failure, the dependency is not added and the caller
7689 * is not allowed to rely on any particular machine state or registration state
7690 * value and may return the failed result code to the upper level.
7691 *
7692 * @param aDepType Dependency type to add.
7693 * @param aState Current machine state (NULL if not interested).
7694 * @param aRegistered Current registered state (NULL if not interested).
7695 *
7696 * @note Locks this object for writing.
7697 */
7698HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7699 MachineState_T *aState /* = NULL */,
7700 BOOL *aRegistered /* = NULL */)
7701{
7702 AutoCaller autoCaller(this);
7703 AssertComRCReturnRC(autoCaller.rc());
7704
7705 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7706
7707 HRESULT rc = checkStateDependency(aDepType);
7708 if (FAILED(rc)) return rc;
7709
7710 {
7711 if (mData->mMachineStateChangePending != 0)
7712 {
7713 /* ensureNoStateDependencies() is waiting for state dependencies to
7714 * drop to zero so don't add more. It may make sense to wait a bit
7715 * and retry before reporting an error (since the pending state
7716 * transition should be really quick) but let's just assert for
7717 * now to see if it ever happens on practice. */
7718
7719 AssertFailed();
7720
7721 return setError(E_ACCESSDENIED,
7722 tr("Machine state change is in progress. Please retry the operation later."));
7723 }
7724
7725 ++mData->mMachineStateDeps;
7726 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7727 }
7728
7729 if (aState)
7730 *aState = mData->mMachineState;
7731 if (aRegistered)
7732 *aRegistered = mData->mRegistered;
7733
7734 return S_OK;
7735}
7736
7737/**
7738 * Decreases the number of objects dependent on the machine state.
7739 * Must always complete the #addStateDependency() call after the state
7740 * dependency is no more necessary.
7741 */
7742void Machine::releaseStateDependency()
7743{
7744 AutoCaller autoCaller(this);
7745 AssertComRCReturnVoid(autoCaller.rc());
7746
7747 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7748
7749 /* releaseStateDependency() w/o addStateDependency()? */
7750 AssertReturnVoid(mData->mMachineStateDeps != 0);
7751 -- mData->mMachineStateDeps;
7752
7753 if (mData->mMachineStateDeps == 0)
7754 {
7755 /* inform ensureNoStateDependencies() that there are no more deps */
7756 if (mData->mMachineStateChangePending != 0)
7757 {
7758 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7759 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7760 }
7761 }
7762}
7763
7764// protected methods
7765/////////////////////////////////////////////////////////////////////////////
7766
7767/**
7768 * Performs machine state checks based on the @a aDepType value. If a check
7769 * fails, this method will set extended error info, otherwise it will return
7770 * S_OK. It is supposed, that on failure, the caller will immediately return
7771 * the return value of this method to the upper level.
7772 *
7773 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7774 *
7775 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7776 * current state of this machine object allows to change settings of the
7777 * machine (i.e. the machine is not registered, or registered but not running
7778 * and not saved). It is useful to call this method from Machine setters
7779 * before performing any change.
7780 *
7781 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7782 * as for MutableStateDep except that if the machine is saved, S_OK is also
7783 * returned. This is useful in setters which allow changing machine
7784 * properties when it is in the saved state.
7785 *
7786 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7787 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7788 * Aborted).
7789 *
7790 * @param aDepType Dependency type to check.
7791 *
7792 * @note Non Machine based classes should use #addStateDependency() and
7793 * #releaseStateDependency() methods or the smart AutoStateDependency
7794 * template.
7795 *
7796 * @note This method must be called from under this object's read or write
7797 * lock.
7798 */
7799HRESULT Machine::checkStateDependency(StateDependency aDepType)
7800{
7801 switch (aDepType)
7802 {
7803 case AnyStateDep:
7804 {
7805 break;
7806 }
7807 case MutableStateDep:
7808 {
7809 if ( mData->mRegistered
7810 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7811 || ( mData->mMachineState != MachineState_Paused
7812 && mData->mMachineState != MachineState_Running
7813 && mData->mMachineState != MachineState_Aborted
7814 && mData->mMachineState != MachineState_Teleported
7815 && mData->mMachineState != MachineState_PoweredOff
7816 )
7817 )
7818 )
7819 return setError(VBOX_E_INVALID_VM_STATE,
7820 tr("The machine is not mutable (state is %s)"),
7821 Global::stringifyMachineState(mData->mMachineState));
7822 break;
7823 }
7824 case MutableOrSavedStateDep:
7825 {
7826 if ( mData->mRegistered
7827 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7828 || ( mData->mMachineState != MachineState_Paused
7829 && mData->mMachineState != MachineState_Running
7830 && mData->mMachineState != MachineState_Aborted
7831 && mData->mMachineState != MachineState_Teleported
7832 && mData->mMachineState != MachineState_Saved
7833 && mData->mMachineState != MachineState_PoweredOff
7834 )
7835 )
7836 )
7837 return setError(VBOX_E_INVALID_VM_STATE,
7838 tr("The machine is not mutable (state is %s)"),
7839 Global::stringifyMachineState(mData->mMachineState));
7840 break;
7841 }
7842 case OfflineStateDep:
7843 {
7844 if ( mData->mRegistered
7845 && ( !isSessionMachine()
7846 || ( mData->mMachineState != MachineState_PoweredOff
7847 && mData->mMachineState != MachineState_Saved
7848 && mData->mMachineState != MachineState_Aborted
7849 && mData->mMachineState != MachineState_Teleported
7850 )
7851 )
7852 )
7853 return setError(VBOX_E_INVALID_VM_STATE,
7854 tr("The machine is not offline (state is %s)"),
7855 Global::stringifyMachineState(mData->mMachineState));
7856 break;
7857 }
7858 }
7859
7860 return S_OK;
7861}
7862
7863/**
7864 * Helper to initialize all associated child objects and allocate data
7865 * structures.
7866 *
7867 * This method must be called as a part of the object's initialization procedure
7868 * (usually done in the #init() method).
7869 *
7870 * @note Must be called only from #init() or from #registeredInit().
7871 */
7872HRESULT Machine::initDataAndChildObjects()
7873{
7874 AutoCaller autoCaller(this);
7875 AssertComRCReturnRC(autoCaller.rc());
7876 AssertComRCReturn(autoCaller.state() == InInit ||
7877 autoCaller.state() == Limited, E_FAIL);
7878
7879 AssertReturn(!mData->mAccessible, E_FAIL);
7880
7881 /* allocate data structures */
7882 mSSData.allocate();
7883 mUserData.allocate();
7884 mHWData.allocate();
7885 mMediaData.allocate();
7886 mStorageControllers.allocate();
7887
7888 /* initialize mOSTypeId */
7889 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
7890
7891 /* create associated BIOS settings object */
7892 unconst(mBIOSSettings).createObject();
7893 mBIOSSettings->init(this);
7894
7895 /* create an associated VRDE object (default is disabled) */
7896 unconst(mVRDEServer).createObject();
7897 mVRDEServer->init(this);
7898
7899 /* create associated serial port objects */
7900 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
7901 {
7902 unconst(mSerialPorts[slot]).createObject();
7903 mSerialPorts[slot]->init(this, slot);
7904 }
7905
7906 /* create associated parallel port objects */
7907 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7908 {
7909 unconst(mParallelPorts[slot]).createObject();
7910 mParallelPorts[slot]->init(this, slot);
7911 }
7912
7913 /* create the audio adapter object (always present, default is disabled) */
7914 unconst(mAudioAdapter).createObject();
7915 mAudioAdapter->init(this);
7916
7917 /* create the USB controller object (always present, default is disabled) */
7918 unconst(mUSBController).createObject();
7919 mUSBController->init(this);
7920
7921 /* create associated network adapter objects */
7922 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
7923 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7924 {
7925 unconst(mNetworkAdapters[slot]).createObject();
7926 mNetworkAdapters[slot]->init(this, slot);
7927 }
7928
7929 /* create the bandwidth control */
7930 unconst(mBandwidthControl).createObject();
7931 mBandwidthControl->init(this);
7932
7933 return S_OK;
7934}
7935
7936/**
7937 * Helper to uninitialize all associated child objects and to free all data
7938 * structures.
7939 *
7940 * This method must be called as a part of the object's uninitialization
7941 * procedure (usually done in the #uninit() method).
7942 *
7943 * @note Must be called only from #uninit() or from #registeredInit().
7944 */
7945void Machine::uninitDataAndChildObjects()
7946{
7947 AutoCaller autoCaller(this);
7948 AssertComRCReturnVoid(autoCaller.rc());
7949 AssertComRCReturnVoid( autoCaller.state() == InUninit
7950 || autoCaller.state() == Limited);
7951
7952 /* tell all our other child objects we've been uninitialized */
7953 if (mBandwidthControl)
7954 {
7955 mBandwidthControl->uninit();
7956 unconst(mBandwidthControl).setNull();
7957 }
7958
7959 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7960 {
7961 if (mNetworkAdapters[slot])
7962 {
7963 mNetworkAdapters[slot]->uninit();
7964 unconst(mNetworkAdapters[slot]).setNull();
7965 }
7966 }
7967
7968 if (mUSBController)
7969 {
7970 mUSBController->uninit();
7971 unconst(mUSBController).setNull();
7972 }
7973
7974 if (mAudioAdapter)
7975 {
7976 mAudioAdapter->uninit();
7977 unconst(mAudioAdapter).setNull();
7978 }
7979
7980 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7981 {
7982 if (mParallelPorts[slot])
7983 {
7984 mParallelPorts[slot]->uninit();
7985 unconst(mParallelPorts[slot]).setNull();
7986 }
7987 }
7988
7989 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
7990 {
7991 if (mSerialPorts[slot])
7992 {
7993 mSerialPorts[slot]->uninit();
7994 unconst(mSerialPorts[slot]).setNull();
7995 }
7996 }
7997
7998 if (mVRDEServer)
7999 {
8000 mVRDEServer->uninit();
8001 unconst(mVRDEServer).setNull();
8002 }
8003
8004 if (mBIOSSettings)
8005 {
8006 mBIOSSettings->uninit();
8007 unconst(mBIOSSettings).setNull();
8008 }
8009
8010 /* Deassociate hard disks (only when a real Machine or a SnapshotMachine
8011 * instance is uninitialized; SessionMachine instances refer to real
8012 * Machine hard disks). This is necessary for a clean re-initialization of
8013 * the VM after successfully re-checking the accessibility state. Note
8014 * that in case of normal Machine or SnapshotMachine uninitialization (as
8015 * a result of unregistering or deleting the snapshot), outdated hard
8016 * disk attachments will already be uninitialized and deleted, so this
8017 * code will not affect them. */
8018 if ( !!mMediaData
8019 && (!isSessionMachine())
8020 )
8021 {
8022 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8023 it != mMediaData->mAttachments.end();
8024 ++it)
8025 {
8026 ComObjPtr<Medium> hd = (*it)->getMedium();
8027 if (hd.isNull())
8028 continue;
8029 HRESULT rc = hd->removeBackReference(mData->mUuid, getSnapshotId());
8030 AssertComRC(rc);
8031 }
8032 }
8033
8034 if (!isSessionMachine() && !isSnapshotMachine())
8035 {
8036 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8037 if (mData->mFirstSnapshot)
8038 {
8039 // snapshots tree is protected by media write lock; strictly
8040 // this isn't necessary here since we're deleting the entire
8041 // machine, but otherwise we assert in Snapshot::uninit()
8042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8043 mData->mFirstSnapshot->uninit();
8044 mData->mFirstSnapshot.setNull();
8045 }
8046
8047 mData->mCurrentSnapshot.setNull();
8048 }
8049
8050 /* free data structures (the essential mData structure is not freed here
8051 * since it may be still in use) */
8052 mMediaData.free();
8053 mStorageControllers.free();
8054 mHWData.free();
8055 mUserData.free();
8056 mSSData.free();
8057}
8058
8059/**
8060 * Returns a pointer to the Machine object for this machine that acts like a
8061 * parent for complex machine data objects such as shared folders, etc.
8062 *
8063 * For primary Machine objects and for SnapshotMachine objects, returns this
8064 * object's pointer itself. For SessionMachine objects, returns the peer
8065 * (primary) machine pointer.
8066 */
8067Machine* Machine::getMachine()
8068{
8069 if (isSessionMachine())
8070 return (Machine*)mPeer;
8071 return this;
8072}
8073
8074/**
8075 * Makes sure that there are no machine state dependents. If necessary, waits
8076 * for the number of dependents to drop to zero.
8077 *
8078 * Make sure this method is called from under this object's write lock to
8079 * guarantee that no new dependents may be added when this method returns
8080 * control to the caller.
8081 *
8082 * @note Locks this object for writing. The lock will be released while waiting
8083 * (if necessary).
8084 *
8085 * @warning To be used only in methods that change the machine state!
8086 */
8087void Machine::ensureNoStateDependencies()
8088{
8089 AssertReturnVoid(isWriteLockOnCurrentThread());
8090
8091 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8092
8093 /* Wait for all state dependents if necessary */
8094 if (mData->mMachineStateDeps != 0)
8095 {
8096 /* lazy semaphore creation */
8097 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8098 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8099
8100 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8101 mData->mMachineStateDeps));
8102
8103 ++mData->mMachineStateChangePending;
8104
8105 /* reset the semaphore before waiting, the last dependent will signal
8106 * it */
8107 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8108
8109 alock.release();
8110
8111 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8112
8113 alock.acquire();
8114
8115 -- mData->mMachineStateChangePending;
8116 }
8117}
8118
8119/**
8120 * Changes the machine state and informs callbacks.
8121 *
8122 * This method is not intended to fail so it either returns S_OK or asserts (and
8123 * returns a failure).
8124 *
8125 * @note Locks this object for writing.
8126 */
8127HRESULT Machine::setMachineState(MachineState_T aMachineState)
8128{
8129 LogFlowThisFuncEnter();
8130 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8131
8132 AutoCaller autoCaller(this);
8133 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8134
8135 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8136
8137 /* wait for state dependents to drop to zero */
8138 ensureNoStateDependencies();
8139
8140 if (mData->mMachineState != aMachineState)
8141 {
8142 mData->mMachineState = aMachineState;
8143
8144 RTTimeNow(&mData->mLastStateChange);
8145
8146 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8147 }
8148
8149 LogFlowThisFuncLeave();
8150 return S_OK;
8151}
8152
8153/**
8154 * Searches for a shared folder with the given logical name
8155 * in the collection of shared folders.
8156 *
8157 * @param aName logical name of the shared folder
8158 * @param aSharedFolder where to return the found object
8159 * @param aSetError whether to set the error info if the folder is
8160 * not found
8161 * @return
8162 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8163 *
8164 * @note
8165 * must be called from under the object's lock!
8166 */
8167HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8168 ComObjPtr<SharedFolder> &aSharedFolder,
8169 bool aSetError /* = false */)
8170{
8171 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8172 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8173 it != mHWData->mSharedFolders.end();
8174 ++it)
8175 {
8176 SharedFolder *pSF = *it;
8177 AutoCaller autoCaller(pSF);
8178 if (pSF->getName() == aName)
8179 {
8180 aSharedFolder = pSF;
8181 rc = S_OK;
8182 break;
8183 }
8184 }
8185
8186 if (aSetError && FAILED(rc))
8187 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8188
8189 return rc;
8190}
8191
8192/**
8193 * Initializes all machine instance data from the given settings structures
8194 * from XML. The exception is the machine UUID which needs special handling
8195 * depending on the caller's use case, so the caller needs to set that herself.
8196 *
8197 * This gets called in several contexts during machine initialization:
8198 *
8199 * -- When machine XML exists on disk already and needs to be loaded into memory,
8200 * for example, from registeredInit() to load all registered machines on
8201 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8202 * attached to the machine should be part of some media registry already.
8203 *
8204 * -- During OVF import, when a machine config has been constructed from an
8205 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8206 * ensure that the media listed as attachments in the config (which have
8207 * been imported from the OVF) receive the correct registry ID.
8208 *
8209 * -- During VM cloning.
8210 *
8211 * @param config Machine settings from XML.
8212 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8213 * @return
8214 */
8215HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8216 const Guid *puuidRegistry)
8217{
8218 // copy name, description, OS type, teleporter, UTC etc.
8219 mUserData->s = config.machineUserData;
8220
8221 // look up the object by Id to check it is valid
8222 ComPtr<IGuestOSType> guestOSType;
8223 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8224 guestOSType.asOutParam());
8225 if (FAILED(rc)) return rc;
8226
8227 // stateFile (optional)
8228 if (config.strStateFile.isEmpty())
8229 mSSData->strStateFilePath.setNull();
8230 else
8231 {
8232 Utf8Str stateFilePathFull(config.strStateFile);
8233 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8234 if (RT_FAILURE(vrc))
8235 return setError(E_FAIL,
8236 tr("Invalid saved state file path '%s' (%Rrc)"),
8237 config.strStateFile.c_str(),
8238 vrc);
8239 mSSData->strStateFilePath = stateFilePathFull;
8240 }
8241
8242 // snapshot folder needs special processing so set it again
8243 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8244 if (FAILED(rc)) return rc;
8245
8246 /* Copy the extra data items (Not in any case config is already the same as
8247 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8248 * make sure the extra data map is copied). */
8249 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8250
8251 /* currentStateModified (optional, default is true) */
8252 mData->mCurrentStateModified = config.fCurrentStateModified;
8253
8254 mData->mLastStateChange = config.timeLastStateChange;
8255
8256 /*
8257 * note: all mUserData members must be assigned prior this point because
8258 * we need to commit changes in order to let mUserData be shared by all
8259 * snapshot machine instances.
8260 */
8261 mUserData.commitCopy();
8262
8263 // machine registry, if present (must be loaded before snapshots)
8264 if (config.canHaveOwnMediaRegistry())
8265 {
8266 // determine machine folder
8267 Utf8Str strMachineFolder = getSettingsFileFull();
8268 strMachineFolder.stripFilename();
8269 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8270 config.mediaRegistry,
8271 strMachineFolder);
8272 if (FAILED(rc)) return rc;
8273 }
8274
8275 /* Snapshot node (optional) */
8276 size_t cRootSnapshots;
8277 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8278 {
8279 // there must be only one root snapshot
8280 Assert(cRootSnapshots == 1);
8281
8282 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8283
8284 rc = loadSnapshot(snap,
8285 config.uuidCurrentSnapshot,
8286 NULL); // no parent == first snapshot
8287 if (FAILED(rc)) return rc;
8288 }
8289
8290 // hardware data
8291 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8292 if (FAILED(rc)) return rc;
8293
8294 // load storage controllers
8295 rc = loadStorageControllers(config.storageMachine,
8296 puuidRegistry,
8297 NULL /* puuidSnapshot */);
8298 if (FAILED(rc)) return rc;
8299
8300 /*
8301 * NOTE: the assignment below must be the last thing to do,
8302 * otherwise it will be not possible to change the settings
8303 * somewhere in the code above because all setters will be
8304 * blocked by checkStateDependency(MutableStateDep).
8305 */
8306
8307 /* set the machine state to Aborted or Saved when appropriate */
8308 if (config.fAborted)
8309 {
8310 mSSData->strStateFilePath.setNull();
8311
8312 /* no need to use setMachineState() during init() */
8313 mData->mMachineState = MachineState_Aborted;
8314 }
8315 else if (!mSSData->strStateFilePath.isEmpty())
8316 {
8317 /* no need to use setMachineState() during init() */
8318 mData->mMachineState = MachineState_Saved;
8319 }
8320
8321 // after loading settings, we are no longer different from the XML on disk
8322 mData->flModifications = 0;
8323
8324 return S_OK;
8325}
8326
8327/**
8328 * Recursively loads all snapshots starting from the given.
8329 *
8330 * @param aNode <Snapshot> node.
8331 * @param aCurSnapshotId Current snapshot ID from the settings file.
8332 * @param aParentSnapshot Parent snapshot.
8333 */
8334HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8335 const Guid &aCurSnapshotId,
8336 Snapshot *aParentSnapshot)
8337{
8338 AssertReturn(!isSnapshotMachine(), E_FAIL);
8339 AssertReturn(!isSessionMachine(), E_FAIL);
8340
8341 HRESULT rc = S_OK;
8342
8343 Utf8Str strStateFile;
8344 if (!data.strStateFile.isEmpty())
8345 {
8346 /* optional */
8347 strStateFile = data.strStateFile;
8348 int vrc = calculateFullPath(strStateFile, strStateFile);
8349 if (RT_FAILURE(vrc))
8350 return setError(E_FAIL,
8351 tr("Invalid saved state file path '%s' (%Rrc)"),
8352 strStateFile.c_str(),
8353 vrc);
8354 }
8355
8356 /* create a snapshot machine object */
8357 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8358 pSnapshotMachine.createObject();
8359 rc = pSnapshotMachine->initFromSettings(this,
8360 data.hardware,
8361 &data.debugging,
8362 &data.autostart,
8363 data.storage,
8364 data.uuid.ref(),
8365 strStateFile);
8366 if (FAILED(rc)) return rc;
8367
8368 /* create a snapshot object */
8369 ComObjPtr<Snapshot> pSnapshot;
8370 pSnapshot.createObject();
8371 /* initialize the snapshot */
8372 rc = pSnapshot->init(mParent, // VirtualBox object
8373 data.uuid,
8374 data.strName,
8375 data.strDescription,
8376 data.timestamp,
8377 pSnapshotMachine,
8378 aParentSnapshot);
8379 if (FAILED(rc)) return rc;
8380
8381 /* memorize the first snapshot if necessary */
8382 if (!mData->mFirstSnapshot)
8383 mData->mFirstSnapshot = pSnapshot;
8384
8385 /* memorize the current snapshot when appropriate */
8386 if ( !mData->mCurrentSnapshot
8387 && pSnapshot->getId() == aCurSnapshotId
8388 )
8389 mData->mCurrentSnapshot = pSnapshot;
8390
8391 // now create the children
8392 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8393 it != data.llChildSnapshots.end();
8394 ++it)
8395 {
8396 const settings::Snapshot &childData = *it;
8397 // recurse
8398 rc = loadSnapshot(childData,
8399 aCurSnapshotId,
8400 pSnapshot); // parent = the one we created above
8401 if (FAILED(rc)) return rc;
8402 }
8403
8404 return rc;
8405}
8406
8407/**
8408 * Loads settings into mHWData.
8409 *
8410 * @param data Reference to the hardware settings.
8411 * @param pDbg Pointer to the debugging settings.
8412 * @param pAutostart Pointer to the autostart settings.
8413 */
8414HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8415 const settings::Autostart *pAutostart)
8416{
8417 AssertReturn(!isSessionMachine(), E_FAIL);
8418
8419 HRESULT rc = S_OK;
8420
8421 try
8422 {
8423 /* The hardware version attribute (optional). */
8424 mHWData->mHWVersion = data.strVersion;
8425 mHWData->mHardwareUUID = data.uuid;
8426
8427 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8428 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8429 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8430 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8431 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8432 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8433 mHWData->mPAEEnabled = data.fPAE;
8434 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8435
8436 mHWData->mCPUCount = data.cCPUs;
8437 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8438 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8439
8440 // cpu
8441 if (mHWData->mCPUHotPlugEnabled)
8442 {
8443 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8444 it != data.llCpus.end();
8445 ++it)
8446 {
8447 const settings::Cpu &cpu = *it;
8448
8449 mHWData->mCPUAttached[cpu.ulId] = true;
8450 }
8451 }
8452
8453 // cpuid leafs
8454 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8455 it != data.llCpuIdLeafs.end();
8456 ++it)
8457 {
8458 const settings::CpuIdLeaf &leaf = *it;
8459
8460 switch (leaf.ulId)
8461 {
8462 case 0x0:
8463 case 0x1:
8464 case 0x2:
8465 case 0x3:
8466 case 0x4:
8467 case 0x5:
8468 case 0x6:
8469 case 0x7:
8470 case 0x8:
8471 case 0x9:
8472 case 0xA:
8473 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8474 break;
8475
8476 case 0x80000000:
8477 case 0x80000001:
8478 case 0x80000002:
8479 case 0x80000003:
8480 case 0x80000004:
8481 case 0x80000005:
8482 case 0x80000006:
8483 case 0x80000007:
8484 case 0x80000008:
8485 case 0x80000009:
8486 case 0x8000000A:
8487 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8488 break;
8489
8490 default:
8491 /* just ignore */
8492 break;
8493 }
8494 }
8495
8496 mHWData->mMemorySize = data.ulMemorySizeMB;
8497 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8498
8499 // boot order
8500 for (size_t i = 0;
8501 i < RT_ELEMENTS(mHWData->mBootOrder);
8502 i++)
8503 {
8504 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8505 if (it == data.mapBootOrder.end())
8506 mHWData->mBootOrder[i] = DeviceType_Null;
8507 else
8508 mHWData->mBootOrder[i] = it->second;
8509 }
8510
8511 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8512 mHWData->mMonitorCount = data.cMonitors;
8513 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8514 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8515 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8516 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8517 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8518 mHWData->mVideoCaptureFile = data.strVideoCaptureFile;
8519 mHWData->mFirmwareType = data.firmwareType;
8520 mHWData->mPointingHIDType = data.pointingHIDType;
8521 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8522 mHWData->mChipsetType = data.chipsetType;
8523 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8524 mHWData->mHPETEnabled = data.fHPETEnabled;
8525
8526 /* VRDEServer */
8527 rc = mVRDEServer->loadSettings(data.vrdeSettings);
8528 if (FAILED(rc)) return rc;
8529
8530 /* BIOS */
8531 rc = mBIOSSettings->loadSettings(data.biosSettings);
8532 if (FAILED(rc)) return rc;
8533
8534 // Bandwidth control (must come before network adapters)
8535 rc = mBandwidthControl->loadSettings(data.ioSettings);
8536 if (FAILED(rc)) return rc;
8537
8538 /* USB Controller */
8539 rc = mUSBController->loadSettings(data.usbController);
8540 if (FAILED(rc)) return rc;
8541
8542 // network adapters
8543 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8544 uint32_t oldCount = mNetworkAdapters.size();
8545 if (newCount > oldCount)
8546 {
8547 mNetworkAdapters.resize(newCount);
8548 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
8549 {
8550 unconst(mNetworkAdapters[slot]).createObject();
8551 mNetworkAdapters[slot]->init(this, slot);
8552 }
8553 }
8554 else if (newCount < oldCount)
8555 mNetworkAdapters.resize(newCount);
8556 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8557 it != data.llNetworkAdapters.end();
8558 ++it)
8559 {
8560 const settings::NetworkAdapter &nic = *it;
8561
8562 /* slot unicity is guaranteed by XML Schema */
8563 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8564 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
8565 if (FAILED(rc)) return rc;
8566 }
8567
8568 // serial ports
8569 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8570 it != data.llSerialPorts.end();
8571 ++it)
8572 {
8573 const settings::SerialPort &s = *it;
8574
8575 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8576 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
8577 if (FAILED(rc)) return rc;
8578 }
8579
8580 // parallel ports (optional)
8581 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8582 it != data.llParallelPorts.end();
8583 ++it)
8584 {
8585 const settings::ParallelPort &p = *it;
8586
8587 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8588 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
8589 if (FAILED(rc)) return rc;
8590 }
8591
8592 /* AudioAdapter */
8593 rc = mAudioAdapter->loadSettings(data.audioAdapter);
8594 if (FAILED(rc)) return rc;
8595
8596 /* Shared folders */
8597 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8598 it != data.llSharedFolders.end();
8599 ++it)
8600 {
8601 const settings::SharedFolder &sf = *it;
8602
8603 ComObjPtr<SharedFolder> sharedFolder;
8604 /* Check for double entries. Not allowed! */
8605 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8606 if (SUCCEEDED(rc))
8607 return setError(VBOX_E_OBJECT_IN_USE,
8608 tr("Shared folder named '%s' already exists"),
8609 sf.strName.c_str());
8610
8611 /* Create the new shared folder. Don't break on error. This will be
8612 * reported when the machine starts. */
8613 sharedFolder.createObject();
8614 rc = sharedFolder->init(getMachine(),
8615 sf.strName,
8616 sf.strHostPath,
8617 RT_BOOL(sf.fWritable),
8618 RT_BOOL(sf.fAutoMount),
8619 false /* fFailOnError */);
8620 if (FAILED(rc)) return rc;
8621 mHWData->mSharedFolders.push_back(sharedFolder);
8622 }
8623
8624 // Clipboard
8625 mHWData->mClipboardMode = data.clipboardMode;
8626
8627 // drag'n'drop
8628 mHWData->mDragAndDropMode = data.dragAndDropMode;
8629
8630 // guest settings
8631 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8632
8633 // IO settings
8634 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8635 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8636
8637 // Host PCI devices
8638 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8639 it != data.pciAttachments.end();
8640 ++it)
8641 {
8642 const settings::HostPCIDeviceAttachment &hpda = *it;
8643 ComObjPtr<PCIDeviceAttachment> pda;
8644
8645 pda.createObject();
8646 pda->loadSettings(this, hpda);
8647 mHWData->mPCIDeviceAssignments.push_back(pda);
8648 }
8649
8650 /*
8651 * (The following isn't really real hardware, but it lives in HWData
8652 * for reasons of convenience.)
8653 */
8654
8655#ifdef VBOX_WITH_GUEST_PROPS
8656 /* Guest properties (optional) */
8657 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8658 it != data.llGuestProperties.end();
8659 ++it)
8660 {
8661 const settings::GuestProperty &prop = *it;
8662 uint32_t fFlags = guestProp::NILFLAG;
8663 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8664 HWData::GuestProperty property = { prop.strName, prop.strValue, (LONG64) prop.timestamp, fFlags };
8665 mHWData->mGuestProperties.push_back(property);
8666 }
8667
8668 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8669#endif /* VBOX_WITH_GUEST_PROPS defined */
8670
8671 rc = loadDebugging(pDbg);
8672 if (FAILED(rc))
8673 return rc;
8674
8675 mHWData->mAutostart = *pAutostart;
8676 }
8677 catch(std::bad_alloc &)
8678 {
8679 return E_OUTOFMEMORY;
8680 }
8681
8682 AssertComRC(rc);
8683 return rc;
8684}
8685
8686/**
8687 * Called from Machine::loadHardware() to load the debugging settings of the
8688 * machine.
8689 *
8690 * @param pDbg Pointer to the settings.
8691 */
8692HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
8693{
8694 mHWData->mDebugging = *pDbg;
8695 /* no more processing currently required, this will probably change. */
8696 return S_OK;
8697}
8698
8699/**
8700 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
8701 *
8702 * @param data
8703 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8704 * @param puuidSnapshot
8705 * @return
8706 */
8707HRESULT Machine::loadStorageControllers(const settings::Storage &data,
8708 const Guid *puuidRegistry,
8709 const Guid *puuidSnapshot)
8710{
8711 AssertReturn(!isSessionMachine(), E_FAIL);
8712
8713 HRESULT rc = S_OK;
8714
8715 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8716 it != data.llStorageControllers.end();
8717 ++it)
8718 {
8719 const settings::StorageController &ctlData = *it;
8720
8721 ComObjPtr<StorageController> pCtl;
8722 /* Try to find one with the name first. */
8723 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8724 if (SUCCEEDED(rc))
8725 return setError(VBOX_E_OBJECT_IN_USE,
8726 tr("Storage controller named '%s' already exists"),
8727 ctlData.strName.c_str());
8728
8729 pCtl.createObject();
8730 rc = pCtl->init(this,
8731 ctlData.strName,
8732 ctlData.storageBus,
8733 ctlData.ulInstance,
8734 ctlData.fBootable);
8735 if (FAILED(rc)) return rc;
8736
8737 mStorageControllers->push_back(pCtl);
8738
8739 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8740 if (FAILED(rc)) return rc;
8741
8742 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8743 if (FAILED(rc)) return rc;
8744
8745 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8746 if (FAILED(rc)) return rc;
8747
8748 /* Set IDE emulation settings (only for AHCI controller). */
8749 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8750 {
8751 if ( (FAILED(rc = pCtl->SetIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8752 || (FAILED(rc = pCtl->SetIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8753 || (FAILED(rc = pCtl->SetIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8754 || (FAILED(rc = pCtl->SetIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8755 )
8756 return rc;
8757 }
8758
8759 /* Load the attached devices now. */
8760 rc = loadStorageDevices(pCtl,
8761 ctlData,
8762 puuidRegistry,
8763 puuidSnapshot);
8764 if (FAILED(rc)) return rc;
8765 }
8766
8767 return S_OK;
8768}
8769
8770/**
8771 * Called from loadStorageControllers for a controller's devices.
8772 *
8773 * @param aStorageController
8774 * @param data
8775 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8776 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
8777 * @return
8778 */
8779HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
8780 const settings::StorageController &data,
8781 const Guid *puuidRegistry,
8782 const Guid *puuidSnapshot)
8783{
8784 HRESULT rc = S_OK;
8785
8786 /* paranoia: detect duplicate attachments */
8787 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8788 it != data.llAttachedDevices.end();
8789 ++it)
8790 {
8791 const settings::AttachedDevice &ad = *it;
8792
8793 for (settings::AttachedDevicesList::const_iterator it2 = it;
8794 it2 != data.llAttachedDevices.end();
8795 ++it2)
8796 {
8797 if (it == it2)
8798 continue;
8799
8800 const settings::AttachedDevice &ad2 = *it2;
8801
8802 if ( ad.lPort == ad2.lPort
8803 && ad.lDevice == ad2.lDevice)
8804 {
8805 return setError(E_FAIL,
8806 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
8807 aStorageController->getName().c_str(),
8808 ad.lPort,
8809 ad.lDevice,
8810 mUserData->s.strName.c_str());
8811 }
8812 }
8813 }
8814
8815 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8816 it != data.llAttachedDevices.end();
8817 ++it)
8818 {
8819 const settings::AttachedDevice &dev = *it;
8820 ComObjPtr<Medium> medium;
8821
8822 switch (dev.deviceType)
8823 {
8824 case DeviceType_Floppy:
8825 case DeviceType_DVD:
8826 if (dev.strHostDriveSrc.isNotEmpty())
8827 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
8828 else
8829 rc = mParent->findRemoveableMedium(dev.deviceType,
8830 dev.uuid,
8831 false /* fRefresh */,
8832 false /* aSetError */,
8833 medium);
8834 if (rc == VBOX_E_OBJECT_NOT_FOUND)
8835 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
8836 rc = S_OK;
8837 break;
8838
8839 case DeviceType_HardDisk:
8840 {
8841 /* find a hard disk by UUID */
8842 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
8843 if (FAILED(rc))
8844 {
8845 if (isSnapshotMachine())
8846 {
8847 // wrap another error message around the "cannot find hard disk" set by findHardDisk
8848 // so the user knows that the bad disk is in a snapshot somewhere
8849 com::ErrorInfo info;
8850 return setError(E_FAIL,
8851 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
8852 puuidSnapshot->raw(),
8853 info.getText().raw());
8854 }
8855 else
8856 return rc;
8857 }
8858
8859 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
8860
8861 if (medium->getType() == MediumType_Immutable)
8862 {
8863 if (isSnapshotMachine())
8864 return setError(E_FAIL,
8865 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8866 "of the virtual machine '%s' ('%s')"),
8867 medium->getLocationFull().c_str(),
8868 dev.uuid.raw(),
8869 puuidSnapshot->raw(),
8870 mUserData->s.strName.c_str(),
8871 mData->m_strConfigFileFull.c_str());
8872
8873 return setError(E_FAIL,
8874 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8875 medium->getLocationFull().c_str(),
8876 dev.uuid.raw(),
8877 mUserData->s.strName.c_str(),
8878 mData->m_strConfigFileFull.c_str());
8879 }
8880
8881 if (medium->getType() == MediumType_MultiAttach)
8882 {
8883 if (isSnapshotMachine())
8884 return setError(E_FAIL,
8885 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8886 "of the virtual machine '%s' ('%s')"),
8887 medium->getLocationFull().c_str(),
8888 dev.uuid.raw(),
8889 puuidSnapshot->raw(),
8890 mUserData->s.strName.c_str(),
8891 mData->m_strConfigFileFull.c_str());
8892
8893 return setError(E_FAIL,
8894 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8895 medium->getLocationFull().c_str(),
8896 dev.uuid.raw(),
8897 mUserData->s.strName.c_str(),
8898 mData->m_strConfigFileFull.c_str());
8899 }
8900
8901 if ( !isSnapshotMachine()
8902 && medium->getChildren().size() != 0
8903 )
8904 return setError(E_FAIL,
8905 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
8906 "because it has %d differencing child hard disks"),
8907 medium->getLocationFull().c_str(),
8908 dev.uuid.raw(),
8909 mUserData->s.strName.c_str(),
8910 mData->m_strConfigFileFull.c_str(),
8911 medium->getChildren().size());
8912
8913 if (findAttachment(mMediaData->mAttachments,
8914 medium))
8915 return setError(E_FAIL,
8916 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
8917 medium->getLocationFull().c_str(),
8918 dev.uuid.raw(),
8919 mUserData->s.strName.c_str(),
8920 mData->m_strConfigFileFull.c_str());
8921
8922 break;
8923 }
8924
8925 default:
8926 return setError(E_FAIL,
8927 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
8928 medium->getLocationFull().c_str(),
8929 mUserData->s.strName.c_str(),
8930 mData->m_strConfigFileFull.c_str());
8931 }
8932
8933 if (FAILED(rc))
8934 break;
8935
8936 /* Bandwidth groups are loaded at this point. */
8937 ComObjPtr<BandwidthGroup> pBwGroup;
8938
8939 if (!dev.strBwGroup.isEmpty())
8940 {
8941 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
8942 if (FAILED(rc))
8943 return setError(E_FAIL,
8944 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
8945 medium->getLocationFull().c_str(),
8946 dev.strBwGroup.c_str(),
8947 mUserData->s.strName.c_str(),
8948 mData->m_strConfigFileFull.c_str());
8949 pBwGroup->reference();
8950 }
8951
8952 const Bstr controllerName = aStorageController->getName();
8953 ComObjPtr<MediumAttachment> pAttachment;
8954 pAttachment.createObject();
8955 rc = pAttachment->init(this,
8956 medium,
8957 controllerName,
8958 dev.lPort,
8959 dev.lDevice,
8960 dev.deviceType,
8961 false,
8962 dev.fPassThrough,
8963 dev.fTempEject,
8964 dev.fNonRotational,
8965 dev.fDiscard,
8966 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
8967 if (FAILED(rc)) break;
8968
8969 /* associate the medium with this machine and snapshot */
8970 if (!medium.isNull())
8971 {
8972 AutoCaller medCaller(medium);
8973 if (FAILED(medCaller.rc())) return medCaller.rc();
8974 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
8975
8976 if (isSnapshotMachine())
8977 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
8978 else
8979 rc = medium->addBackReference(mData->mUuid);
8980 /* If the medium->addBackReference fails it sets an appropriate
8981 * error message, so no need to do any guesswork here. */
8982
8983 if (puuidRegistry)
8984 // caller wants registry ID to be set on all attached media (OVF import case)
8985 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
8986 }
8987
8988 if (FAILED(rc))
8989 break;
8990
8991 /* back up mMediaData to let registeredInit() properly rollback on failure
8992 * (= limited accessibility) */
8993 setModified(IsModified_Storage);
8994 mMediaData.backup();
8995 mMediaData->mAttachments.push_back(pAttachment);
8996 }
8997
8998 return rc;
8999}
9000
9001/**
9002 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9003 *
9004 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9005 * @param aSnapshot where to return the found snapshot
9006 * @param aSetError true to set extended error info on failure
9007 */
9008HRESULT Machine::findSnapshotById(const Guid &aId,
9009 ComObjPtr<Snapshot> &aSnapshot,
9010 bool aSetError /* = false */)
9011{
9012 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9013
9014 if (!mData->mFirstSnapshot)
9015 {
9016 if (aSetError)
9017 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9018 return E_FAIL;
9019 }
9020
9021 if (aId.isEmpty())
9022 aSnapshot = mData->mFirstSnapshot;
9023 else
9024 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9025
9026 if (!aSnapshot)
9027 {
9028 if (aSetError)
9029 return setError(E_FAIL,
9030 tr("Could not find a snapshot with UUID {%s}"),
9031 aId.toString().c_str());
9032 return E_FAIL;
9033 }
9034
9035 return S_OK;
9036}
9037
9038/**
9039 * Returns the snapshot with the given name or fails of no such snapshot.
9040 *
9041 * @param aName snapshot name to find
9042 * @param aSnapshot where to return the found snapshot
9043 * @param aSetError true to set extended error info on failure
9044 */
9045HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9046 ComObjPtr<Snapshot> &aSnapshot,
9047 bool aSetError /* = false */)
9048{
9049 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9050
9051 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9052
9053 if (!mData->mFirstSnapshot)
9054 {
9055 if (aSetError)
9056 return setError(VBOX_E_OBJECT_NOT_FOUND,
9057 tr("This machine does not have any snapshots"));
9058 return VBOX_E_OBJECT_NOT_FOUND;
9059 }
9060
9061 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9062
9063 if (!aSnapshot)
9064 {
9065 if (aSetError)
9066 return setError(VBOX_E_OBJECT_NOT_FOUND,
9067 tr("Could not find a snapshot named '%s'"), strName.c_str());
9068 return VBOX_E_OBJECT_NOT_FOUND;
9069 }
9070
9071 return S_OK;
9072}
9073
9074/**
9075 * Returns a storage controller object with the given name.
9076 *
9077 * @param aName storage controller name to find
9078 * @param aStorageController where to return the found storage controller
9079 * @param aSetError true to set extended error info on failure
9080 */
9081HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9082 ComObjPtr<StorageController> &aStorageController,
9083 bool aSetError /* = false */)
9084{
9085 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9086
9087 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9088 it != mStorageControllers->end();
9089 ++it)
9090 {
9091 if ((*it)->getName() == aName)
9092 {
9093 aStorageController = (*it);
9094 return S_OK;
9095 }
9096 }
9097
9098 if (aSetError)
9099 return setError(VBOX_E_OBJECT_NOT_FOUND,
9100 tr("Could not find a storage controller named '%s'"),
9101 aName.c_str());
9102 return VBOX_E_OBJECT_NOT_FOUND;
9103}
9104
9105HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9106 MediaData::AttachmentList &atts)
9107{
9108 AutoCaller autoCaller(this);
9109 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9110
9111 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9112
9113 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9114 it != mMediaData->mAttachments.end();
9115 ++it)
9116 {
9117 const ComObjPtr<MediumAttachment> &pAtt = *it;
9118
9119 // should never happen, but deal with NULL pointers in the list.
9120 AssertStmt(!pAtt.isNull(), continue);
9121
9122 // getControllerName() needs caller+read lock
9123 AutoCaller autoAttCaller(pAtt);
9124 if (FAILED(autoAttCaller.rc()))
9125 {
9126 atts.clear();
9127 return autoAttCaller.rc();
9128 }
9129 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9130
9131 if (pAtt->getControllerName() == aName)
9132 atts.push_back(pAtt);
9133 }
9134
9135 return S_OK;
9136}
9137
9138/**
9139 * Helper for #saveSettings. Cares about renaming the settings directory and
9140 * file if the machine name was changed and about creating a new settings file
9141 * if this is a new machine.
9142 *
9143 * @note Must be never called directly but only from #saveSettings().
9144 */
9145HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9146{
9147 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9148
9149 HRESULT rc = S_OK;
9150
9151 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9152
9153 /// @todo need to handle primary group change, too
9154
9155 /* attempt to rename the settings file if machine name is changed */
9156 if ( mUserData->s.fNameSync
9157 && mUserData.isBackedUp()
9158 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9159 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9160 )
9161 {
9162 bool dirRenamed = false;
9163 bool fileRenamed = false;
9164
9165 Utf8Str configFile, newConfigFile;
9166 Utf8Str configFilePrev, newConfigFilePrev;
9167 Utf8Str configDir, newConfigDir;
9168
9169 do
9170 {
9171 int vrc = VINF_SUCCESS;
9172
9173 Utf8Str name = mUserData.backedUpData()->s.strName;
9174 Utf8Str newName = mUserData->s.strName;
9175 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9176 if (group == "/")
9177 group.setNull();
9178 Utf8Str newGroup = mUserData->s.llGroups.front();
9179 if (newGroup == "/")
9180 newGroup.setNull();
9181
9182 configFile = mData->m_strConfigFileFull;
9183
9184 /* first, rename the directory if it matches the group and machine name */
9185 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9186 group.c_str(), RTPATH_DELIMITER, name.c_str());
9187 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9188 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9189 configDir = configFile;
9190 configDir.stripFilename();
9191 newConfigDir = configDir;
9192 if ( configDir.length() >= groupPlusName.length()
9193 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9194 {
9195 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9196 Utf8Str newConfigBaseDir(newConfigDir);
9197 newConfigDir.append(newGroupPlusName);
9198 /* consistency: use \ if appropriate on the platform */
9199 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9200 /* new dir and old dir cannot be equal here because of 'if'
9201 * above and because name != newName */
9202 Assert(configDir != newConfigDir);
9203 if (!fSettingsFileIsNew)
9204 {
9205 /* perform real rename only if the machine is not new */
9206 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9207 if ( vrc == VERR_FILE_NOT_FOUND
9208 || vrc == VERR_PATH_NOT_FOUND)
9209 {
9210 /* create the parent directory, then retry renaming */
9211 Utf8Str parent(newConfigDir);
9212 parent.stripFilename();
9213 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9214 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9215 }
9216 if (RT_FAILURE(vrc))
9217 {
9218 rc = setError(E_FAIL,
9219 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9220 configDir.c_str(),
9221 newConfigDir.c_str(),
9222 vrc);
9223 break;
9224 }
9225 /* delete subdirectories which are no longer needed */
9226 Utf8Str dir(configDir);
9227 dir.stripFilename();
9228 while (dir != newConfigBaseDir && dir != ".")
9229 {
9230 vrc = RTDirRemove(dir.c_str());
9231 if (RT_FAILURE(vrc))
9232 break;
9233 dir.stripFilename();
9234 }
9235 dirRenamed = true;
9236 }
9237 }
9238
9239 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9240 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9241
9242 /* then try to rename the settings file itself */
9243 if (newConfigFile != configFile)
9244 {
9245 /* get the path to old settings file in renamed directory */
9246 configFile = Utf8StrFmt("%s%c%s",
9247 newConfigDir.c_str(),
9248 RTPATH_DELIMITER,
9249 RTPathFilename(configFile.c_str()));
9250 if (!fSettingsFileIsNew)
9251 {
9252 /* perform real rename only if the machine is not new */
9253 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9254 if (RT_FAILURE(vrc))
9255 {
9256 rc = setError(E_FAIL,
9257 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9258 configFile.c_str(),
9259 newConfigFile.c_str(),
9260 vrc);
9261 break;
9262 }
9263 fileRenamed = true;
9264 configFilePrev = configFile;
9265 configFilePrev += "-prev";
9266 newConfigFilePrev = newConfigFile;
9267 newConfigFilePrev += "-prev";
9268 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9269 }
9270 }
9271
9272 // update m_strConfigFileFull amd mConfigFile
9273 mData->m_strConfigFileFull = newConfigFile;
9274 // compute the relative path too
9275 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9276
9277 // store the old and new so that VirtualBox::saveSettings() can update
9278 // the media registry
9279 if ( mData->mRegistered
9280 && configDir != newConfigDir)
9281 {
9282 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9283
9284 if (pfNeedsGlobalSaveSettings)
9285 *pfNeedsGlobalSaveSettings = true;
9286 }
9287
9288 // in the saved state file path, replace the old directory with the new directory
9289 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9290 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9291
9292 // and do the same thing for the saved state file paths of all the online snapshots
9293 if (mData->mFirstSnapshot)
9294 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9295 newConfigDir.c_str());
9296 }
9297 while (0);
9298
9299 if (FAILED(rc))
9300 {
9301 /* silently try to rename everything back */
9302 if (fileRenamed)
9303 {
9304 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9305 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9306 }
9307 if (dirRenamed)
9308 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9309 }
9310
9311 if (FAILED(rc)) return rc;
9312 }
9313
9314 if (fSettingsFileIsNew)
9315 {
9316 /* create a virgin config file */
9317 int vrc = VINF_SUCCESS;
9318
9319 /* ensure the settings directory exists */
9320 Utf8Str path(mData->m_strConfigFileFull);
9321 path.stripFilename();
9322 if (!RTDirExists(path.c_str()))
9323 {
9324 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9325 if (RT_FAILURE(vrc))
9326 {
9327 return setError(E_FAIL,
9328 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9329 path.c_str(),
9330 vrc);
9331 }
9332 }
9333
9334 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9335 path = Utf8Str(mData->m_strConfigFileFull);
9336 RTFILE f = NIL_RTFILE;
9337 vrc = RTFileOpen(&f, path.c_str(),
9338 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9339 if (RT_FAILURE(vrc))
9340 return setError(E_FAIL,
9341 tr("Could not create the settings file '%s' (%Rrc)"),
9342 path.c_str(),
9343 vrc);
9344 RTFileClose(f);
9345 }
9346
9347 return rc;
9348}
9349
9350/**
9351 * Saves and commits machine data, user data and hardware data.
9352 *
9353 * Note that on failure, the data remains uncommitted.
9354 *
9355 * @a aFlags may combine the following flags:
9356 *
9357 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9358 * Used when saving settings after an operation that makes them 100%
9359 * correspond to the settings from the current snapshot.
9360 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9361 * #isReallyModified() returns false. This is necessary for cases when we
9362 * change machine data directly, not through the backup()/commit() mechanism.
9363 * - SaveS_Force: settings will be saved without doing a deep compare of the
9364 * settings structures. This is used when this is called because snapshots
9365 * have changed to avoid the overhead of the deep compare.
9366 *
9367 * @note Must be called from under this object's write lock. Locks children for
9368 * writing.
9369 *
9370 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9371 * initialized to false and that will be set to true by this function if
9372 * the caller must invoke VirtualBox::saveSettings() because the global
9373 * settings have changed. This will happen if a machine rename has been
9374 * saved and the global machine and media registries will therefore need
9375 * updating.
9376 */
9377HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9378 int aFlags /*= 0*/)
9379{
9380 LogFlowThisFuncEnter();
9381
9382 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9383
9384 /* make sure child objects are unable to modify the settings while we are
9385 * saving them */
9386 ensureNoStateDependencies();
9387
9388 AssertReturn(!isSnapshotMachine(),
9389 E_FAIL);
9390
9391 HRESULT rc = S_OK;
9392 bool fNeedsWrite = false;
9393
9394 /* First, prepare to save settings. It will care about renaming the
9395 * settings directory and file if the machine name was changed and about
9396 * creating a new settings file if this is a new machine. */
9397 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9398 if (FAILED(rc)) return rc;
9399
9400 // keep a pointer to the current settings structures
9401 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9402 settings::MachineConfigFile *pNewConfig = NULL;
9403
9404 try
9405 {
9406 // make a fresh one to have everyone write stuff into
9407 pNewConfig = new settings::MachineConfigFile(NULL);
9408 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9409
9410 // now go and copy all the settings data from COM to the settings structures
9411 // (this calles saveSettings() on all the COM objects in the machine)
9412 copyMachineDataToSettings(*pNewConfig);
9413
9414 if (aFlags & SaveS_ResetCurStateModified)
9415 {
9416 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9417 mData->mCurrentStateModified = FALSE;
9418 fNeedsWrite = true; // always, no need to compare
9419 }
9420 else if (aFlags & SaveS_Force)
9421 {
9422 fNeedsWrite = true; // always, no need to compare
9423 }
9424 else
9425 {
9426 if (!mData->mCurrentStateModified)
9427 {
9428 // do a deep compare of the settings that we just saved with the settings
9429 // previously stored in the config file; this invokes MachineConfigFile::operator==
9430 // which does a deep compare of all the settings, which is expensive but less expensive
9431 // than writing out XML in vain
9432 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9433
9434 // could still be modified if any settings changed
9435 mData->mCurrentStateModified = fAnySettingsChanged;
9436
9437 fNeedsWrite = fAnySettingsChanged;
9438 }
9439 else
9440 fNeedsWrite = true;
9441 }
9442
9443 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9444
9445 if (fNeedsWrite)
9446 // now spit it all out!
9447 pNewConfig->write(mData->m_strConfigFileFull);
9448
9449 mData->pMachineConfigFile = pNewConfig;
9450 delete pOldConfig;
9451 commit();
9452
9453 // after saving settings, we are no longer different from the XML on disk
9454 mData->flModifications = 0;
9455 }
9456 catch (HRESULT err)
9457 {
9458 // we assume that error info is set by the thrower
9459 rc = err;
9460
9461 // restore old config
9462 delete pNewConfig;
9463 mData->pMachineConfigFile = pOldConfig;
9464 }
9465 catch (...)
9466 {
9467 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9468 }
9469
9470 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9471 {
9472 /* Fire the data change event, even on failure (since we've already
9473 * committed all data). This is done only for SessionMachines because
9474 * mutable Machine instances are always not registered (i.e. private
9475 * to the client process that creates them) and thus don't need to
9476 * inform callbacks. */
9477 if (isSessionMachine())
9478 mParent->onMachineDataChange(mData->mUuid);
9479 }
9480
9481 LogFlowThisFunc(("rc=%08X\n", rc));
9482 LogFlowThisFuncLeave();
9483 return rc;
9484}
9485
9486/**
9487 * Implementation for saving the machine settings into the given
9488 * settings::MachineConfigFile instance. This copies machine extradata
9489 * from the previous machine config file in the instance data, if any.
9490 *
9491 * This gets called from two locations:
9492 *
9493 * -- Machine::saveSettings(), during the regular XML writing;
9494 *
9495 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9496 * exported to OVF and we write the VirtualBox proprietary XML
9497 * into a <vbox:Machine> tag.
9498 *
9499 * This routine fills all the fields in there, including snapshots, *except*
9500 * for the following:
9501 *
9502 * -- fCurrentStateModified. There is some special logic associated with that.
9503 *
9504 * The caller can then call MachineConfigFile::write() or do something else
9505 * with it.
9506 *
9507 * Caller must hold the machine lock!
9508 *
9509 * This throws XML errors and HRESULT, so the caller must have a catch block!
9510 */
9511void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
9512{
9513 // deep copy extradata
9514 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9515
9516 config.uuid = mData->mUuid;
9517
9518 // copy name, description, OS type, teleport, UTC etc.
9519 config.machineUserData = mUserData->s;
9520
9521 if ( mData->mMachineState == MachineState_Saved
9522 || mData->mMachineState == MachineState_Restoring
9523 // when deleting a snapshot we may or may not have a saved state in the current state,
9524 // so let's not assert here please
9525 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9526 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9527 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9528 && (!mSSData->strStateFilePath.isEmpty())
9529 )
9530 )
9531 {
9532 Assert(!mSSData->strStateFilePath.isEmpty());
9533 /* try to make the file name relative to the settings file dir */
9534 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9535 }
9536 else
9537 {
9538 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9539 config.strStateFile.setNull();
9540 }
9541
9542 if (mData->mCurrentSnapshot)
9543 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
9544 else
9545 config.uuidCurrentSnapshot.clear();
9546
9547 config.timeLastStateChange = mData->mLastStateChange;
9548 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9549 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9550
9551 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9552 if (FAILED(rc)) throw rc;
9553
9554 rc = saveStorageControllers(config.storageMachine);
9555 if (FAILED(rc)) throw rc;
9556
9557 // save machine's media registry if this is VirtualBox 4.0 or later
9558 if (config.canHaveOwnMediaRegistry())
9559 {
9560 // determine machine folder
9561 Utf8Str strMachineFolder = getSettingsFileFull();
9562 strMachineFolder.stripFilename();
9563 mParent->saveMediaRegistry(config.mediaRegistry,
9564 getId(), // only media with registry ID == machine UUID
9565 strMachineFolder);
9566 // this throws HRESULT
9567 }
9568
9569 // save snapshots
9570 rc = saveAllSnapshots(config);
9571 if (FAILED(rc)) throw rc;
9572}
9573
9574/**
9575 * Saves all snapshots of the machine into the given machine config file. Called
9576 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9577 * @param config
9578 * @return
9579 */
9580HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
9581{
9582 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9583
9584 HRESULT rc = S_OK;
9585
9586 try
9587 {
9588 config.llFirstSnapshot.clear();
9589
9590 if (mData->mFirstSnapshot)
9591 {
9592 settings::Snapshot snapNew;
9593 config.llFirstSnapshot.push_back(snapNew);
9594
9595 // get reference to the fresh copy of the snapshot on the list and
9596 // work on that copy directly to avoid excessive copying later
9597 settings::Snapshot &snap = config.llFirstSnapshot.front();
9598
9599 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
9600 if (FAILED(rc)) throw rc;
9601 }
9602
9603// if (mType == IsSessionMachine)
9604// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9605
9606 }
9607 catch (HRESULT err)
9608 {
9609 /* we assume that error info is set by the thrower */
9610 rc = err;
9611 }
9612 catch (...)
9613 {
9614 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9615 }
9616
9617 return rc;
9618}
9619
9620/**
9621 * Saves the VM hardware configuration. It is assumed that the
9622 * given node is empty.
9623 *
9624 * @param data Reference to the settings object for the hardware config.
9625 * @param pDbg Pointer to the settings object for the debugging config
9626 * which happens to live in mHWData.
9627 * @param pAutostart Pointer to the settings object for the autostart config
9628 * which happens to live in mHWData.
9629 */
9630HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9631 settings::Autostart *pAutostart)
9632{
9633 HRESULT rc = S_OK;
9634
9635 try
9636 {
9637 /* The hardware version attribute (optional).
9638 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9639 if ( mHWData->mHWVersion == "1"
9640 && mSSData->strStateFilePath.isEmpty()
9641 )
9642 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. */
9643
9644 data.strVersion = mHWData->mHWVersion;
9645 data.uuid = mHWData->mHardwareUUID;
9646
9647 // CPU
9648 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9649 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
9650 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9651 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9652 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9653 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9654 data.fPAE = !!mHWData->mPAEEnabled;
9655 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9656
9657 /* Standard and Extended CPUID leafs. */
9658 data.llCpuIdLeafs.clear();
9659 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
9660 {
9661 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9662 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9663 }
9664 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
9665 {
9666 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9667 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9668 }
9669
9670 data.cCPUs = mHWData->mCPUCount;
9671 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9672 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9673
9674 data.llCpus.clear();
9675 if (data.fCpuHotPlug)
9676 {
9677 for (unsigned idx = 0; idx < data.cCPUs; idx++)
9678 {
9679 if (mHWData->mCPUAttached[idx])
9680 {
9681 settings::Cpu cpu;
9682 cpu.ulId = idx;
9683 data.llCpus.push_back(cpu);
9684 }
9685 }
9686 }
9687
9688 // memory
9689 data.ulMemorySizeMB = mHWData->mMemorySize;
9690 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9691
9692 // firmware
9693 data.firmwareType = mHWData->mFirmwareType;
9694
9695 // HID
9696 data.pointingHIDType = mHWData->mPointingHIDType;
9697 data.keyboardHIDType = mHWData->mKeyboardHIDType;
9698
9699 // chipset
9700 data.chipsetType = mHWData->mChipsetType;
9701
9702 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9703
9704 // HPET
9705 data.fHPETEnabled = !!mHWData->mHPETEnabled;
9706
9707 // boot order
9708 data.mapBootOrder.clear();
9709 for (size_t i = 0;
9710 i < RT_ELEMENTS(mHWData->mBootOrder);
9711 ++i)
9712 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9713
9714 // display
9715 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9716 data.cMonitors = mHWData->mMonitorCount;
9717 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9718 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9719 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
9720 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
9721 data.fVideoCaptureEnabled = !! mHWData->mVideoCaptureEnabled;
9722 data.strVideoCaptureFile = mHWData->mVideoCaptureFile;
9723
9724 /* VRDEServer settings (optional) */
9725 rc = mVRDEServer->saveSettings(data.vrdeSettings);
9726 if (FAILED(rc)) throw rc;
9727
9728 /* BIOS (required) */
9729 rc = mBIOSSettings->saveSettings(data.biosSettings);
9730 if (FAILED(rc)) throw rc;
9731
9732 /* USB Controller (required) */
9733 rc = mUSBController->saveSettings(data.usbController);
9734 if (FAILED(rc)) throw rc;
9735
9736 /* Network adapters (required) */
9737 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
9738 data.llNetworkAdapters.clear();
9739 /* Write out only the nominal number of network adapters for this
9740 * chipset type. Since Machine::commit() hasn't been called there
9741 * may be extra NIC settings in the vector. */
9742 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
9743 {
9744 settings::NetworkAdapter nic;
9745 nic.ulSlot = slot;
9746 /* paranoia check... must not be NULL, but must not crash either. */
9747 if (mNetworkAdapters[slot])
9748 {
9749 rc = mNetworkAdapters[slot]->saveSettings(nic);
9750 if (FAILED(rc)) throw rc;
9751
9752 data.llNetworkAdapters.push_back(nic);
9753 }
9754 }
9755
9756 /* Serial ports */
9757 data.llSerialPorts.clear();
9758 for (ULONG slot = 0;
9759 slot < RT_ELEMENTS(mSerialPorts);
9760 ++slot)
9761 {
9762 settings::SerialPort s;
9763 s.ulSlot = slot;
9764 rc = mSerialPorts[slot]->saveSettings(s);
9765 if (FAILED(rc)) return rc;
9766
9767 data.llSerialPorts.push_back(s);
9768 }
9769
9770 /* Parallel ports */
9771 data.llParallelPorts.clear();
9772 for (ULONG slot = 0;
9773 slot < RT_ELEMENTS(mParallelPorts);
9774 ++slot)
9775 {
9776 settings::ParallelPort p;
9777 p.ulSlot = slot;
9778 rc = mParallelPorts[slot]->saveSettings(p);
9779 if (FAILED(rc)) return rc;
9780
9781 data.llParallelPorts.push_back(p);
9782 }
9783
9784 /* Audio adapter */
9785 rc = mAudioAdapter->saveSettings(data.audioAdapter);
9786 if (FAILED(rc)) return rc;
9787
9788 /* Shared folders */
9789 data.llSharedFolders.clear();
9790 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
9791 it != mHWData->mSharedFolders.end();
9792 ++it)
9793 {
9794 SharedFolder *pSF = *it;
9795 AutoCaller sfCaller(pSF);
9796 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
9797 settings::SharedFolder sf;
9798 sf.strName = pSF->getName();
9799 sf.strHostPath = pSF->getHostPath();
9800 sf.fWritable = !!pSF->isWritable();
9801 sf.fAutoMount = !!pSF->isAutoMounted();
9802
9803 data.llSharedFolders.push_back(sf);
9804 }
9805
9806 // clipboard
9807 data.clipboardMode = mHWData->mClipboardMode;
9808
9809 // drag'n'drop
9810 data.dragAndDropMode = mHWData->mDragAndDropMode;
9811
9812 /* Guest */
9813 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
9814
9815 // IO settings
9816 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
9817 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
9818
9819 /* BandwidthControl (required) */
9820 rc = mBandwidthControl->saveSettings(data.ioSettings);
9821 if (FAILED(rc)) throw rc;
9822
9823 /* Host PCI devices */
9824 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
9825 it != mHWData->mPCIDeviceAssignments.end();
9826 ++it)
9827 {
9828 ComObjPtr<PCIDeviceAttachment> pda = *it;
9829 settings::HostPCIDeviceAttachment hpda;
9830
9831 rc = pda->saveSettings(hpda);
9832 if (FAILED(rc)) throw rc;
9833
9834 data.pciAttachments.push_back(hpda);
9835 }
9836
9837
9838 // guest properties
9839 data.llGuestProperties.clear();
9840#ifdef VBOX_WITH_GUEST_PROPS
9841 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
9842 it != mHWData->mGuestProperties.end();
9843 ++it)
9844 {
9845 HWData::GuestProperty property = *it;
9846
9847 /* Remove transient guest properties at shutdown unless we
9848 * are saving state */
9849 if ( ( mData->mMachineState == MachineState_PoweredOff
9850 || mData->mMachineState == MachineState_Aborted
9851 || mData->mMachineState == MachineState_Teleported)
9852 && ( property.mFlags & guestProp::TRANSIENT
9853 || property.mFlags & guestProp::TRANSRESET))
9854 continue;
9855 settings::GuestProperty prop;
9856 prop.strName = property.strName;
9857 prop.strValue = property.strValue;
9858 prop.timestamp = property.mTimestamp;
9859 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
9860 guestProp::writeFlags(property.mFlags, szFlags);
9861 prop.strFlags = szFlags;
9862
9863 data.llGuestProperties.push_back(prop);
9864 }
9865
9866 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
9867 /* I presume this doesn't require a backup(). */
9868 mData->mGuestPropertiesModified = FALSE;
9869#endif /* VBOX_WITH_GUEST_PROPS defined */
9870
9871 *pDbg = mHWData->mDebugging;
9872 *pAutostart = mHWData->mAutostart;
9873 }
9874 catch(std::bad_alloc &)
9875 {
9876 return E_OUTOFMEMORY;
9877 }
9878
9879 AssertComRC(rc);
9880 return rc;
9881}
9882
9883/**
9884 * Saves the storage controller configuration.
9885 *
9886 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
9887 */
9888HRESULT Machine::saveStorageControllers(settings::Storage &data)
9889{
9890 data.llStorageControllers.clear();
9891
9892 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9893 it != mStorageControllers->end();
9894 ++it)
9895 {
9896 HRESULT rc;
9897 ComObjPtr<StorageController> pCtl = *it;
9898
9899 settings::StorageController ctl;
9900 ctl.strName = pCtl->getName();
9901 ctl.controllerType = pCtl->getControllerType();
9902 ctl.storageBus = pCtl->getStorageBus();
9903 ctl.ulInstance = pCtl->getInstance();
9904 ctl.fBootable = pCtl->getBootable();
9905
9906 /* Save the port count. */
9907 ULONG portCount;
9908 rc = pCtl->COMGETTER(PortCount)(&portCount);
9909 ComAssertComRCRet(rc, rc);
9910 ctl.ulPortCount = portCount;
9911
9912 /* Save fUseHostIOCache */
9913 BOOL fUseHostIOCache;
9914 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
9915 ComAssertComRCRet(rc, rc);
9916 ctl.fUseHostIOCache = !!fUseHostIOCache;
9917
9918 /* Save IDE emulation settings. */
9919 if (ctl.controllerType == StorageControllerType_IntelAhci)
9920 {
9921 if ( (FAILED(rc = pCtl->GetIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
9922 || (FAILED(rc = pCtl->GetIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
9923 || (FAILED(rc = pCtl->GetIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
9924 || (FAILED(rc = pCtl->GetIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
9925 )
9926 ComAssertComRCRet(rc, rc);
9927 }
9928
9929 /* save the devices now. */
9930 rc = saveStorageDevices(pCtl, ctl);
9931 ComAssertComRCRet(rc, rc);
9932
9933 data.llStorageControllers.push_back(ctl);
9934 }
9935
9936 return S_OK;
9937}
9938
9939/**
9940 * Saves the hard disk configuration.
9941 */
9942HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
9943 settings::StorageController &data)
9944{
9945 MediaData::AttachmentList atts;
9946
9947 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
9948 if (FAILED(rc)) return rc;
9949
9950 data.llAttachedDevices.clear();
9951 for (MediaData::AttachmentList::const_iterator it = atts.begin();
9952 it != atts.end();
9953 ++it)
9954 {
9955 settings::AttachedDevice dev;
9956
9957 MediumAttachment *pAttach = *it;
9958 Medium *pMedium = pAttach->getMedium();
9959
9960 dev.deviceType = pAttach->getType();
9961 dev.lPort = pAttach->getPort();
9962 dev.lDevice = pAttach->getDevice();
9963 if (pMedium)
9964 {
9965 if (pMedium->isHostDrive())
9966 dev.strHostDriveSrc = pMedium->getLocationFull();
9967 else
9968 dev.uuid = pMedium->getId();
9969 dev.fPassThrough = pAttach->getPassthrough();
9970 dev.fTempEject = pAttach->getTempEject();
9971 dev.fDiscard = pAttach->getDiscard();
9972 }
9973
9974 dev.strBwGroup = pAttach->getBandwidthGroup();
9975
9976 data.llAttachedDevices.push_back(dev);
9977 }
9978
9979 return S_OK;
9980}
9981
9982/**
9983 * Saves machine state settings as defined by aFlags
9984 * (SaveSTS_* values).
9985 *
9986 * @param aFlags Combination of SaveSTS_* flags.
9987 *
9988 * @note Locks objects for writing.
9989 */
9990HRESULT Machine::saveStateSettings(int aFlags)
9991{
9992 if (aFlags == 0)
9993 return S_OK;
9994
9995 AutoCaller autoCaller(this);
9996 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9997
9998 /* This object's write lock is also necessary to serialize file access
9999 * (prevent concurrent reads and writes) */
10000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10001
10002 HRESULT rc = S_OK;
10003
10004 Assert(mData->pMachineConfigFile);
10005
10006 try
10007 {
10008 if (aFlags & SaveSTS_CurStateModified)
10009 mData->pMachineConfigFile->fCurrentStateModified = true;
10010
10011 if (aFlags & SaveSTS_StateFilePath)
10012 {
10013 if (!mSSData->strStateFilePath.isEmpty())
10014 /* try to make the file name relative to the settings file dir */
10015 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10016 else
10017 mData->pMachineConfigFile->strStateFile.setNull();
10018 }
10019
10020 if (aFlags & SaveSTS_StateTimeStamp)
10021 {
10022 Assert( mData->mMachineState != MachineState_Aborted
10023 || mSSData->strStateFilePath.isEmpty());
10024
10025 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10026
10027 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10028//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10029 }
10030
10031 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10032 }
10033 catch (...)
10034 {
10035 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10036 }
10037
10038 return rc;
10039}
10040
10041/**
10042 * Ensures that the given medium is added to a media registry. If this machine
10043 * was created with 4.0 or later, then the machine registry is used. Otherwise
10044 * the global VirtualBox media registry is used.
10045 *
10046 * Caller must NOT hold machine lock, media tree or any medium locks!
10047 *
10048 * @param pMedium
10049 */
10050void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10051{
10052 /* Paranoia checks: do not hold machine or media tree locks. */
10053 AssertReturnVoid(!isWriteLockOnCurrentThread());
10054 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10055
10056 ComObjPtr<Medium> pBase;
10057 {
10058 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10059 pBase = pMedium->getBase();
10060 }
10061
10062 /* Paranoia checks: do not hold medium locks. */
10063 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10064 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10065
10066 // decide which medium registry to use now that the medium is attached:
10067 Guid uuid;
10068 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10069 // machine XML is VirtualBox 4.0 or higher:
10070 uuid = getId(); // machine UUID
10071 else
10072 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10073
10074 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10075 mParent->markRegistryModified(uuid);
10076
10077 /* For more complex hard disk structures it can happen that the base
10078 * medium isn't yet associated with any medium registry. Do that now. */
10079 if (pMedium != pBase)
10080 {
10081 if (pBase->addRegistry(uuid, true /* fRecurse */))
10082 mParent->markRegistryModified(uuid);
10083 }
10084}
10085
10086/**
10087 * Creates differencing hard disks for all normal hard disks attached to this
10088 * machine and a new set of attachments to refer to created disks.
10089 *
10090 * Used when taking a snapshot or when deleting the current state. Gets called
10091 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10092 *
10093 * This method assumes that mMediaData contains the original hard disk attachments
10094 * it needs to create diffs for. On success, these attachments will be replaced
10095 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10096 * called to delete created diffs which will also rollback mMediaData and restore
10097 * whatever was backed up before calling this method.
10098 *
10099 * Attachments with non-normal hard disks are left as is.
10100 *
10101 * If @a aOnline is @c false then the original hard disks that require implicit
10102 * diffs will be locked for reading. Otherwise it is assumed that they are
10103 * already locked for writing (when the VM was started). Note that in the latter
10104 * case it is responsibility of the caller to lock the newly created diffs for
10105 * writing if this method succeeds.
10106 *
10107 * @param aProgress Progress object to run (must contain at least as
10108 * many operations left as the number of hard disks
10109 * attached).
10110 * @param aOnline Whether the VM was online prior to this operation.
10111 *
10112 * @note The progress object is not marked as completed, neither on success nor
10113 * on failure. This is a responsibility of the caller.
10114 *
10115 * @note Locks this object for writing.
10116 */
10117HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10118 ULONG aWeight,
10119 bool aOnline)
10120{
10121 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10122
10123 AutoCaller autoCaller(this);
10124 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10125
10126 AutoMultiWriteLock2 alock(this->lockHandle(),
10127 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10128
10129 /* must be in a protective state because we release the lock below */
10130 AssertReturn( mData->mMachineState == MachineState_Saving
10131 || mData->mMachineState == MachineState_LiveSnapshotting
10132 || mData->mMachineState == MachineState_RestoringSnapshot
10133 || mData->mMachineState == MachineState_DeletingSnapshot
10134 , E_FAIL);
10135
10136 HRESULT rc = S_OK;
10137
10138 MediumLockListMap lockedMediaOffline;
10139 MediumLockListMap *lockedMediaMap;
10140 if (aOnline)
10141 lockedMediaMap = &mData->mSession.mLockedMedia;
10142 else
10143 lockedMediaMap = &lockedMediaOffline;
10144
10145 try
10146 {
10147 if (!aOnline)
10148 {
10149 /* lock all attached hard disks early to detect "in use"
10150 * situations before creating actual diffs */
10151 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10152 it != mMediaData->mAttachments.end();
10153 ++it)
10154 {
10155 MediumAttachment* pAtt = *it;
10156 if (pAtt->getType() == DeviceType_HardDisk)
10157 {
10158 Medium* pMedium = pAtt->getMedium();
10159 Assert(pMedium);
10160
10161 MediumLockList *pMediumLockList(new MediumLockList());
10162 alock.release();
10163 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10164 false /* fMediumLockWrite */,
10165 NULL,
10166 *pMediumLockList);
10167 alock.acquire();
10168 if (FAILED(rc))
10169 {
10170 delete pMediumLockList;
10171 throw rc;
10172 }
10173 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10174 if (FAILED(rc))
10175 {
10176 throw setError(rc,
10177 tr("Collecting locking information for all attached media failed"));
10178 }
10179 }
10180 }
10181
10182 /* Now lock all media. If this fails, nothing is locked. */
10183 alock.release();
10184 rc = lockedMediaMap->Lock();
10185 alock.acquire();
10186 if (FAILED(rc))
10187 {
10188 throw setError(rc,
10189 tr("Locking of attached media failed"));
10190 }
10191 }
10192
10193 /* remember the current list (note that we don't use backup() since
10194 * mMediaData may be already backed up) */
10195 MediaData::AttachmentList atts = mMediaData->mAttachments;
10196
10197 /* start from scratch */
10198 mMediaData->mAttachments.clear();
10199
10200 /* go through remembered attachments and create diffs for normal hard
10201 * disks and attach them */
10202 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10203 it != atts.end();
10204 ++it)
10205 {
10206 MediumAttachment* pAtt = *it;
10207
10208 DeviceType_T devType = pAtt->getType();
10209 Medium* pMedium = pAtt->getMedium();
10210
10211 if ( devType != DeviceType_HardDisk
10212 || pMedium == NULL
10213 || pMedium->getType() != MediumType_Normal)
10214 {
10215 /* copy the attachment as is */
10216
10217 /** @todo the progress object created in Console::TakeSnaphot
10218 * only expects operations for hard disks. Later other
10219 * device types need to show up in the progress as well. */
10220 if (devType == DeviceType_HardDisk)
10221 {
10222 if (pMedium == NULL)
10223 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10224 aWeight); // weight
10225 else
10226 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10227 pMedium->getBase()->getName().c_str()).raw(),
10228 aWeight); // weight
10229 }
10230
10231 mMediaData->mAttachments.push_back(pAtt);
10232 continue;
10233 }
10234
10235 /* need a diff */
10236 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10237 pMedium->getBase()->getName().c_str()).raw(),
10238 aWeight); // weight
10239
10240 Utf8Str strFullSnapshotFolder;
10241 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10242
10243 ComObjPtr<Medium> diff;
10244 diff.createObject();
10245 // store the diff in the same registry as the parent
10246 // (this cannot fail here because we can't create implicit diffs for
10247 // unregistered images)
10248 Guid uuidRegistryParent;
10249 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10250 Assert(fInRegistry); NOREF(fInRegistry);
10251 rc = diff->init(mParent,
10252 pMedium->getPreferredDiffFormat(),
10253 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10254 uuidRegistryParent);
10255 if (FAILED(rc)) throw rc;
10256
10257 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10258 * the push_back? Looks like we're going to release medium with the
10259 * wrong kind of lock (general issue with if we fail anywhere at all)
10260 * and an orphaned VDI in the snapshots folder. */
10261
10262 /* update the appropriate lock list */
10263 MediumLockList *pMediumLockList;
10264 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10265 AssertComRCThrowRC(rc);
10266 if (aOnline)
10267 {
10268 alock.release();
10269 rc = pMediumLockList->Update(pMedium, false);
10270 alock.acquire();
10271 AssertComRCThrowRC(rc);
10272 }
10273
10274 /* release the locks before the potentially lengthy operation */
10275 alock.release();
10276 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10277 pMediumLockList,
10278 NULL /* aProgress */,
10279 true /* aWait */);
10280 alock.acquire();
10281 if (FAILED(rc)) throw rc;
10282
10283 rc = lockedMediaMap->Unlock();
10284 AssertComRCThrowRC(rc);
10285 alock.release();
10286 rc = pMediumLockList->Append(diff, true);
10287 alock.acquire();
10288 AssertComRCThrowRC(rc);
10289 alock.release();
10290 rc = lockedMediaMap->Lock();
10291 alock.acquire();
10292 AssertComRCThrowRC(rc);
10293
10294 rc = diff->addBackReference(mData->mUuid);
10295 AssertComRCThrowRC(rc);
10296
10297 /* add a new attachment */
10298 ComObjPtr<MediumAttachment> attachment;
10299 attachment.createObject();
10300 rc = attachment->init(this,
10301 diff,
10302 pAtt->getControllerName(),
10303 pAtt->getPort(),
10304 pAtt->getDevice(),
10305 DeviceType_HardDisk,
10306 true /* aImplicit */,
10307 false /* aPassthrough */,
10308 false /* aTempEject */,
10309 pAtt->getNonRotational(),
10310 pAtt->getDiscard(),
10311 pAtt->getBandwidthGroup());
10312 if (FAILED(rc)) throw rc;
10313
10314 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10315 AssertComRCThrowRC(rc);
10316 mMediaData->mAttachments.push_back(attachment);
10317 }
10318 }
10319 catch (HRESULT aRC) { rc = aRC; }
10320
10321 /* unlock all hard disks we locked */
10322 if (!aOnline)
10323 {
10324 ErrorInfoKeeper eik;
10325
10326 HRESULT rc1 = lockedMediaMap->Clear();
10327 AssertComRC(rc1);
10328 }
10329
10330 if (FAILED(rc))
10331 {
10332 MultiResult mrc = rc;
10333
10334 alock.release();
10335 mrc = deleteImplicitDiffs();
10336 }
10337
10338 return rc;
10339}
10340
10341/**
10342 * Deletes implicit differencing hard disks created either by
10343 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10344 *
10345 * Note that to delete hard disks created by #AttachDevice() this method is
10346 * called from #fixupMedia() when the changes are rolled back.
10347 *
10348 * @note Locks this object for writing.
10349 */
10350HRESULT Machine::deleteImplicitDiffs()
10351{
10352 AutoCaller autoCaller(this);
10353 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10354
10355 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10356 LogFlowThisFuncEnter();
10357
10358 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10359
10360 HRESULT rc = S_OK;
10361
10362 MediaData::AttachmentList implicitAtts;
10363
10364 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10365
10366 /* enumerate new attachments */
10367 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10368 it != mMediaData->mAttachments.end();
10369 ++it)
10370 {
10371 ComObjPtr<Medium> hd = (*it)->getMedium();
10372 if (hd.isNull())
10373 continue;
10374
10375 if ((*it)->isImplicit())
10376 {
10377 /* deassociate and mark for deletion */
10378 LogFlowThisFunc(("Detaching '%s', pending deletion\n", (*it)->getLogName()));
10379 rc = hd->removeBackReference(mData->mUuid);
10380 AssertComRC(rc);
10381 implicitAtts.push_back(*it);
10382 continue;
10383 }
10384
10385 /* was this hard disk attached before? */
10386 if (!findAttachment(oldAtts, hd))
10387 {
10388 /* no: de-associate */
10389 LogFlowThisFunc(("Detaching '%s', no deletion\n", (*it)->getLogName()));
10390 rc = hd->removeBackReference(mData->mUuid);
10391 AssertComRC(rc);
10392 continue;
10393 }
10394 LogFlowThisFunc(("Not detaching '%s'\n", (*it)->getLogName()));
10395 }
10396
10397 /* rollback hard disk changes */
10398 mMediaData.rollback();
10399
10400 MultiResult mrc(S_OK);
10401
10402 /* delete unused implicit diffs */
10403 if (implicitAtts.size() != 0)
10404 {
10405 /* will release the lock before the potentially lengthy
10406 * operation, so protect with the special state (unless already
10407 * protected) */
10408 MachineState_T oldState = mData->mMachineState;
10409 if ( oldState != MachineState_Saving
10410 && oldState != MachineState_LiveSnapshotting
10411 && oldState != MachineState_RestoringSnapshot
10412 && oldState != MachineState_DeletingSnapshot
10413 && oldState != MachineState_DeletingSnapshotOnline
10414 && oldState != MachineState_DeletingSnapshotPaused
10415 )
10416 setMachineState(MachineState_SettingUp);
10417
10418 alock.release();
10419
10420 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
10421 it != implicitAtts.end();
10422 ++it)
10423 {
10424 LogFlowThisFunc(("Deleting '%s'\n", (*it)->getLogName()));
10425 ComObjPtr<Medium> hd = (*it)->getMedium();
10426
10427 rc = hd->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10428 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, (*it)->getLogName(), hd->getLocationFull().c_str() ));
10429 mrc = rc;
10430 }
10431
10432 alock.acquire();
10433
10434 if (mData->mMachineState == MachineState_SettingUp)
10435 setMachineState(oldState);
10436 }
10437
10438 return mrc;
10439}
10440
10441/**
10442 * Looks through the given list of media attachments for one with the given parameters
10443 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10444 * can be searched as well if needed.
10445 *
10446 * @param list
10447 * @param aControllerName
10448 * @param aControllerPort
10449 * @param aDevice
10450 * @return
10451 */
10452MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10453 IN_BSTR aControllerName,
10454 LONG aControllerPort,
10455 LONG aDevice)
10456{
10457 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10458 it != ll.end();
10459 ++it)
10460 {
10461 MediumAttachment *pAttach = *it;
10462 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
10463 return pAttach;
10464 }
10465
10466 return NULL;
10467}
10468
10469/**
10470 * Looks through the given list of media attachments for one with the given parameters
10471 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10472 * can be searched as well if needed.
10473 *
10474 * @param list
10475 * @param aControllerName
10476 * @param aControllerPort
10477 * @param aDevice
10478 * @return
10479 */
10480MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10481 ComObjPtr<Medium> pMedium)
10482{
10483 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10484 it != ll.end();
10485 ++it)
10486 {
10487 MediumAttachment *pAttach = *it;
10488 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10489 if (pMediumThis == pMedium)
10490 return pAttach;
10491 }
10492
10493 return NULL;
10494}
10495
10496/**
10497 * Looks through the given list of media attachments for one with the given parameters
10498 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10499 * can be searched as well if needed.
10500 *
10501 * @param list
10502 * @param aControllerName
10503 * @param aControllerPort
10504 * @param aDevice
10505 * @return
10506 */
10507MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10508 Guid &id)
10509{
10510 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10511 it != ll.end();
10512 ++it)
10513 {
10514 MediumAttachment *pAttach = *it;
10515 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10516 if (pMediumThis->getId() == id)
10517 return pAttach;
10518 }
10519
10520 return NULL;
10521}
10522
10523/**
10524 * Main implementation for Machine::DetachDevice. This also gets called
10525 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10526 *
10527 * @param pAttach Medium attachment to detach.
10528 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10529 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
10530 * @return
10531 */
10532HRESULT Machine::detachDevice(MediumAttachment *pAttach,
10533 AutoWriteLock &writeLock,
10534 Snapshot *pSnapshot)
10535{
10536 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
10537 DeviceType_T mediumType = pAttach->getType();
10538
10539 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
10540
10541 if (pAttach->isImplicit())
10542 {
10543 /* attempt to implicitly delete the implicitly created diff */
10544
10545 /// @todo move the implicit flag from MediumAttachment to Medium
10546 /// and forbid any hard disk operation when it is implicit. Or maybe
10547 /// a special media state for it to make it even more simple.
10548
10549 Assert(mMediaData.isBackedUp());
10550
10551 /* will release the lock before the potentially lengthy operation, so
10552 * protect with the special state */
10553 MachineState_T oldState = mData->mMachineState;
10554 setMachineState(MachineState_SettingUp);
10555
10556 writeLock.release();
10557
10558 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
10559 true /*aWait*/);
10560
10561 writeLock.acquire();
10562
10563 setMachineState(oldState);
10564
10565 if (FAILED(rc)) return rc;
10566 }
10567
10568 setModified(IsModified_Storage);
10569 mMediaData.backup();
10570 mMediaData->mAttachments.remove(pAttach);
10571
10572 if (!oldmedium.isNull())
10573 {
10574 // if this is from a snapshot, do not defer detachment to commitMedia()
10575 if (pSnapshot)
10576 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
10577 // else if non-hard disk media, do not defer detachment to commitMedia() either
10578 else if (mediumType != DeviceType_HardDisk)
10579 oldmedium->removeBackReference(mData->mUuid);
10580 }
10581
10582 return S_OK;
10583}
10584
10585/**
10586 * Goes thru all media of the given list and
10587 *
10588 * 1) calls detachDevice() on each of them for this machine and
10589 * 2) adds all Medium objects found in the process to the given list,
10590 * depending on cleanupMode.
10591 *
10592 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10593 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10594 * media to the list.
10595 *
10596 * This gets called from Machine::Unregister, both for the actual Machine and
10597 * the SnapshotMachine objects that might be found in the snapshots.
10598 *
10599 * Requires caller and locking. The machine lock must be passed in because it
10600 * will be passed on to detachDevice which needs it for temporary unlocking.
10601 *
10602 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
10603 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
10604 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
10605 * otherwise no media get added.
10606 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
10607 * @return
10608 */
10609HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
10610 Snapshot *pSnapshot,
10611 CleanupMode_T cleanupMode,
10612 MediaList &llMedia)
10613{
10614 Assert(isWriteLockOnCurrentThread());
10615
10616 HRESULT rc;
10617
10618 // make a temporary list because detachDevice invalidates iterators into
10619 // mMediaData->mAttachments
10620 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
10621
10622 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
10623 it != llAttachments2.end();
10624 ++it)
10625 {
10626 ComObjPtr<MediumAttachment> &pAttach = *it;
10627 ComObjPtr<Medium> pMedium = pAttach->getMedium();
10628
10629 if (!pMedium.isNull())
10630 {
10631 AutoCaller mac(pMedium);
10632 if (FAILED(mac.rc())) return mac.rc();
10633 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
10634 DeviceType_T devType = pMedium->getDeviceType();
10635 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
10636 && devType == DeviceType_HardDisk)
10637 || (cleanupMode == CleanupMode_Full)
10638 )
10639 {
10640 llMedia.push_back(pMedium);
10641 ComObjPtr<Medium> pParent = pMedium->getParent();
10642 /*
10643 * Search for medias which are not attached to any machine, but
10644 * in the chain to an attached disk. Mediums are only consided
10645 * if they are:
10646 * - have only one child
10647 * - no references to any machines
10648 * - are of normal medium type
10649 */
10650 while (!pParent.isNull())
10651 {
10652 AutoCaller mac1(pParent);
10653 if (FAILED(mac1.rc())) return mac1.rc();
10654 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
10655 if (pParent->getChildren().size() == 1)
10656 {
10657 if ( pParent->getMachineBackRefCount() == 0
10658 && pParent->getType() == MediumType_Normal
10659 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
10660 llMedia.push_back(pParent);
10661 }
10662 else
10663 break;
10664 pParent = pParent->getParent();
10665 }
10666 }
10667 }
10668
10669 // real machine: then we need to use the proper method
10670 rc = detachDevice(pAttach, writeLock, pSnapshot);
10671
10672 if (FAILED(rc))
10673 return rc;
10674 }
10675
10676 return S_OK;
10677}
10678
10679/**
10680 * Perform deferred hard disk detachments.
10681 *
10682 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10683 * backed up).
10684 *
10685 * If @a aOnline is @c true then this method will also unlock the old hard disks
10686 * for which the new implicit diffs were created and will lock these new diffs for
10687 * writing.
10688 *
10689 * @param aOnline Whether the VM was online prior to this operation.
10690 *
10691 * @note Locks this object for writing!
10692 */
10693void Machine::commitMedia(bool aOnline /*= false*/)
10694{
10695 AutoCaller autoCaller(this);
10696 AssertComRCReturnVoid(autoCaller.rc());
10697
10698 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10699
10700 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
10701
10702 HRESULT rc = S_OK;
10703
10704 /* no attach/detach operations -- nothing to do */
10705 if (!mMediaData.isBackedUp())
10706 return;
10707
10708 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10709 bool fMediaNeedsLocking = false;
10710
10711 /* enumerate new attachments */
10712 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10713 it != mMediaData->mAttachments.end();
10714 ++it)
10715 {
10716 MediumAttachment *pAttach = *it;
10717
10718 pAttach->commit();
10719
10720 Medium* pMedium = pAttach->getMedium();
10721 bool fImplicit = pAttach->isImplicit();
10722
10723 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
10724 (pMedium) ? pMedium->getName().c_str() : "NULL",
10725 fImplicit));
10726
10727 /** @todo convert all this Machine-based voodoo to MediumAttachment
10728 * based commit logic. */
10729 if (fImplicit)
10730 {
10731 /* convert implicit attachment to normal */
10732 pAttach->setImplicit(false);
10733
10734 if ( aOnline
10735 && pMedium
10736 && pAttach->getType() == DeviceType_HardDisk
10737 )
10738 {
10739 ComObjPtr<Medium> parent = pMedium->getParent();
10740 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
10741
10742 /* update the appropriate lock list */
10743 MediumLockList *pMediumLockList;
10744 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10745 AssertComRC(rc);
10746 if (pMediumLockList)
10747 {
10748 /* unlock if there's a need to change the locking */
10749 if (!fMediaNeedsLocking)
10750 {
10751 rc = mData->mSession.mLockedMedia.Unlock();
10752 AssertComRC(rc);
10753 fMediaNeedsLocking = true;
10754 }
10755 rc = pMediumLockList->Update(parent, false);
10756 AssertComRC(rc);
10757 rc = pMediumLockList->Append(pMedium, true);
10758 AssertComRC(rc);
10759 }
10760 }
10761
10762 continue;
10763 }
10764
10765 if (pMedium)
10766 {
10767 /* was this medium attached before? */
10768 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
10769 oldIt != oldAtts.end();
10770 ++oldIt)
10771 {
10772 MediumAttachment *pOldAttach = *oldIt;
10773 if (pOldAttach->getMedium() == pMedium)
10774 {
10775 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
10776
10777 /* yes: remove from old to avoid de-association */
10778 oldAtts.erase(oldIt);
10779 break;
10780 }
10781 }
10782 }
10783 }
10784
10785 /* enumerate remaining old attachments and de-associate from the
10786 * current machine state */
10787 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
10788 it != oldAtts.end();
10789 ++it)
10790 {
10791 MediumAttachment *pAttach = *it;
10792 Medium* pMedium = pAttach->getMedium();
10793
10794 /* Detach only hard disks, since DVD/floppy media is detached
10795 * instantly in MountMedium. */
10796 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
10797 {
10798 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
10799
10800 /* now de-associate from the current machine state */
10801 rc = pMedium->removeBackReference(mData->mUuid);
10802 AssertComRC(rc);
10803
10804 if (aOnline)
10805 {
10806 /* unlock since medium is not used anymore */
10807 MediumLockList *pMediumLockList;
10808 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10809 AssertComRC(rc);
10810 if (pMediumLockList)
10811 {
10812 rc = mData->mSession.mLockedMedia.Remove(pAttach);
10813 AssertComRC(rc);
10814 }
10815 }
10816 }
10817 }
10818
10819 /* take media locks again so that the locking state is consistent */
10820 if (fMediaNeedsLocking)
10821 {
10822 Assert(aOnline);
10823 rc = mData->mSession.mLockedMedia.Lock();
10824 AssertComRC(rc);
10825 }
10826
10827 /* commit the hard disk changes */
10828 mMediaData.commit();
10829
10830 if (isSessionMachine())
10831 {
10832 /*
10833 * Update the parent machine to point to the new owner.
10834 * This is necessary because the stored parent will point to the
10835 * session machine otherwise and cause crashes or errors later
10836 * when the session machine gets invalid.
10837 */
10838 /** @todo Change the MediumAttachment class to behave like any other
10839 * class in this regard by creating peer MediumAttachment
10840 * objects for session machines and share the data with the peer
10841 * machine.
10842 */
10843 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10844 it != mMediaData->mAttachments.end();
10845 ++it)
10846 {
10847 (*it)->updateParentMachine(mPeer);
10848 }
10849
10850 /* attach new data to the primary machine and reshare it */
10851 mPeer->mMediaData.attach(mMediaData);
10852 }
10853
10854 return;
10855}
10856
10857/**
10858 * Perform deferred deletion of implicitly created diffs.
10859 *
10860 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10861 * backed up).
10862 *
10863 * @note Locks this object for writing!
10864 */
10865void Machine::rollbackMedia()
10866{
10867 AutoCaller autoCaller(this);
10868 AssertComRCReturnVoid (autoCaller.rc());
10869
10870 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10871
10872 LogFlowThisFunc(("Entering\n"));
10873
10874 HRESULT rc = S_OK;
10875
10876 /* no attach/detach operations -- nothing to do */
10877 if (!mMediaData.isBackedUp())
10878 return;
10879
10880 /* enumerate new attachments */
10881 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10882 it != mMediaData->mAttachments.end();
10883 ++it)
10884 {
10885 MediumAttachment *pAttach = *it;
10886 /* Fix up the backrefs for DVD/floppy media. */
10887 if (pAttach->getType() != DeviceType_HardDisk)
10888 {
10889 Medium* pMedium = pAttach->getMedium();
10890 if (pMedium)
10891 {
10892 rc = pMedium->removeBackReference(mData->mUuid);
10893 AssertComRC(rc);
10894 }
10895 }
10896
10897 (*it)->rollback();
10898
10899 pAttach = *it;
10900 /* Fix up the backrefs for DVD/floppy media. */
10901 if (pAttach->getType() != DeviceType_HardDisk)
10902 {
10903 Medium* pMedium = pAttach->getMedium();
10904 if (pMedium)
10905 {
10906 rc = pMedium->addBackReference(mData->mUuid);
10907 AssertComRC(rc);
10908 }
10909 }
10910 }
10911
10912 /** @todo convert all this Machine-based voodoo to MediumAttachment
10913 * based rollback logic. */
10914 deleteImplicitDiffs();
10915
10916 return;
10917}
10918
10919/**
10920 * Returns true if the settings file is located in the directory named exactly
10921 * as the machine; this means, among other things, that the machine directory
10922 * should be auto-renamed.
10923 *
10924 * @param aSettingsDir if not NULL, the full machine settings file directory
10925 * name will be assigned there.
10926 *
10927 * @note Doesn't lock anything.
10928 * @note Not thread safe (must be called from this object's lock).
10929 */
10930bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
10931{
10932 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
10933 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
10934 if (aSettingsDir)
10935 *aSettingsDir = strMachineDirName;
10936 strMachineDirName.stripPath(); // vmname
10937 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
10938 strConfigFileOnly.stripPath() // vmname.vbox
10939 .stripExt(); // vmname
10940
10941 AssertReturn(!strMachineDirName.isEmpty(), false);
10942 AssertReturn(!strConfigFileOnly.isEmpty(), false);
10943
10944 return strMachineDirName == strConfigFileOnly;
10945}
10946
10947/**
10948 * Discards all changes to machine settings.
10949 *
10950 * @param aNotify Whether to notify the direct session about changes or not.
10951 *
10952 * @note Locks objects for writing!
10953 */
10954void Machine::rollback(bool aNotify)
10955{
10956 AutoCaller autoCaller(this);
10957 AssertComRCReturn(autoCaller.rc(), (void)0);
10958
10959 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10960
10961 if (!mStorageControllers.isNull())
10962 {
10963 if (mStorageControllers.isBackedUp())
10964 {
10965 /* unitialize all new devices (absent in the backed up list). */
10966 StorageControllerList::const_iterator it = mStorageControllers->begin();
10967 StorageControllerList *backedList = mStorageControllers.backedUpData();
10968 while (it != mStorageControllers->end())
10969 {
10970 if ( std::find(backedList->begin(), backedList->end(), *it)
10971 == backedList->end()
10972 )
10973 {
10974 (*it)->uninit();
10975 }
10976 ++it;
10977 }
10978
10979 /* restore the list */
10980 mStorageControllers.rollback();
10981 }
10982
10983 /* rollback any changes to devices after restoring the list */
10984 if (mData->flModifications & IsModified_Storage)
10985 {
10986 StorageControllerList::const_iterator it = mStorageControllers->begin();
10987 while (it != mStorageControllers->end())
10988 {
10989 (*it)->rollback();
10990 ++it;
10991 }
10992 }
10993 }
10994
10995 mUserData.rollback();
10996
10997 mHWData.rollback();
10998
10999 if (mData->flModifications & IsModified_Storage)
11000 rollbackMedia();
11001
11002 if (mBIOSSettings)
11003 mBIOSSettings->rollback();
11004
11005 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11006 mVRDEServer->rollback();
11007
11008 if (mAudioAdapter)
11009 mAudioAdapter->rollback();
11010
11011 if (mUSBController && (mData->flModifications & IsModified_USB))
11012 mUSBController->rollback();
11013
11014 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11015 mBandwidthControl->rollback();
11016
11017 if (!mHWData.isNull())
11018 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11019 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11020 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11021 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11022
11023 if (mData->flModifications & IsModified_NetworkAdapters)
11024 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11025 if ( mNetworkAdapters[slot]
11026 && mNetworkAdapters[slot]->isModified())
11027 {
11028 mNetworkAdapters[slot]->rollback();
11029 networkAdapters[slot] = mNetworkAdapters[slot];
11030 }
11031
11032 if (mData->flModifications & IsModified_SerialPorts)
11033 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11034 if ( mSerialPorts[slot]
11035 && mSerialPorts[slot]->isModified())
11036 {
11037 mSerialPorts[slot]->rollback();
11038 serialPorts[slot] = mSerialPorts[slot];
11039 }
11040
11041 if (mData->flModifications & IsModified_ParallelPorts)
11042 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11043 if ( mParallelPorts[slot]
11044 && mParallelPorts[slot]->isModified())
11045 {
11046 mParallelPorts[slot]->rollback();
11047 parallelPorts[slot] = mParallelPorts[slot];
11048 }
11049
11050 if (aNotify)
11051 {
11052 /* inform the direct session about changes */
11053
11054 ComObjPtr<Machine> that = this;
11055 uint32_t flModifications = mData->flModifications;
11056 alock.release();
11057
11058 if (flModifications & IsModified_SharedFolders)
11059 that->onSharedFolderChange();
11060
11061 if (flModifications & IsModified_VRDEServer)
11062 that->onVRDEServerChange(/* aRestart */ TRUE);
11063 if (flModifications & IsModified_USB)
11064 that->onUSBControllerChange();
11065
11066 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
11067 if (networkAdapters[slot])
11068 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
11069 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
11070 if (serialPorts[slot])
11071 that->onSerialPortChange(serialPorts[slot]);
11072 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
11073 if (parallelPorts[slot])
11074 that->onParallelPortChange(parallelPorts[slot]);
11075
11076 if (flModifications & IsModified_Storage)
11077 that->onStorageControllerChange();
11078
11079#if 0
11080 if (flModifications & IsModified_BandwidthControl)
11081 that->onBandwidthControlChange();
11082#endif
11083 }
11084}
11085
11086/**
11087 * Commits all the changes to machine settings.
11088 *
11089 * Note that this operation is supposed to never fail.
11090 *
11091 * @note Locks this object and children for writing.
11092 */
11093void Machine::commit()
11094{
11095 AutoCaller autoCaller(this);
11096 AssertComRCReturnVoid(autoCaller.rc());
11097
11098 AutoCaller peerCaller(mPeer);
11099 AssertComRCReturnVoid(peerCaller.rc());
11100
11101 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11102
11103 /*
11104 * use safe commit to ensure Snapshot machines (that share mUserData)
11105 * will still refer to a valid memory location
11106 */
11107 mUserData.commitCopy();
11108
11109 mHWData.commit();
11110
11111 if (mMediaData.isBackedUp())
11112 commitMedia();
11113
11114 mBIOSSettings->commit();
11115 mVRDEServer->commit();
11116 mAudioAdapter->commit();
11117 mUSBController->commit();
11118 mBandwidthControl->commit();
11119
11120 /* Since mNetworkAdapters is a list which might have been changed (resized)
11121 * without using the Backupable<> template we need to handle the copying
11122 * of the list entries manually, including the creation of peers for the
11123 * new objects. */
11124 bool commitNetworkAdapters = false;
11125 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11126 if (mPeer)
11127 {
11128 /* commit everything, even the ones which will go away */
11129 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11130 mNetworkAdapters[slot]->commit();
11131 /* copy over the new entries, creating a peer and uninit the original */
11132 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11133 for (size_t slot = 0; slot < newSize; slot++)
11134 {
11135 /* look if this adapter has a peer device */
11136 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
11137 if (!peer)
11138 {
11139 /* no peer means the adapter is a newly created one;
11140 * create a peer owning data this data share it with */
11141 peer.createObject();
11142 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11143 }
11144 mPeer->mNetworkAdapters[slot] = peer;
11145 }
11146 /* uninit any no longer needed network adapters */
11147 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
11148 mNetworkAdapters[slot]->uninit();
11149 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
11150 {
11151 if (mPeer->mNetworkAdapters[slot])
11152 mPeer->mNetworkAdapters[slot]->uninit();
11153 }
11154 /* Keep the original network adapter count until this point, so that
11155 * discarding a chipset type change will not lose settings. */
11156 mNetworkAdapters.resize(newSize);
11157 mPeer->mNetworkAdapters.resize(newSize);
11158 }
11159 else
11160 {
11161 /* we have no peer (our parent is the newly created machine);
11162 * just commit changes to the network adapters */
11163 commitNetworkAdapters = true;
11164 }
11165 if (commitNetworkAdapters)
11166 {
11167 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11168 mNetworkAdapters[slot]->commit();
11169 }
11170
11171 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11172 mSerialPorts[slot]->commit();
11173 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11174 mParallelPorts[slot]->commit();
11175
11176 bool commitStorageControllers = false;
11177
11178 if (mStorageControllers.isBackedUp())
11179 {
11180 mStorageControllers.commit();
11181
11182 if (mPeer)
11183 {
11184 /* Commit all changes to new controllers (this will reshare data with
11185 * peers for those who have peers) */
11186 StorageControllerList *newList = new StorageControllerList();
11187 StorageControllerList::const_iterator it = mStorageControllers->begin();
11188 while (it != mStorageControllers->end())
11189 {
11190 (*it)->commit();
11191
11192 /* look if this controller has a peer device */
11193 ComObjPtr<StorageController> peer = (*it)->getPeer();
11194 if (!peer)
11195 {
11196 /* no peer means the device is a newly created one;
11197 * create a peer owning data this device share it with */
11198 peer.createObject();
11199 peer->init(mPeer, *it, true /* aReshare */);
11200 }
11201 else
11202 {
11203 /* remove peer from the old list */
11204 mPeer->mStorageControllers->remove(peer);
11205 }
11206 /* and add it to the new list */
11207 newList->push_back(peer);
11208
11209 ++it;
11210 }
11211
11212 /* uninit old peer's controllers that are left */
11213 it = mPeer->mStorageControllers->begin();
11214 while (it != mPeer->mStorageControllers->end())
11215 {
11216 (*it)->uninit();
11217 ++it;
11218 }
11219
11220 /* attach new list of controllers to our peer */
11221 mPeer->mStorageControllers.attach(newList);
11222 }
11223 else
11224 {
11225 /* we have no peer (our parent is the newly created machine);
11226 * just commit changes to devices */
11227 commitStorageControllers = true;
11228 }
11229 }
11230 else
11231 {
11232 /* the list of controllers itself is not changed,
11233 * just commit changes to controllers themselves */
11234 commitStorageControllers = true;
11235 }
11236
11237 if (commitStorageControllers)
11238 {
11239 StorageControllerList::const_iterator it = mStorageControllers->begin();
11240 while (it != mStorageControllers->end())
11241 {
11242 (*it)->commit();
11243 ++it;
11244 }
11245 }
11246
11247 if (isSessionMachine())
11248 {
11249 /* attach new data to the primary machine and reshare it */
11250 mPeer->mUserData.attach(mUserData);
11251 mPeer->mHWData.attach(mHWData);
11252 /* mMediaData is reshared by fixupMedia */
11253 // mPeer->mMediaData.attach(mMediaData);
11254 Assert(mPeer->mMediaData.data() == mMediaData.data());
11255 }
11256}
11257
11258/**
11259 * Copies all the hardware data from the given machine.
11260 *
11261 * Currently, only called when the VM is being restored from a snapshot. In
11262 * particular, this implies that the VM is not running during this method's
11263 * call.
11264 *
11265 * @note This method must be called from under this object's lock.
11266 *
11267 * @note This method doesn't call #commit(), so all data remains backed up and
11268 * unsaved.
11269 */
11270void Machine::copyFrom(Machine *aThat)
11271{
11272 AssertReturnVoid(!isSnapshotMachine());
11273 AssertReturnVoid(aThat->isSnapshotMachine());
11274
11275 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11276
11277 mHWData.assignCopy(aThat->mHWData);
11278
11279 // create copies of all shared folders (mHWData after attaching a copy
11280 // contains just references to original objects)
11281 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11282 it != mHWData->mSharedFolders.end();
11283 ++it)
11284 {
11285 ComObjPtr<SharedFolder> folder;
11286 folder.createObject();
11287 HRESULT rc = folder->initCopy(getMachine(), *it);
11288 AssertComRC(rc);
11289 *it = folder;
11290 }
11291
11292 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
11293 mVRDEServer->copyFrom(aThat->mVRDEServer);
11294 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
11295 mUSBController->copyFrom(aThat->mUSBController);
11296 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
11297
11298 /* create private copies of all controllers */
11299 mStorageControllers.backup();
11300 mStorageControllers->clear();
11301 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11302 it != aThat->mStorageControllers->end();
11303 ++it)
11304 {
11305 ComObjPtr<StorageController> ctrl;
11306 ctrl.createObject();
11307 ctrl->initCopy(this, *it);
11308 mStorageControllers->push_back(ctrl);
11309 }
11310
11311 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11312 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
11313 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11314 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
11315 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11316 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
11317}
11318
11319/**
11320 * Returns whether the given storage controller is hotplug capable.
11321 *
11322 * @returns true if the controller supports hotplugging
11323 * false otherwise.
11324 * @param enmCtrlType The controller type to check for.
11325 */
11326bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11327{
11328 switch (enmCtrlType)
11329 {
11330 case StorageControllerType_IntelAhci:
11331 return true;
11332 case StorageControllerType_LsiLogic:
11333 case StorageControllerType_LsiLogicSas:
11334 case StorageControllerType_BusLogic:
11335 case StorageControllerType_PIIX3:
11336 case StorageControllerType_PIIX4:
11337 case StorageControllerType_ICH6:
11338 case StorageControllerType_I82078:
11339 default:
11340 return false;
11341 }
11342}
11343
11344#ifdef VBOX_WITH_RESOURCE_USAGE_API
11345
11346void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11347{
11348 AssertReturnVoid(isWriteLockOnCurrentThread());
11349 AssertPtrReturnVoid(aCollector);
11350
11351 pm::CollectorHAL *hal = aCollector->getHAL();
11352 /* Create sub metrics */
11353 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11354 "Percentage of processor time spent in user mode by the VM process.");
11355 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11356 "Percentage of processor time spent in kernel mode by the VM process.");
11357 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11358 "Size of resident portion of VM process in memory.");
11359 /* Create and register base metrics */
11360 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11361 cpuLoadUser, cpuLoadKernel);
11362 aCollector->registerBaseMetric(cpuLoad);
11363 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11364 ramUsageUsed);
11365 aCollector->registerBaseMetric(ramUsage);
11366
11367 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11368 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11369 new pm::AggregateAvg()));
11370 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11371 new pm::AggregateMin()));
11372 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11373 new pm::AggregateMax()));
11374 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11375 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11376 new pm::AggregateAvg()));
11377 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11378 new pm::AggregateMin()));
11379 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11380 new pm::AggregateMax()));
11381
11382 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11383 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11384 new pm::AggregateAvg()));
11385 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11386 new pm::AggregateMin()));
11387 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11388 new pm::AggregateMax()));
11389
11390
11391 /* Guest metrics collector */
11392 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11393 aCollector->registerGuest(mCollectorGuest);
11394 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11395 this, __PRETTY_FUNCTION__, mCollectorGuest));
11396
11397 /* Create sub metrics */
11398 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11399 "Percentage of processor time spent in user mode as seen by the guest.");
11400 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11401 "Percentage of processor time spent in kernel mode as seen by the guest.");
11402 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11403 "Percentage of processor time spent idling as seen by the guest.");
11404
11405 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11406 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11407 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11408 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11409 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11410 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11411
11412 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11413
11414 /* Create and register base metrics */
11415 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11416 guestLoadUser, guestLoadKernel, guestLoadIdle);
11417 aCollector->registerBaseMetric(guestCpuLoad);
11418
11419 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11420 guestMemTotal, guestMemFree,
11421 guestMemBalloon, guestMemShared,
11422 guestMemCache, guestPagedTotal);
11423 aCollector->registerBaseMetric(guestCpuMem);
11424
11425 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11426 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11427 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11428 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11429
11430 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11431 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11432 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11433 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11434
11435 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11436 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11437 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11438 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11439
11440 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11441 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11442 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11443 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11444
11445 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11446 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11447 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11448 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11449
11450 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11451 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11452 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11453 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11454
11455 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11456 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11457 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11458 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11459
11460 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11461 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11462 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11463 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11464
11465 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11466 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11467 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11468 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11469}
11470
11471void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11472{
11473 AssertReturnVoid(isWriteLockOnCurrentThread());
11474
11475 if (aCollector)
11476 {
11477 aCollector->unregisterMetricsFor(aMachine);
11478 aCollector->unregisterBaseMetricsFor(aMachine);
11479 }
11480}
11481
11482#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11483
11484
11485////////////////////////////////////////////////////////////////////////////////
11486
11487DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11488
11489HRESULT SessionMachine::FinalConstruct()
11490{
11491 LogFlowThisFunc(("\n"));
11492
11493#if defined(RT_OS_WINDOWS)
11494 mIPCSem = NULL;
11495#elif defined(RT_OS_OS2)
11496 mIPCSem = NULLHANDLE;
11497#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11498 mIPCSem = -1;
11499#else
11500# error "Port me!"
11501#endif
11502
11503 return BaseFinalConstruct();
11504}
11505
11506void SessionMachine::FinalRelease()
11507{
11508 LogFlowThisFunc(("\n"));
11509
11510 uninit(Uninit::Unexpected);
11511
11512 BaseFinalRelease();
11513}
11514
11515/**
11516 * @note Must be called only by Machine::openSession() from its own write lock.
11517 */
11518HRESULT SessionMachine::init(Machine *aMachine)
11519{
11520 LogFlowThisFuncEnter();
11521 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
11522
11523 AssertReturn(aMachine, E_INVALIDARG);
11524
11525 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
11526
11527 /* Enclose the state transition NotReady->InInit->Ready */
11528 AutoInitSpan autoInitSpan(this);
11529 AssertReturn(autoInitSpan.isOk(), E_FAIL);
11530
11531 /* create the interprocess semaphore */
11532#if defined(RT_OS_WINDOWS)
11533 mIPCSemName = aMachine->mData->m_strConfigFileFull;
11534 for (size_t i = 0; i < mIPCSemName.length(); i++)
11535 if (mIPCSemName.raw()[i] == '\\')
11536 mIPCSemName.raw()[i] = '/';
11537 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
11538 ComAssertMsgRet(mIPCSem,
11539 ("Cannot create IPC mutex '%ls', err=%d",
11540 mIPCSemName.raw(), ::GetLastError()),
11541 E_FAIL);
11542#elif defined(RT_OS_OS2)
11543 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
11544 aMachine->mData->mUuid.raw());
11545 mIPCSemName = ipcSem;
11546 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
11547 ComAssertMsgRet(arc == NO_ERROR,
11548 ("Cannot create IPC mutex '%s', arc=%ld",
11549 ipcSem.c_str(), arc),
11550 E_FAIL);
11551#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11552# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11553# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
11554 /** @todo Check that this still works correctly. */
11555 AssertCompileSize(key_t, 8);
11556# else
11557 AssertCompileSize(key_t, 4);
11558# endif
11559 key_t key;
11560 mIPCSem = -1;
11561 mIPCKey = "0";
11562 for (uint32_t i = 0; i < 1 << 24; i++)
11563 {
11564 key = ((uint32_t)'V' << 24) | i;
11565 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
11566 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
11567 {
11568 mIPCSem = sem;
11569 if (sem >= 0)
11570 mIPCKey = BstrFmt("%u", key);
11571 break;
11572 }
11573 }
11574# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11575 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
11576 char *pszSemName = NULL;
11577 RTStrUtf8ToCurrentCP(&pszSemName, semName);
11578 key_t key = ::ftok(pszSemName, 'V');
11579 RTStrFree(pszSemName);
11580
11581 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
11582# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11583
11584 int errnoSave = errno;
11585 if (mIPCSem < 0 && errnoSave == ENOSYS)
11586 {
11587 setError(E_FAIL,
11588 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
11589 "support for SysV IPC. Check the host kernel configuration for "
11590 "CONFIG_SYSVIPC=y"));
11591 return E_FAIL;
11592 }
11593 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
11594 * the IPC semaphores */
11595 if (mIPCSem < 0 && errnoSave == ENOSPC)
11596 {
11597#ifdef RT_OS_LINUX
11598 setError(E_FAIL,
11599 tr("Cannot create IPC semaphore because the system limit for the "
11600 "maximum number of semaphore sets (SEMMNI), or the system wide "
11601 "maximum number of semaphores (SEMMNS) would be exceeded. The "
11602 "current set of SysV IPC semaphores can be determined from "
11603 "the file /proc/sysvipc/sem"));
11604#else
11605 setError(E_FAIL,
11606 tr("Cannot create IPC semaphore because the system-imposed limit "
11607 "on the maximum number of allowed semaphores or semaphore "
11608 "identifiers system-wide would be exceeded"));
11609#endif
11610 return E_FAIL;
11611 }
11612 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
11613 E_FAIL);
11614 /* set the initial value to 1 */
11615 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
11616 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
11617 E_FAIL);
11618#else
11619# error "Port me!"
11620#endif
11621
11622 /* memorize the peer Machine */
11623 unconst(mPeer) = aMachine;
11624 /* share the parent pointer */
11625 unconst(mParent) = aMachine->mParent;
11626
11627 /* take the pointers to data to share */
11628 mData.share(aMachine->mData);
11629 mSSData.share(aMachine->mSSData);
11630
11631 mUserData.share(aMachine->mUserData);
11632 mHWData.share(aMachine->mHWData);
11633 mMediaData.share(aMachine->mMediaData);
11634
11635 mStorageControllers.allocate();
11636 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
11637 it != aMachine->mStorageControllers->end();
11638 ++it)
11639 {
11640 ComObjPtr<StorageController> ctl;
11641 ctl.createObject();
11642 ctl->init(this, *it);
11643 mStorageControllers->push_back(ctl);
11644 }
11645
11646 unconst(mBIOSSettings).createObject();
11647 mBIOSSettings->init(this, aMachine->mBIOSSettings);
11648 /* create another VRDEServer object that will be mutable */
11649 unconst(mVRDEServer).createObject();
11650 mVRDEServer->init(this, aMachine->mVRDEServer);
11651 /* create another audio adapter object that will be mutable */
11652 unconst(mAudioAdapter).createObject();
11653 mAudioAdapter->init(this, aMachine->mAudioAdapter);
11654 /* create a list of serial ports that will be mutable */
11655 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11656 {
11657 unconst(mSerialPorts[slot]).createObject();
11658 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
11659 }
11660 /* create a list of parallel ports that will be mutable */
11661 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11662 {
11663 unconst(mParallelPorts[slot]).createObject();
11664 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
11665 }
11666 /* create another USB controller object that will be mutable */
11667 unconst(mUSBController).createObject();
11668 mUSBController->init(this, aMachine->mUSBController);
11669
11670 /* create a list of network adapters that will be mutable */
11671 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
11672 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11673 {
11674 unconst(mNetworkAdapters[slot]).createObject();
11675 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
11676 }
11677
11678 /* create another bandwidth control object that will be mutable */
11679 unconst(mBandwidthControl).createObject();
11680 mBandwidthControl->init(this, aMachine->mBandwidthControl);
11681
11682 /* default is to delete saved state on Saved -> PoweredOff transition */
11683 mRemoveSavedState = true;
11684
11685 /* Confirm a successful initialization when it's the case */
11686 autoInitSpan.setSucceeded();
11687
11688 LogFlowThisFuncLeave();
11689 return S_OK;
11690}
11691
11692/**
11693 * Uninitializes this session object. If the reason is other than
11694 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
11695 *
11696 * @param aReason uninitialization reason
11697 *
11698 * @note Locks mParent + this object for writing.
11699 */
11700void SessionMachine::uninit(Uninit::Reason aReason)
11701{
11702 LogFlowThisFuncEnter();
11703 LogFlowThisFunc(("reason=%d\n", aReason));
11704
11705 /*
11706 * Strongly reference ourselves to prevent this object deletion after
11707 * mData->mSession.mMachine.setNull() below (which can release the last
11708 * reference and call the destructor). Important: this must be done before
11709 * accessing any members (and before AutoUninitSpan that does it as well).
11710 * This self reference will be released as the very last step on return.
11711 */
11712 ComObjPtr<SessionMachine> selfRef = this;
11713
11714 /* Enclose the state transition Ready->InUninit->NotReady */
11715 AutoUninitSpan autoUninitSpan(this);
11716 if (autoUninitSpan.uninitDone())
11717 {
11718 LogFlowThisFunc(("Already uninitialized\n"));
11719 LogFlowThisFuncLeave();
11720 return;
11721 }
11722
11723 if (autoUninitSpan.initFailed())
11724 {
11725 /* We've been called by init() because it's failed. It's not really
11726 * necessary (nor it's safe) to perform the regular uninit sequence
11727 * below, the following is enough.
11728 */
11729 LogFlowThisFunc(("Initialization failed.\n"));
11730#if defined(RT_OS_WINDOWS)
11731 if (mIPCSem)
11732 ::CloseHandle(mIPCSem);
11733 mIPCSem = NULL;
11734#elif defined(RT_OS_OS2)
11735 if (mIPCSem != NULLHANDLE)
11736 ::DosCloseMutexSem(mIPCSem);
11737 mIPCSem = NULLHANDLE;
11738#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11739 if (mIPCSem >= 0)
11740 ::semctl(mIPCSem, 0, IPC_RMID);
11741 mIPCSem = -1;
11742# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11743 mIPCKey = "0";
11744# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
11745#else
11746# error "Port me!"
11747#endif
11748 uninitDataAndChildObjects();
11749 mData.free();
11750 unconst(mParent) = NULL;
11751 unconst(mPeer) = NULL;
11752 LogFlowThisFuncLeave();
11753 return;
11754 }
11755
11756 MachineState_T lastState;
11757 {
11758 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
11759 lastState = mData->mMachineState;
11760 }
11761 NOREF(lastState);
11762
11763#ifdef VBOX_WITH_USB
11764 // release all captured USB devices, but do this before requesting the locks below
11765 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
11766 {
11767 /* Console::captureUSBDevices() is called in the VM process only after
11768 * setting the machine state to Starting or Restoring.
11769 * Console::detachAllUSBDevices() will be called upon successful
11770 * termination. So, we need to release USB devices only if there was
11771 * an abnormal termination of a running VM.
11772 *
11773 * This is identical to SessionMachine::DetachAllUSBDevices except
11774 * for the aAbnormal argument. */
11775 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
11776 AssertComRC(rc);
11777 NOREF(rc);
11778
11779 USBProxyService *service = mParent->host()->usbProxyService();
11780 if (service)
11781 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
11782 }
11783#endif /* VBOX_WITH_USB */
11784
11785 // we need to lock this object in uninit() because the lock is shared
11786 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
11787 // and others need mParent lock, and USB needs host lock.
11788 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
11789
11790#if 0
11791 // Trigger async cleanup tasks, avoid doing things here which are not
11792 // vital to be done immediately and maybe need more locks. This calls
11793 // Machine::unregisterMetrics().
11794 mParent->onMachineUninit(mPeer);
11795#else
11796 /*
11797 * It is safe to call Machine::unregisterMetrics() here because
11798 * PerformanceCollector::samplerCallback no longer accesses guest methods
11799 * holding the lock.
11800 */
11801 unregisterMetrics(mParent->performanceCollector(), mPeer);
11802#endif
11803 /* The guest must be unregistered after its metrics (@bugref{5949}). */
11804 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11805 this, __PRETTY_FUNCTION__, mCollectorGuest));
11806 if (mCollectorGuest)
11807 {
11808 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
11809 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
11810 mCollectorGuest = NULL;
11811 }
11812
11813 if (aReason == Uninit::Abnormal)
11814 {
11815 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
11816 Global::IsOnlineOrTransient(lastState)));
11817
11818 /* reset the state to Aborted */
11819 if (mData->mMachineState != MachineState_Aborted)
11820 setMachineState(MachineState_Aborted);
11821 }
11822
11823 // any machine settings modified?
11824 if (mData->flModifications)
11825 {
11826 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
11827 rollback(false /* aNotify */);
11828 }
11829
11830 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
11831 || !mConsoleTaskData.mSnapshot);
11832 if (!mConsoleTaskData.strStateFilePath.isEmpty())
11833 {
11834 LogWarningThisFunc(("canceling failed save state request!\n"));
11835 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
11836 }
11837 else if (!mConsoleTaskData.mSnapshot.isNull())
11838 {
11839 LogWarningThisFunc(("canceling untaken snapshot!\n"));
11840
11841 /* delete all differencing hard disks created (this will also attach
11842 * their parents back by rolling back mMediaData) */
11843 rollbackMedia();
11844
11845 // delete the saved state file (it might have been already created)
11846 // AFTER killing the snapshot so that releaseSavedStateFile() won't
11847 // think it's still in use
11848 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
11849 mConsoleTaskData.mSnapshot->uninit();
11850 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
11851 }
11852
11853 if (!mData->mSession.mType.isEmpty())
11854 {
11855 /* mType is not null when this machine's process has been started by
11856 * Machine::LaunchVMProcess(), therefore it is our child. We
11857 * need to queue the PID to reap the process (and avoid zombies on
11858 * Linux). */
11859 Assert(mData->mSession.mPID != NIL_RTPROCESS);
11860 mParent->addProcessToReap(mData->mSession.mPID);
11861 }
11862
11863 mData->mSession.mPID = NIL_RTPROCESS;
11864
11865 if (aReason == Uninit::Unexpected)
11866 {
11867 /* Uninitialization didn't come from #checkForDeath(), so tell the
11868 * client watcher thread to update the set of machines that have open
11869 * sessions. */
11870 mParent->updateClientWatcher();
11871 }
11872
11873 /* uninitialize all remote controls */
11874 if (mData->mSession.mRemoteControls.size())
11875 {
11876 LogFlowThisFunc(("Closing remote sessions (%d):\n",
11877 mData->mSession.mRemoteControls.size()));
11878
11879 Data::Session::RemoteControlList::iterator it =
11880 mData->mSession.mRemoteControls.begin();
11881 while (it != mData->mSession.mRemoteControls.end())
11882 {
11883 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
11884 HRESULT rc = (*it)->Uninitialize();
11885 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
11886 if (FAILED(rc))
11887 LogWarningThisFunc(("Forgot to close the remote session?\n"));
11888 ++it;
11889 }
11890 mData->mSession.mRemoteControls.clear();
11891 }
11892
11893 /*
11894 * An expected uninitialization can come only from #checkForDeath().
11895 * Otherwise it means that something's gone really wrong (for example,
11896 * the Session implementation has released the VirtualBox reference
11897 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
11898 * etc). However, it's also possible, that the client releases the IPC
11899 * semaphore correctly (i.e. before it releases the VirtualBox reference),
11900 * but the VirtualBox release event comes first to the server process.
11901 * This case is practically possible, so we should not assert on an
11902 * unexpected uninit, just log a warning.
11903 */
11904
11905 if ((aReason == Uninit::Unexpected))
11906 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
11907
11908 if (aReason != Uninit::Normal)
11909 {
11910 mData->mSession.mDirectControl.setNull();
11911 }
11912 else
11913 {
11914 /* this must be null here (see #OnSessionEnd()) */
11915 Assert(mData->mSession.mDirectControl.isNull());
11916 Assert(mData->mSession.mState == SessionState_Unlocking);
11917 Assert(!mData->mSession.mProgress.isNull());
11918 }
11919 if (mData->mSession.mProgress)
11920 {
11921 if (aReason == Uninit::Normal)
11922 mData->mSession.mProgress->notifyComplete(S_OK);
11923 else
11924 mData->mSession.mProgress->notifyComplete(E_FAIL,
11925 COM_IIDOF(ISession),
11926 getComponentName(),
11927 tr("The VM session was aborted"));
11928 mData->mSession.mProgress.setNull();
11929 }
11930
11931 /* remove the association between the peer machine and this session machine */
11932 Assert( (SessionMachine*)mData->mSession.mMachine == this
11933 || aReason == Uninit::Unexpected);
11934
11935 /* reset the rest of session data */
11936 mData->mSession.mMachine.setNull();
11937 mData->mSession.mState = SessionState_Unlocked;
11938 mData->mSession.mType.setNull();
11939
11940 /* close the interprocess semaphore before leaving the exclusive lock */
11941#if defined(RT_OS_WINDOWS)
11942 if (mIPCSem)
11943 ::CloseHandle(mIPCSem);
11944 mIPCSem = NULL;
11945#elif defined(RT_OS_OS2)
11946 if (mIPCSem != NULLHANDLE)
11947 ::DosCloseMutexSem(mIPCSem);
11948 mIPCSem = NULLHANDLE;
11949#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11950 if (mIPCSem >= 0)
11951 ::semctl(mIPCSem, 0, IPC_RMID);
11952 mIPCSem = -1;
11953# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11954 mIPCKey = "0";
11955# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
11956#else
11957# error "Port me!"
11958#endif
11959
11960 /* fire an event */
11961 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
11962
11963 uninitDataAndChildObjects();
11964
11965 /* free the essential data structure last */
11966 mData.free();
11967
11968 /* release the exclusive lock before setting the below two to NULL */
11969 multilock.release();
11970
11971 unconst(mParent) = NULL;
11972 unconst(mPeer) = NULL;
11973
11974 LogFlowThisFuncLeave();
11975}
11976
11977// util::Lockable interface
11978////////////////////////////////////////////////////////////////////////////////
11979
11980/**
11981 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
11982 * with the primary Machine instance (mPeer).
11983 */
11984RWLockHandle *SessionMachine::lockHandle() const
11985{
11986 AssertReturn(mPeer != NULL, NULL);
11987 return mPeer->lockHandle();
11988}
11989
11990// IInternalMachineControl methods
11991////////////////////////////////////////////////////////////////////////////////
11992
11993/**
11994 * Passes collected guest statistics to performance collector object
11995 */
11996STDMETHODIMP SessionMachine::ReportGuestStatistics(ULONG aValidStats, ULONG aCpuUser,
11997 ULONG aCpuKernel, ULONG aCpuIdle,
11998 ULONG aMemTotal, ULONG aMemFree,
11999 ULONG aMemBalloon, ULONG aMemShared,
12000 ULONG aMemCache, ULONG aPageTotal,
12001 ULONG aAllocVMM, ULONG aFreeVMM,
12002 ULONG aBalloonedVMM, ULONG aSharedVMM)
12003{
12004 if (mCollectorGuest)
12005 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12006 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12007 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12008 aBalloonedVMM, aSharedVMM);
12009
12010 return S_OK;
12011}
12012
12013/**
12014 * @note Locks this object for writing.
12015 */
12016STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12017{
12018 AutoCaller autoCaller(this);
12019 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12020
12021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12022
12023 mRemoveSavedState = aRemove;
12024
12025 return S_OK;
12026}
12027
12028/**
12029 * @note Locks the same as #setMachineState() does.
12030 */
12031STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12032{
12033 return setMachineState(aMachineState);
12034}
12035
12036/**
12037 * @note Locks this object for reading.
12038 */
12039STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
12040{
12041 AutoCaller autoCaller(this);
12042 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12043
12044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12045
12046#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
12047 mIPCSemName.cloneTo(aId);
12048 return S_OK;
12049#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12050# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12051 mIPCKey.cloneTo(aId);
12052# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12053 mData->m_strConfigFileFull.cloneTo(aId);
12054# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12055 return S_OK;
12056#else
12057# error "Port me!"
12058#endif
12059}
12060
12061/**
12062 * @note Locks this object for writing.
12063 */
12064STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12065{
12066 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12067 AutoCaller autoCaller(this);
12068 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12069
12070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12071
12072 if (mData->mSession.mState != SessionState_Locked)
12073 return VBOX_E_INVALID_OBJECT_STATE;
12074
12075 if (!mData->mSession.mProgress.isNull())
12076 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12077
12078 LogFlowThisFunc(("returns S_OK.\n"));
12079 return S_OK;
12080}
12081
12082/**
12083 * @note Locks this object for writing.
12084 */
12085STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12086{
12087 AutoCaller autoCaller(this);
12088 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12089
12090 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12091
12092 if (mData->mSession.mState != SessionState_Locked)
12093 return VBOX_E_INVALID_OBJECT_STATE;
12094
12095 /* Finalize the LaunchVMProcess progress object. */
12096 if (mData->mSession.mProgress)
12097 {
12098 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12099 mData->mSession.mProgress.setNull();
12100 }
12101
12102 if (SUCCEEDED((HRESULT)iResult))
12103 {
12104#ifdef VBOX_WITH_RESOURCE_USAGE_API
12105 /* The VM has been powered up successfully, so it makes sense
12106 * now to offer the performance metrics for a running machine
12107 * object. Doing it earlier wouldn't be safe. */
12108 registerMetrics(mParent->performanceCollector(), mPeer,
12109 mData->mSession.mPID);
12110#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12111 }
12112
12113 return S_OK;
12114}
12115
12116/**
12117 * @note Locks this object for writing.
12118 */
12119STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12120{
12121 LogFlowThisFuncEnter();
12122
12123 CheckComArgOutPointerValid(aProgress);
12124
12125 AutoCaller autoCaller(this);
12126 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12127
12128 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12129
12130 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12131 E_FAIL);
12132
12133 /* create a progress object to track operation completion */
12134 ComObjPtr<Progress> pProgress;
12135 pProgress.createObject();
12136 pProgress->init(getVirtualBox(),
12137 static_cast<IMachine *>(this) /* aInitiator */,
12138 Bstr(tr("Stopping the virtual machine")).raw(),
12139 FALSE /* aCancelable */);
12140
12141 /* fill in the console task data */
12142 mConsoleTaskData.mLastState = mData->mMachineState;
12143 mConsoleTaskData.mProgress = pProgress;
12144
12145 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12146 setMachineState(MachineState_Stopping);
12147
12148 pProgress.queryInterfaceTo(aProgress);
12149
12150 return S_OK;
12151}
12152
12153/**
12154 * @note Locks this object for writing.
12155 */
12156STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12157{
12158 LogFlowThisFuncEnter();
12159
12160 AutoCaller autoCaller(this);
12161 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12162
12163 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12164
12165 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12166 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12167 && mConsoleTaskData.mLastState != MachineState_Null,
12168 E_FAIL);
12169
12170 /*
12171 * On failure, set the state to the state we had when BeginPoweringDown()
12172 * was called (this is expected by Console::PowerDown() and the associated
12173 * task). On success the VM process already changed the state to
12174 * MachineState_PoweredOff, so no need to do anything.
12175 */
12176 if (FAILED(iResult))
12177 setMachineState(mConsoleTaskData.mLastState);
12178
12179 /* notify the progress object about operation completion */
12180 Assert(mConsoleTaskData.mProgress);
12181 if (SUCCEEDED(iResult))
12182 mConsoleTaskData.mProgress->notifyComplete(S_OK);
12183 else
12184 {
12185 Utf8Str strErrMsg(aErrMsg);
12186 if (strErrMsg.length())
12187 mConsoleTaskData.mProgress->notifyComplete(iResult,
12188 COM_IIDOF(ISession),
12189 getComponentName(),
12190 strErrMsg.c_str());
12191 else
12192 mConsoleTaskData.mProgress->notifyComplete(iResult);
12193 }
12194
12195 /* clear out the temporary saved state data */
12196 mConsoleTaskData.mLastState = MachineState_Null;
12197 mConsoleTaskData.mProgress.setNull();
12198
12199 LogFlowThisFuncLeave();
12200 return S_OK;
12201}
12202
12203
12204/**
12205 * Goes through the USB filters of the given machine to see if the given
12206 * device matches any filter or not.
12207 *
12208 * @note Locks the same as USBController::hasMatchingFilter() does.
12209 */
12210STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12211 BOOL *aMatched,
12212 ULONG *aMaskedIfs)
12213{
12214 LogFlowThisFunc(("\n"));
12215
12216 CheckComArgNotNull(aUSBDevice);
12217 CheckComArgOutPointerValid(aMatched);
12218
12219 AutoCaller autoCaller(this);
12220 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12221
12222#ifdef VBOX_WITH_USB
12223 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
12224#else
12225 NOREF(aUSBDevice);
12226 NOREF(aMaskedIfs);
12227 *aMatched = FALSE;
12228#endif
12229
12230 return S_OK;
12231}
12232
12233/**
12234 * @note Locks the same as Host::captureUSBDevice() does.
12235 */
12236STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12237{
12238 LogFlowThisFunc(("\n"));
12239
12240 AutoCaller autoCaller(this);
12241 AssertComRCReturnRC(autoCaller.rc());
12242
12243#ifdef VBOX_WITH_USB
12244 /* if captureDeviceForVM() fails, it must have set extended error info */
12245 clearError();
12246 MultiResult rc = mParent->host()->checkUSBProxyService();
12247 if (FAILED(rc)) return rc;
12248
12249 USBProxyService *service = mParent->host()->usbProxyService();
12250 AssertReturn(service, E_FAIL);
12251 return service->captureDeviceForVM(this, Guid(aId).ref());
12252#else
12253 NOREF(aId);
12254 return E_NOTIMPL;
12255#endif
12256}
12257
12258/**
12259 * @note Locks the same as Host::detachUSBDevice() does.
12260 */
12261STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12262{
12263 LogFlowThisFunc(("\n"));
12264
12265 AutoCaller autoCaller(this);
12266 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12267
12268#ifdef VBOX_WITH_USB
12269 USBProxyService *service = mParent->host()->usbProxyService();
12270 AssertReturn(service, E_FAIL);
12271 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12272#else
12273 NOREF(aId);
12274 NOREF(aDone);
12275 return E_NOTIMPL;
12276#endif
12277}
12278
12279/**
12280 * Inserts all machine filters to the USB proxy service and then calls
12281 * Host::autoCaptureUSBDevices().
12282 *
12283 * Called by Console from the VM process upon VM startup.
12284 *
12285 * @note Locks what called methods lock.
12286 */
12287STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12288{
12289 LogFlowThisFunc(("\n"));
12290
12291 AutoCaller autoCaller(this);
12292 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12293
12294#ifdef VBOX_WITH_USB
12295 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
12296 AssertComRC(rc);
12297 NOREF(rc);
12298
12299 USBProxyService *service = mParent->host()->usbProxyService();
12300 AssertReturn(service, E_FAIL);
12301 return service->autoCaptureDevicesForVM(this);
12302#else
12303 return S_OK;
12304#endif
12305}
12306
12307/**
12308 * Removes all machine filters from the USB proxy service and then calls
12309 * Host::detachAllUSBDevices().
12310 *
12311 * Called by Console from the VM process upon normal VM termination or by
12312 * SessionMachine::uninit() upon abnormal VM termination (from under the
12313 * Machine/SessionMachine lock).
12314 *
12315 * @note Locks what called methods lock.
12316 */
12317STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12318{
12319 LogFlowThisFunc(("\n"));
12320
12321 AutoCaller autoCaller(this);
12322 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12323
12324#ifdef VBOX_WITH_USB
12325 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12326 AssertComRC(rc);
12327 NOREF(rc);
12328
12329 USBProxyService *service = mParent->host()->usbProxyService();
12330 AssertReturn(service, E_FAIL);
12331 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12332#else
12333 NOREF(aDone);
12334 return S_OK;
12335#endif
12336}
12337
12338/**
12339 * @note Locks this object for writing.
12340 */
12341STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12342 IProgress **aProgress)
12343{
12344 LogFlowThisFuncEnter();
12345
12346 AssertReturn(aSession, E_INVALIDARG);
12347 AssertReturn(aProgress, E_INVALIDARG);
12348
12349 AutoCaller autoCaller(this);
12350
12351 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
12352 /*
12353 * We don't assert below because it might happen that a non-direct session
12354 * informs us it is closed right after we've been uninitialized -- it's ok.
12355 */
12356 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12357
12358 /* get IInternalSessionControl interface */
12359 ComPtr<IInternalSessionControl> control(aSession);
12360
12361 ComAssertRet(!control.isNull(), E_INVALIDARG);
12362
12363 /* Creating a Progress object requires the VirtualBox lock, and
12364 * thus locking it here is required by the lock order rules. */
12365 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12366
12367 if (control == mData->mSession.mDirectControl)
12368 {
12369 ComAssertRet(aProgress, E_POINTER);
12370
12371 /* The direct session is being normally closed by the client process
12372 * ----------------------------------------------------------------- */
12373
12374 /* go to the closing state (essential for all open*Session() calls and
12375 * for #checkForDeath()) */
12376 Assert(mData->mSession.mState == SessionState_Locked);
12377 mData->mSession.mState = SessionState_Unlocking;
12378
12379 /* set direct control to NULL to release the remote instance */
12380 mData->mSession.mDirectControl.setNull();
12381 LogFlowThisFunc(("Direct control is set to NULL\n"));
12382
12383 if (mData->mSession.mProgress)
12384 {
12385 /* finalize the progress, someone might wait if a frontend
12386 * closes the session before powering on the VM. */
12387 mData->mSession.mProgress->notifyComplete(E_FAIL,
12388 COM_IIDOF(ISession),
12389 getComponentName(),
12390 tr("The VM session was closed before any attempt to power it on"));
12391 mData->mSession.mProgress.setNull();
12392 }
12393
12394 /* Create the progress object the client will use to wait until
12395 * #checkForDeath() is called to uninitialize this session object after
12396 * it releases the IPC semaphore.
12397 * Note! Because we're "reusing" mProgress here, this must be a proxy
12398 * object just like for LaunchVMProcess. */
12399 Assert(mData->mSession.mProgress.isNull());
12400 ComObjPtr<ProgressProxy> progress;
12401 progress.createObject();
12402 ComPtr<IUnknown> pPeer(mPeer);
12403 progress->init(mParent, pPeer,
12404 Bstr(tr("Closing session")).raw(),
12405 FALSE /* aCancelable */);
12406 progress.queryInterfaceTo(aProgress);
12407 mData->mSession.mProgress = progress;
12408 }
12409 else
12410 {
12411 /* the remote session is being normally closed */
12412 Data::Session::RemoteControlList::iterator it =
12413 mData->mSession.mRemoteControls.begin();
12414 while (it != mData->mSession.mRemoteControls.end())
12415 {
12416 if (control == *it)
12417 break;
12418 ++it;
12419 }
12420 BOOL found = it != mData->mSession.mRemoteControls.end();
12421 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12422 E_INVALIDARG);
12423 // This MUST be erase(it), not remove(*it) as the latter triggers a
12424 // very nasty use after free due to the place where the value "lives".
12425 mData->mSession.mRemoteControls.erase(it);
12426 }
12427
12428 /* signal the client watcher thread, because the client is going away */
12429 mParent->updateClientWatcher();
12430
12431 LogFlowThisFuncLeave();
12432 return S_OK;
12433}
12434
12435/**
12436 * @note Locks this object for writing.
12437 */
12438STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12439{
12440 LogFlowThisFuncEnter();
12441
12442 CheckComArgOutPointerValid(aProgress);
12443 CheckComArgOutPointerValid(aStateFilePath);
12444
12445 AutoCaller autoCaller(this);
12446 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12447
12448 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12449
12450 AssertReturn( mData->mMachineState == MachineState_Paused
12451 && mConsoleTaskData.mLastState == MachineState_Null
12452 && mConsoleTaskData.strStateFilePath.isEmpty(),
12453 E_FAIL);
12454
12455 /* create a progress object to track operation completion */
12456 ComObjPtr<Progress> pProgress;
12457 pProgress.createObject();
12458 pProgress->init(getVirtualBox(),
12459 static_cast<IMachine *>(this) /* aInitiator */,
12460 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12461 FALSE /* aCancelable */);
12462
12463 Utf8Str strStateFilePath;
12464 /* stateFilePath is null when the machine is not running */
12465 if (mData->mMachineState == MachineState_Paused)
12466 composeSavedStateFilename(strStateFilePath);
12467
12468 /* fill in the console task data */
12469 mConsoleTaskData.mLastState = mData->mMachineState;
12470 mConsoleTaskData.strStateFilePath = strStateFilePath;
12471 mConsoleTaskData.mProgress = pProgress;
12472
12473 /* set the state to Saving (this is expected by Console::SaveState()) */
12474 setMachineState(MachineState_Saving);
12475
12476 strStateFilePath.cloneTo(aStateFilePath);
12477 pProgress.queryInterfaceTo(aProgress);
12478
12479 return S_OK;
12480}
12481
12482/**
12483 * @note Locks mParent + this object for writing.
12484 */
12485STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12486{
12487 LogFlowThisFunc(("\n"));
12488
12489 AutoCaller autoCaller(this);
12490 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12491
12492 /* endSavingState() need mParent lock */
12493 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12494
12495 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12496 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12497 && mConsoleTaskData.mLastState != MachineState_Null
12498 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12499 E_FAIL);
12500
12501 /*
12502 * On failure, set the state to the state we had when BeginSavingState()
12503 * was called (this is expected by Console::SaveState() and the associated
12504 * task). On success the VM process already changed the state to
12505 * MachineState_Saved, so no need to do anything.
12506 */
12507 if (FAILED(iResult))
12508 setMachineState(mConsoleTaskData.mLastState);
12509
12510 return endSavingState(iResult, aErrMsg);
12511}
12512
12513/**
12514 * @note Locks this object for writing.
12515 */
12516STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12517{
12518 LogFlowThisFunc(("\n"));
12519
12520 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12521
12522 AutoCaller autoCaller(this);
12523 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12524
12525 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12526
12527 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12528 || mData->mMachineState == MachineState_Teleported
12529 || mData->mMachineState == MachineState_Aborted
12530 , E_FAIL); /** @todo setError. */
12531
12532 Utf8Str stateFilePathFull = aSavedStateFile;
12533 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
12534 if (RT_FAILURE(vrc))
12535 return setError(VBOX_E_FILE_ERROR,
12536 tr("Invalid saved state file path '%ls' (%Rrc)"),
12537 aSavedStateFile,
12538 vrc);
12539
12540 mSSData->strStateFilePath = stateFilePathFull;
12541
12542 /* The below setMachineState() will detect the state transition and will
12543 * update the settings file */
12544
12545 return setMachineState(MachineState_Saved);
12546}
12547
12548STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
12549 ComSafeArrayOut(BSTR, aValues),
12550 ComSafeArrayOut(LONG64, aTimestamps),
12551 ComSafeArrayOut(BSTR, aFlags))
12552{
12553 LogFlowThisFunc(("\n"));
12554
12555#ifdef VBOX_WITH_GUEST_PROPS
12556 using namespace guestProp;
12557
12558 AutoCaller autoCaller(this);
12559 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12560
12561 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12562
12563 CheckComArgOutSafeArrayPointerValid(aNames);
12564 CheckComArgOutSafeArrayPointerValid(aValues);
12565 CheckComArgOutSafeArrayPointerValid(aTimestamps);
12566 CheckComArgOutSafeArrayPointerValid(aFlags);
12567
12568 size_t cEntries = mHWData->mGuestProperties.size();
12569 com::SafeArray<BSTR> names(cEntries);
12570 com::SafeArray<BSTR> values(cEntries);
12571 com::SafeArray<LONG64> timestamps(cEntries);
12572 com::SafeArray<BSTR> flags(cEntries);
12573 unsigned i = 0;
12574 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
12575 it != mHWData->mGuestProperties.end();
12576 ++it)
12577 {
12578 char szFlags[MAX_FLAGS_LEN + 1];
12579 it->strName.cloneTo(&names[i]);
12580 it->strValue.cloneTo(&values[i]);
12581 timestamps[i] = it->mTimestamp;
12582 /* If it is NULL, keep it NULL. */
12583 if (it->mFlags)
12584 {
12585 writeFlags(it->mFlags, szFlags);
12586 Bstr(szFlags).cloneTo(&flags[i]);
12587 }
12588 else
12589 flags[i] = NULL;
12590 ++i;
12591 }
12592 names.detachTo(ComSafeArrayOutArg(aNames));
12593 values.detachTo(ComSafeArrayOutArg(aValues));
12594 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
12595 flags.detachTo(ComSafeArrayOutArg(aFlags));
12596 return S_OK;
12597#else
12598 ReturnComNotImplemented();
12599#endif
12600}
12601
12602STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
12603 IN_BSTR aValue,
12604 LONG64 aTimestamp,
12605 IN_BSTR aFlags)
12606{
12607 LogFlowThisFunc(("\n"));
12608
12609#ifdef VBOX_WITH_GUEST_PROPS
12610 using namespace guestProp;
12611
12612 CheckComArgStrNotEmptyOrNull(aName);
12613 CheckComArgNotNull(aValue);
12614 CheckComArgNotNull(aFlags);
12615
12616 try
12617 {
12618 /*
12619 * Convert input up front.
12620 */
12621 Utf8Str utf8Name(aName);
12622 uint32_t fFlags = NILFLAG;
12623 if (aFlags)
12624 {
12625 Utf8Str utf8Flags(aFlags);
12626 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
12627 AssertRCReturn(vrc, E_INVALIDARG);
12628 }
12629
12630 /*
12631 * Now grab the object lock, validate the state and do the update.
12632 */
12633 AutoCaller autoCaller(this);
12634 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12635
12636 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12637
12638 switch (mData->mMachineState)
12639 {
12640 case MachineState_Paused:
12641 case MachineState_Running:
12642 case MachineState_Teleporting:
12643 case MachineState_TeleportingPausedVM:
12644 case MachineState_LiveSnapshotting:
12645 case MachineState_DeletingSnapshotOnline:
12646 case MachineState_DeletingSnapshotPaused:
12647 case MachineState_Saving:
12648 break;
12649
12650 default:
12651#ifndef DEBUG_sunlover
12652 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
12653 VBOX_E_INVALID_VM_STATE);
12654#else
12655 return VBOX_E_INVALID_VM_STATE;
12656#endif
12657 }
12658
12659 setModified(IsModified_MachineData);
12660 mHWData.backup();
12661
12662 /** @todo r=bird: The careful memory handling doesn't work out here because
12663 * the catch block won't undo any damage we've done. So, if push_back throws
12664 * bad_alloc then you've lost the value.
12665 *
12666 * Another thing. Doing a linear search here isn't extremely efficient, esp.
12667 * since values that changes actually bubbles to the end of the list. Using
12668 * something that has an efficient lookup and can tolerate a bit of updates
12669 * would be nice. RTStrSpace is one suggestion (it's not perfect). Some
12670 * combination of RTStrCache (for sharing names and getting uniqueness into
12671 * the bargain) and hash/tree is another. */
12672 for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();
12673 iter != mHWData->mGuestProperties.end();
12674 ++iter)
12675 if (utf8Name == iter->strName)
12676 {
12677 mHWData->mGuestProperties.erase(iter);
12678 mData->mGuestPropertiesModified = TRUE;
12679 break;
12680 }
12681 if (aValue != NULL)
12682 {
12683 HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };
12684 mHWData->mGuestProperties.push_back(property);
12685 mData->mGuestPropertiesModified = TRUE;
12686 }
12687
12688 /*
12689 * Send a callback notification if appropriate
12690 */
12691 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
12692 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
12693 RTSTR_MAX,
12694 utf8Name.c_str(),
12695 RTSTR_MAX, NULL)
12696 )
12697 {
12698 alock.release();
12699
12700 mParent->onGuestPropertyChange(mData->mUuid,
12701 aName,
12702 aValue,
12703 aFlags);
12704 }
12705 }
12706 catch (...)
12707 {
12708 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
12709 }
12710 return S_OK;
12711#else
12712 ReturnComNotImplemented();
12713#endif
12714}
12715
12716STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
12717 IMediumAttachment **aNewAttachment)
12718{
12719 CheckComArgNotNull(aAttachment);
12720 CheckComArgOutPointerValid(aNewAttachment);
12721
12722 AutoCaller autoCaller(this);
12723 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12724
12725 // request the host lock first, since might be calling Host methods for getting host drives;
12726 // next, protect the media tree all the while we're in here, as well as our member variables
12727 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
12728 this->lockHandle(),
12729 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
12730
12731 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
12732
12733 Bstr ctrlName;
12734 LONG lPort;
12735 LONG lDevice;
12736 bool fTempEject;
12737 {
12738 AutoCaller autoAttachCaller(this);
12739 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12740
12741 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12742
12743 /* Need to query the details first, as the IMediumAttachment reference
12744 * might be to the original settings, which we are going to change. */
12745 ctrlName = pAttach->getControllerName();
12746 lPort = pAttach->getPort();
12747 lDevice = pAttach->getDevice();
12748 fTempEject = pAttach->getTempEject();
12749 }
12750
12751 if (!fTempEject)
12752 {
12753 /* Remember previously mounted medium. The medium before taking the
12754 * backup is not necessarily the same thing. */
12755 ComObjPtr<Medium> oldmedium;
12756 oldmedium = pAttach->getMedium();
12757
12758 setModified(IsModified_Storage);
12759 mMediaData.backup();
12760
12761 // The backup operation makes the pAttach reference point to the
12762 // old settings. Re-get the correct reference.
12763 pAttach = findAttachment(mMediaData->mAttachments,
12764 ctrlName.raw(),
12765 lPort,
12766 lDevice);
12767
12768 {
12769 AutoCaller autoAttachCaller(this);
12770 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12771
12772 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12773 if (!oldmedium.isNull())
12774 oldmedium->removeBackReference(mData->mUuid);
12775
12776 pAttach->updateMedium(NULL);
12777 pAttach->updateEjected();
12778 }
12779
12780 setModified(IsModified_Storage);
12781 }
12782 else
12783 {
12784 {
12785 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12786 pAttach->updateEjected();
12787 }
12788 }
12789
12790 pAttach.queryInterfaceTo(aNewAttachment);
12791
12792 return S_OK;
12793}
12794
12795// public methods only for internal purposes
12796/////////////////////////////////////////////////////////////////////////////
12797
12798/**
12799 * Called from the client watcher thread to check for expected or unexpected
12800 * death of the client process that has a direct session to this machine.
12801 *
12802 * On Win32 and on OS/2, this method is called only when we've got the
12803 * mutex (i.e. the client has either died or terminated normally) so it always
12804 * returns @c true (the client is terminated, the session machine is
12805 * uninitialized).
12806 *
12807 * On other platforms, the method returns @c true if the client process has
12808 * terminated normally or abnormally and the session machine was uninitialized,
12809 * and @c false if the client process is still alive.
12810 *
12811 * @note Locks this object for writing.
12812 */
12813bool SessionMachine::checkForDeath()
12814{
12815 Uninit::Reason reason;
12816 bool terminated = false;
12817
12818 /* Enclose autoCaller with a block because calling uninit() from under it
12819 * will deadlock. */
12820 {
12821 AutoCaller autoCaller(this);
12822 if (!autoCaller.isOk())
12823 {
12824 /* return true if not ready, to cause the client watcher to exclude
12825 * the corresponding session from watching */
12826 LogFlowThisFunc(("Already uninitialized!\n"));
12827 return true;
12828 }
12829
12830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12831
12832 /* Determine the reason of death: if the session state is Closing here,
12833 * everything is fine. Otherwise it means that the client did not call
12834 * OnSessionEnd() before it released the IPC semaphore. This may happen
12835 * either because the client process has abnormally terminated, or
12836 * because it simply forgot to call ISession::Close() before exiting. We
12837 * threat the latter also as an abnormal termination (see
12838 * Session::uninit() for details). */
12839 reason = mData->mSession.mState == SessionState_Unlocking ?
12840 Uninit::Normal :
12841 Uninit::Abnormal;
12842
12843#if defined(RT_OS_WINDOWS)
12844
12845 AssertMsg(mIPCSem, ("semaphore must be created"));
12846
12847 /* release the IPC mutex */
12848 ::ReleaseMutex(mIPCSem);
12849
12850 terminated = true;
12851
12852#elif defined(RT_OS_OS2)
12853
12854 AssertMsg(mIPCSem, ("semaphore must be created"));
12855
12856 /* release the IPC mutex */
12857 ::DosReleaseMutexSem(mIPCSem);
12858
12859 terminated = true;
12860
12861#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12862
12863 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
12864
12865 int val = ::semctl(mIPCSem, 0, GETVAL);
12866 if (val > 0)
12867 {
12868 /* the semaphore is signaled, meaning the session is terminated */
12869 terminated = true;
12870 }
12871
12872#else
12873# error "Port me!"
12874#endif
12875
12876 } /* AutoCaller block */
12877
12878 if (terminated)
12879 uninit(reason);
12880
12881 return terminated;
12882}
12883
12884/**
12885 * @note Locks this object for reading.
12886 */
12887HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
12888{
12889 LogFlowThisFunc(("\n"));
12890
12891 AutoCaller autoCaller(this);
12892 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12893
12894 ComPtr<IInternalSessionControl> directControl;
12895 {
12896 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12897 directControl = mData->mSession.mDirectControl;
12898 }
12899
12900 /* ignore notifications sent after #OnSessionEnd() is called */
12901 if (!directControl)
12902 return S_OK;
12903
12904 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
12905}
12906
12907/**
12908 * @note Locks this object for reading.
12909 */
12910HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
12911 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
12912{
12913 LogFlowThisFunc(("\n"));
12914
12915 AutoCaller autoCaller(this);
12916 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12917
12918 ComPtr<IInternalSessionControl> directControl;
12919 {
12920 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12921 directControl = mData->mSession.mDirectControl;
12922 }
12923
12924 /* ignore notifications sent after #OnSessionEnd() is called */
12925 if (!directControl)
12926 return S_OK;
12927 /*
12928 * instead acting like callback we ask IVirtualBox deliver corresponding event
12929 */
12930
12931 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
12932 return S_OK;
12933}
12934
12935/**
12936 * @note Locks this object for reading.
12937 */
12938HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
12939{
12940 LogFlowThisFunc(("\n"));
12941
12942 AutoCaller autoCaller(this);
12943 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12944
12945 ComPtr<IInternalSessionControl> directControl;
12946 {
12947 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12948 directControl = mData->mSession.mDirectControl;
12949 }
12950
12951 /* ignore notifications sent after #OnSessionEnd() is called */
12952 if (!directControl)
12953 return S_OK;
12954
12955 return directControl->OnSerialPortChange(serialPort);
12956}
12957
12958/**
12959 * @note Locks this object for reading.
12960 */
12961HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
12962{
12963 LogFlowThisFunc(("\n"));
12964
12965 AutoCaller autoCaller(this);
12966 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12967
12968 ComPtr<IInternalSessionControl> directControl;
12969 {
12970 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12971 directControl = mData->mSession.mDirectControl;
12972 }
12973
12974 /* ignore notifications sent after #OnSessionEnd() is called */
12975 if (!directControl)
12976 return S_OK;
12977
12978 return directControl->OnParallelPortChange(parallelPort);
12979}
12980
12981/**
12982 * @note Locks this object for reading.
12983 */
12984HRESULT SessionMachine::onStorageControllerChange()
12985{
12986 LogFlowThisFunc(("\n"));
12987
12988 AutoCaller autoCaller(this);
12989 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12990
12991 ComPtr<IInternalSessionControl> directControl;
12992 {
12993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12994 directControl = mData->mSession.mDirectControl;
12995 }
12996
12997 /* ignore notifications sent after #OnSessionEnd() is called */
12998 if (!directControl)
12999 return S_OK;
13000
13001 return directControl->OnStorageControllerChange();
13002}
13003
13004/**
13005 * @note Locks this object for reading.
13006 */
13007HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13008{
13009 LogFlowThisFunc(("\n"));
13010
13011 AutoCaller autoCaller(this);
13012 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13013
13014 ComPtr<IInternalSessionControl> directControl;
13015 {
13016 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13017 directControl = mData->mSession.mDirectControl;
13018 }
13019
13020 /* ignore notifications sent after #OnSessionEnd() is called */
13021 if (!directControl)
13022 return S_OK;
13023
13024 return directControl->OnMediumChange(aAttachment, aForce);
13025}
13026
13027/**
13028 * @note Locks this object for reading.
13029 */
13030HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
13031{
13032 LogFlowThisFunc(("\n"));
13033
13034 AutoCaller autoCaller(this);
13035 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
13036
13037 ComPtr<IInternalSessionControl> directControl;
13038 {
13039 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13040 directControl = mData->mSession.mDirectControl;
13041 }
13042
13043 /* ignore notifications sent after #OnSessionEnd() is called */
13044 if (!directControl)
13045 return S_OK;
13046
13047 return directControl->OnCPUChange(aCPU, aRemove);
13048}
13049
13050HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
13051{
13052 LogFlowThisFunc(("\n"));
13053
13054 AutoCaller autoCaller(this);
13055 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
13056
13057 ComPtr<IInternalSessionControl> directControl;
13058 {
13059 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13060 directControl = mData->mSession.mDirectControl;
13061 }
13062
13063 /* ignore notifications sent after #OnSessionEnd() is called */
13064 if (!directControl)
13065 return S_OK;
13066
13067 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13068}
13069
13070/**
13071 * @note Locks this object for reading.
13072 */
13073HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
13074{
13075 LogFlowThisFunc(("\n"));
13076
13077 AutoCaller autoCaller(this);
13078 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13079
13080 ComPtr<IInternalSessionControl> directControl;
13081 {
13082 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13083 directControl = mData->mSession.mDirectControl;
13084 }
13085
13086 /* ignore notifications sent after #OnSessionEnd() is called */
13087 if (!directControl)
13088 return S_OK;
13089
13090 return directControl->OnVRDEServerChange(aRestart);
13091}
13092
13093/**
13094 * @note Locks this object for reading.
13095 */
13096HRESULT SessionMachine::onUSBControllerChange()
13097{
13098 LogFlowThisFunc(("\n"));
13099
13100 AutoCaller autoCaller(this);
13101 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13102
13103 ComPtr<IInternalSessionControl> directControl;
13104 {
13105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13106 directControl = mData->mSession.mDirectControl;
13107 }
13108
13109 /* ignore notifications sent after #OnSessionEnd() is called */
13110 if (!directControl)
13111 return S_OK;
13112
13113 return directControl->OnUSBControllerChange();
13114}
13115
13116/**
13117 * @note Locks this object for reading.
13118 */
13119HRESULT SessionMachine::onSharedFolderChange()
13120{
13121 LogFlowThisFunc(("\n"));
13122
13123 AutoCaller autoCaller(this);
13124 AssertComRCReturnRC(autoCaller.rc());
13125
13126 ComPtr<IInternalSessionControl> directControl;
13127 {
13128 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13129 directControl = mData->mSession.mDirectControl;
13130 }
13131
13132 /* ignore notifications sent after #OnSessionEnd() is called */
13133 if (!directControl)
13134 return S_OK;
13135
13136 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13137}
13138
13139/**
13140 * @note Locks this object for reading.
13141 */
13142HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
13143{
13144 LogFlowThisFunc(("\n"));
13145
13146 AutoCaller autoCaller(this);
13147 AssertComRCReturnRC(autoCaller.rc());
13148
13149 ComPtr<IInternalSessionControl> directControl;
13150 {
13151 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13152 directControl = mData->mSession.mDirectControl;
13153 }
13154
13155 /* ignore notifications sent after #OnSessionEnd() is called */
13156 if (!directControl)
13157 return S_OK;
13158
13159 return directControl->OnClipboardModeChange(aClipboardMode);
13160}
13161
13162/**
13163 * @note Locks this object for reading.
13164 */
13165HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
13166{
13167 LogFlowThisFunc(("\n"));
13168
13169 AutoCaller autoCaller(this);
13170 AssertComRCReturnRC(autoCaller.rc());
13171
13172 ComPtr<IInternalSessionControl> directControl;
13173 {
13174 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13175 directControl = mData->mSession.mDirectControl;
13176 }
13177
13178 /* ignore notifications sent after #OnSessionEnd() is called */
13179 if (!directControl)
13180 return S_OK;
13181
13182 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
13183}
13184
13185/**
13186 * @note Locks this object for reading.
13187 */
13188HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13189{
13190 LogFlowThisFunc(("\n"));
13191
13192 AutoCaller autoCaller(this);
13193 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
13194
13195 ComPtr<IInternalSessionControl> directControl;
13196 {
13197 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13198 directControl = mData->mSession.mDirectControl;
13199 }
13200
13201 /* ignore notifications sent after #OnSessionEnd() is called */
13202 if (!directControl)
13203 return S_OK;
13204
13205 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13206}
13207
13208/**
13209 * @note Locks this object for reading.
13210 */
13211HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove)
13212{
13213 LogFlowThisFunc(("\n"));
13214
13215 AutoCaller autoCaller(this);
13216 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13217
13218 ComPtr<IInternalSessionControl> directControl;
13219 {
13220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13221 directControl = mData->mSession.mDirectControl;
13222 }
13223
13224 /* ignore notifications sent after #OnSessionEnd() is called */
13225 if (!directControl)
13226 return S_OK;
13227
13228 return directControl->OnStorageDeviceChange(aAttachment, aRemove);
13229}
13230
13231/**
13232 * Returns @c true if this machine's USB controller reports it has a matching
13233 * filter for the given USB device and @c false otherwise.
13234 *
13235 * @note locks this object for reading.
13236 */
13237bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13238{
13239 AutoCaller autoCaller(this);
13240 /* silently return if not ready -- this method may be called after the
13241 * direct machine session has been called */
13242 if (!autoCaller.isOk())
13243 return false;
13244
13245#ifdef VBOX_WITH_USB
13246 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13247
13248 switch (mData->mMachineState)
13249 {
13250 case MachineState_Starting:
13251 case MachineState_Restoring:
13252 case MachineState_TeleportingIn:
13253 case MachineState_Paused:
13254 case MachineState_Running:
13255 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13256 * elsewhere... */
13257 alock.release();
13258 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
13259 default: break;
13260 }
13261#else
13262 NOREF(aDevice);
13263 NOREF(aMaskedIfs);
13264#endif
13265 return false;
13266}
13267
13268/**
13269 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13270 */
13271HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
13272 IVirtualBoxErrorInfo *aError,
13273 ULONG aMaskedIfs)
13274{
13275 LogFlowThisFunc(("\n"));
13276
13277 AutoCaller autoCaller(this);
13278
13279 /* This notification may happen after the machine object has been
13280 * uninitialized (the session was closed), so don't assert. */
13281 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13282
13283 ComPtr<IInternalSessionControl> directControl;
13284 {
13285 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13286 directControl = mData->mSession.mDirectControl;
13287 }
13288
13289 /* fail on notifications sent after #OnSessionEnd() is called, it is
13290 * expected by the caller */
13291 if (!directControl)
13292 return E_FAIL;
13293
13294 /* No locks should be held at this point. */
13295 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13296 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13297
13298 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13299}
13300
13301/**
13302 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13303 */
13304HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
13305 IVirtualBoxErrorInfo *aError)
13306{
13307 LogFlowThisFunc(("\n"));
13308
13309 AutoCaller autoCaller(this);
13310
13311 /* This notification may happen after the machine object has been
13312 * uninitialized (the session was closed), so don't assert. */
13313 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13314
13315 ComPtr<IInternalSessionControl> directControl;
13316 {
13317 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13318 directControl = mData->mSession.mDirectControl;
13319 }
13320
13321 /* fail on notifications sent after #OnSessionEnd() is called, it is
13322 * expected by the caller */
13323 if (!directControl)
13324 return E_FAIL;
13325
13326 /* No locks should be held at this point. */
13327 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13328 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13329
13330 return directControl->OnUSBDeviceDetach(aId, aError);
13331}
13332
13333// protected methods
13334/////////////////////////////////////////////////////////////////////////////
13335
13336/**
13337 * Helper method to finalize saving the state.
13338 *
13339 * @note Must be called from under this object's lock.
13340 *
13341 * @param aRc S_OK if the snapshot has been taken successfully
13342 * @param aErrMsg human readable error message for failure
13343 *
13344 * @note Locks mParent + this objects for writing.
13345 */
13346HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13347{
13348 LogFlowThisFuncEnter();
13349
13350 AutoCaller autoCaller(this);
13351 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13352
13353 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13354
13355 HRESULT rc = S_OK;
13356
13357 if (SUCCEEDED(aRc))
13358 {
13359 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13360
13361 /* save all VM settings */
13362 rc = saveSettings(NULL);
13363 // no need to check whether VirtualBox.xml needs saving also since
13364 // we can't have a name change pending at this point
13365 }
13366 else
13367 {
13368 // delete the saved state file (it might have been already created);
13369 // we need not check whether this is shared with a snapshot here because
13370 // we certainly created this saved state file here anew
13371 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13372 }
13373
13374 /* notify the progress object about operation completion */
13375 Assert(mConsoleTaskData.mProgress);
13376 if (SUCCEEDED(aRc))
13377 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13378 else
13379 {
13380 if (aErrMsg.length())
13381 mConsoleTaskData.mProgress->notifyComplete(aRc,
13382 COM_IIDOF(ISession),
13383 getComponentName(),
13384 aErrMsg.c_str());
13385 else
13386 mConsoleTaskData.mProgress->notifyComplete(aRc);
13387 }
13388
13389 /* clear out the temporary saved state data */
13390 mConsoleTaskData.mLastState = MachineState_Null;
13391 mConsoleTaskData.strStateFilePath.setNull();
13392 mConsoleTaskData.mProgress.setNull();
13393
13394 LogFlowThisFuncLeave();
13395 return rc;
13396}
13397
13398/**
13399 * Deletes the given file if it is no longer in use by either the current machine state
13400 * (if the machine is "saved") or any of the machine's snapshots.
13401 *
13402 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13403 * but is different for each SnapshotMachine. When calling this, the order of calling this
13404 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13405 * is therefore critical. I know, it's all rather messy.
13406 *
13407 * @param strStateFile
13408 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
13409 */
13410void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
13411 Snapshot *pSnapshotToIgnore)
13412{
13413 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13414 if ( (strStateFile.isNotEmpty())
13415 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13416 )
13417 // ... and it must also not be shared with other snapshots
13418 if ( !mData->mFirstSnapshot
13419 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13420 // this checks the SnapshotMachine's state file paths
13421 )
13422 RTFileDelete(strStateFile.c_str());
13423}
13424
13425/**
13426 * Locks the attached media.
13427 *
13428 * All attached hard disks are locked for writing and DVD/floppy are locked for
13429 * reading. Parents of attached hard disks (if any) are locked for reading.
13430 *
13431 * This method also performs accessibility check of all media it locks: if some
13432 * media is inaccessible, the method will return a failure and a bunch of
13433 * extended error info objects per each inaccessible medium.
13434 *
13435 * Note that this method is atomic: if it returns a success, all media are
13436 * locked as described above; on failure no media is locked at all (all
13437 * succeeded individual locks will be undone).
13438 *
13439 * This method is intended to be called when the machine is in Starting or
13440 * Restoring state and asserts otherwise.
13441 *
13442 * The locks made by this method must be undone by calling #unlockMedia() when
13443 * no more needed.
13444 */
13445HRESULT SessionMachine::lockMedia()
13446{
13447 AutoCaller autoCaller(this);
13448 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13449
13450 AutoMultiWriteLock2 alock(this->lockHandle(),
13451 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13452
13453 AssertReturn( mData->mMachineState == MachineState_Starting
13454 || mData->mMachineState == MachineState_Restoring
13455 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13456 /* bail out if trying to lock things with already set up locking */
13457 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13458
13459 clearError();
13460 MultiResult mrc(S_OK);
13461
13462 /* Collect locking information for all medium objects attached to the VM. */
13463 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13464 it != mMediaData->mAttachments.end();
13465 ++it)
13466 {
13467 MediumAttachment* pAtt = *it;
13468 DeviceType_T devType = pAtt->getType();
13469 Medium *pMedium = pAtt->getMedium();
13470
13471 MediumLockList *pMediumLockList(new MediumLockList());
13472 // There can be attachments without a medium (floppy/dvd), and thus
13473 // it's impossible to create a medium lock list. It still makes sense
13474 // to have the empty medium lock list in the map in case a medium is
13475 // attached later.
13476 if (pMedium != NULL)
13477 {
13478 MediumType_T mediumType = pMedium->getType();
13479 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13480 || mediumType == MediumType_Shareable;
13481 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13482
13483 alock.release();
13484 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13485 !fIsReadOnlyLock /* fMediumLockWrite */,
13486 NULL,
13487 *pMediumLockList);
13488 alock.acquire();
13489 if (FAILED(mrc))
13490 {
13491 delete pMediumLockList;
13492 mData->mSession.mLockedMedia.Clear();
13493 break;
13494 }
13495 }
13496
13497 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13498 if (FAILED(rc))
13499 {
13500 mData->mSession.mLockedMedia.Clear();
13501 mrc = setError(rc,
13502 tr("Collecting locking information for all attached media failed"));
13503 break;
13504 }
13505 }
13506
13507 if (SUCCEEDED(mrc))
13508 {
13509 /* Now lock all media. If this fails, nothing is locked. */
13510 alock.release();
13511 HRESULT rc = mData->mSession.mLockedMedia.Lock();
13512 alock.acquire();
13513 if (FAILED(rc))
13514 {
13515 mrc = setError(rc,
13516 tr("Locking of attached media failed"));
13517 }
13518 }
13519
13520 return mrc;
13521}
13522
13523/**
13524 * Undoes the locks made by by #lockMedia().
13525 */
13526void SessionMachine::unlockMedia()
13527{
13528 AutoCaller autoCaller(this);
13529 AssertComRCReturnVoid(autoCaller.rc());
13530
13531 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13532
13533 /* we may be holding important error info on the current thread;
13534 * preserve it */
13535 ErrorInfoKeeper eik;
13536
13537 HRESULT rc = mData->mSession.mLockedMedia.Clear();
13538 AssertComRC(rc);
13539}
13540
13541/**
13542 * Helper to change the machine state (reimplementation).
13543 *
13544 * @note Locks this object for writing.
13545 * @note This method must not call saveSettings or SaveSettings, otherwise
13546 * it can cause crashes in random places due to unexpectedly committing
13547 * the current settings. The caller is responsible for that. The call
13548 * to saveStateSettings is fine, because this method does not commit.
13549 */
13550HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
13551{
13552 LogFlowThisFuncEnter();
13553 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
13554
13555 AutoCaller autoCaller(this);
13556 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13557
13558 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13559
13560 MachineState_T oldMachineState = mData->mMachineState;
13561
13562 AssertMsgReturn(oldMachineState != aMachineState,
13563 ("oldMachineState=%s, aMachineState=%s\n",
13564 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
13565 E_FAIL);
13566
13567 HRESULT rc = S_OK;
13568
13569 int stsFlags = 0;
13570 bool deleteSavedState = false;
13571
13572 /* detect some state transitions */
13573
13574 if ( ( oldMachineState == MachineState_Saved
13575 && aMachineState == MachineState_Restoring)
13576 || ( ( oldMachineState == MachineState_PoweredOff
13577 || oldMachineState == MachineState_Teleported
13578 || oldMachineState == MachineState_Aborted
13579 )
13580 && ( aMachineState == MachineState_TeleportingIn
13581 || aMachineState == MachineState_Starting
13582 )
13583 )
13584 )
13585 {
13586 /* The EMT thread is about to start */
13587
13588 /* Nothing to do here for now... */
13589
13590 /// @todo NEWMEDIA don't let mDVDDrive and other children
13591 /// change anything when in the Starting/Restoring state
13592 }
13593 else if ( ( oldMachineState == MachineState_Running
13594 || oldMachineState == MachineState_Paused
13595 || oldMachineState == MachineState_Teleporting
13596 || oldMachineState == MachineState_LiveSnapshotting
13597 || oldMachineState == MachineState_Stuck
13598 || oldMachineState == MachineState_Starting
13599 || oldMachineState == MachineState_Stopping
13600 || oldMachineState == MachineState_Saving
13601 || oldMachineState == MachineState_Restoring
13602 || oldMachineState == MachineState_TeleportingPausedVM
13603 || oldMachineState == MachineState_TeleportingIn
13604 )
13605 && ( aMachineState == MachineState_PoweredOff
13606 || aMachineState == MachineState_Saved
13607 || aMachineState == MachineState_Teleported
13608 || aMachineState == MachineState_Aborted
13609 )
13610 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
13611 * snapshot */
13612 && ( mConsoleTaskData.mSnapshot.isNull()
13613 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
13614 )
13615 )
13616 {
13617 /* The EMT thread has just stopped, unlock attached media. Note that as
13618 * opposed to locking that is done from Console, we do unlocking here
13619 * because the VM process may have aborted before having a chance to
13620 * properly unlock all media it locked. */
13621
13622 unlockMedia();
13623 }
13624
13625 if (oldMachineState == MachineState_Restoring)
13626 {
13627 if (aMachineState != MachineState_Saved)
13628 {
13629 /*
13630 * delete the saved state file once the machine has finished
13631 * restoring from it (note that Console sets the state from
13632 * Restoring to Saved if the VM couldn't restore successfully,
13633 * to give the user an ability to fix an error and retry --
13634 * we keep the saved state file in this case)
13635 */
13636 deleteSavedState = true;
13637 }
13638 }
13639 else if ( oldMachineState == MachineState_Saved
13640 && ( aMachineState == MachineState_PoweredOff
13641 || aMachineState == MachineState_Aborted
13642 || aMachineState == MachineState_Teleported
13643 )
13644 )
13645 {
13646 /*
13647 * delete the saved state after Console::ForgetSavedState() is called
13648 * or if the VM process (owning a direct VM session) crashed while the
13649 * VM was Saved
13650 */
13651
13652 /// @todo (dmik)
13653 // Not sure that deleting the saved state file just because of the
13654 // client death before it attempted to restore the VM is a good
13655 // thing. But when it crashes we need to go to the Aborted state
13656 // which cannot have the saved state file associated... The only
13657 // way to fix this is to make the Aborted condition not a VM state
13658 // but a bool flag: i.e., when a crash occurs, set it to true and
13659 // change the state to PoweredOff or Saved depending on the
13660 // saved state presence.
13661
13662 deleteSavedState = true;
13663 mData->mCurrentStateModified = TRUE;
13664 stsFlags |= SaveSTS_CurStateModified;
13665 }
13666
13667 if ( aMachineState == MachineState_Starting
13668 || aMachineState == MachineState_Restoring
13669 || aMachineState == MachineState_TeleportingIn
13670 )
13671 {
13672 /* set the current state modified flag to indicate that the current
13673 * state is no more identical to the state in the
13674 * current snapshot */
13675 if (!mData->mCurrentSnapshot.isNull())
13676 {
13677 mData->mCurrentStateModified = TRUE;
13678 stsFlags |= SaveSTS_CurStateModified;
13679 }
13680 }
13681
13682 if (deleteSavedState)
13683 {
13684 if (mRemoveSavedState)
13685 {
13686 Assert(!mSSData->strStateFilePath.isEmpty());
13687
13688 // it is safe to delete the saved state file if ...
13689 if ( !mData->mFirstSnapshot // ... we have no snapshots or
13690 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
13691 // ... none of the snapshots share the saved state file
13692 )
13693 RTFileDelete(mSSData->strStateFilePath.c_str());
13694 }
13695
13696 mSSData->strStateFilePath.setNull();
13697 stsFlags |= SaveSTS_StateFilePath;
13698 }
13699
13700 /* redirect to the underlying peer machine */
13701 mPeer->setMachineState(aMachineState);
13702
13703 if ( aMachineState == MachineState_PoweredOff
13704 || aMachineState == MachineState_Teleported
13705 || aMachineState == MachineState_Aborted
13706 || aMachineState == MachineState_Saved)
13707 {
13708 /* the machine has stopped execution
13709 * (or the saved state file was adopted) */
13710 stsFlags |= SaveSTS_StateTimeStamp;
13711 }
13712
13713 if ( ( oldMachineState == MachineState_PoweredOff
13714 || oldMachineState == MachineState_Aborted
13715 || oldMachineState == MachineState_Teleported
13716 )
13717 && aMachineState == MachineState_Saved)
13718 {
13719 /* the saved state file was adopted */
13720 Assert(!mSSData->strStateFilePath.isEmpty());
13721 stsFlags |= SaveSTS_StateFilePath;
13722 }
13723
13724#ifdef VBOX_WITH_GUEST_PROPS
13725 if ( aMachineState == MachineState_PoweredOff
13726 || aMachineState == MachineState_Aborted
13727 || aMachineState == MachineState_Teleported)
13728 {
13729 /* Make sure any transient guest properties get removed from the
13730 * property store on shutdown. */
13731
13732 HWData::GuestPropertyList::iterator it;
13733 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
13734 if (!fNeedsSaving)
13735 for (it = mHWData->mGuestProperties.begin();
13736 it != mHWData->mGuestProperties.end(); ++it)
13737 if ( (it->mFlags & guestProp::TRANSIENT)
13738 || (it->mFlags & guestProp::TRANSRESET))
13739 {
13740 fNeedsSaving = true;
13741 break;
13742 }
13743 if (fNeedsSaving)
13744 {
13745 mData->mCurrentStateModified = TRUE;
13746 stsFlags |= SaveSTS_CurStateModified;
13747 }
13748 }
13749#endif
13750
13751 rc = saveStateSettings(stsFlags);
13752
13753 if ( ( oldMachineState != MachineState_PoweredOff
13754 && oldMachineState != MachineState_Aborted
13755 && oldMachineState != MachineState_Teleported
13756 )
13757 && ( aMachineState == MachineState_PoweredOff
13758 || aMachineState == MachineState_Aborted
13759 || aMachineState == MachineState_Teleported
13760 )
13761 )
13762 {
13763 /* we've been shut down for any reason */
13764 /* no special action so far */
13765 }
13766
13767 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
13768 LogFlowThisFuncLeave();
13769 return rc;
13770}
13771
13772/**
13773 * Sends the current machine state value to the VM process.
13774 *
13775 * @note Locks this object for reading, then calls a client process.
13776 */
13777HRESULT SessionMachine::updateMachineStateOnClient()
13778{
13779 AutoCaller autoCaller(this);
13780 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13781
13782 ComPtr<IInternalSessionControl> directControl;
13783 {
13784 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13785 AssertReturn(!!mData, E_FAIL);
13786 directControl = mData->mSession.mDirectControl;
13787
13788 /* directControl may be already set to NULL here in #OnSessionEnd()
13789 * called too early by the direct session process while there is still
13790 * some operation (like deleting the snapshot) in progress. The client
13791 * process in this case is waiting inside Session::close() for the
13792 * "end session" process object to complete, while #uninit() called by
13793 * #checkForDeath() on the Watcher thread is waiting for the pending
13794 * operation to complete. For now, we accept this inconsistent behavior
13795 * and simply do nothing here. */
13796
13797 if (mData->mSession.mState == SessionState_Unlocking)
13798 return S_OK;
13799
13800 AssertReturn(!directControl.isNull(), E_FAIL);
13801 }
13802
13803 return directControl->UpdateMachineState(mData->mMachineState);
13804}
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