VirtualBox

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

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

Main: Introduce "StorageMgmt/SilentReconfigureWhilePaused" extradata flag to allow attachment reconfiguration while the VM is paused

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 473.4 KB
Line 
1/* $Id: MachineImpl.cpp 45284 2013-04-02 11:55:41Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
27# include <errno.h>
28# include <sys/types.h>
29# include <sys/stat.h>
30# include <sys/ipc.h>
31# include <sys/sem.h>
32#endif
33
34#include "Logging.h"
35#include "VirtualBoxImpl.h"
36#include "MachineImpl.h"
37#include "ProgressImpl.h"
38#include "ProgressProxyImpl.h"
39#include "MediumAttachmentImpl.h"
40#include "MediumImpl.h"
41#include "MediumLock.h"
42#include "USBControllerImpl.h"
43#include "HostImpl.h"
44#include "SharedFolderImpl.h"
45#include "GuestOSTypeImpl.h"
46#include "VirtualBoxErrorInfoImpl.h"
47#include "GuestImpl.h"
48#include "StorageControllerImpl.h"
49#include "DisplayImpl.h"
50#include "DisplayUtils.h"
51#include "BandwidthControlImpl.h"
52#include "MachineImplCloneVM.h"
53#include "AutostartDb.h"
54
55// generated header
56#include "VBoxEvents.h"
57
58#ifdef VBOX_WITH_USB
59# include "USBProxyService.h"
60#endif
61
62#include "AutoCaller.h"
63#include "HashedPw.h"
64#include "Performance.h"
65
66#include <iprt/asm.h>
67#include <iprt/path.h>
68#include <iprt/dir.h>
69#include <iprt/env.h>
70#include <iprt/lockvalidator.h>
71#include <iprt/process.h>
72#include <iprt/cpp/utils.h>
73#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
74#include <iprt/sha.h>
75#include <iprt/string.h>
76
77#include <VBox/com/array.h>
78#include <VBox/com/list.h>
79
80#include <VBox/err.h>
81#include <VBox/param.h>
82#include <VBox/settings.h>
83#include <VBox/vmm/ssm.h>
84
85#ifdef VBOX_WITH_GUEST_PROPS
86# include <VBox/HostServices/GuestPropertySvc.h>
87# include <VBox/com/array.h>
88#endif
89
90#include "VBox/com/MultiResult.h"
91
92#include <algorithm>
93
94#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
95# define HOSTSUFF_EXE ".exe"
96#else /* !RT_OS_WINDOWS */
97# define HOSTSUFF_EXE ""
98#endif /* !RT_OS_WINDOWS */
99
100// defines / prototypes
101/////////////////////////////////////////////////////////////////////////////
102
103/////////////////////////////////////////////////////////////////////////////
104// Machine::Data structure
105/////////////////////////////////////////////////////////////////////////////
106
107Machine::Data::Data()
108{
109 mRegistered = FALSE;
110 pMachineConfigFile = NULL;
111 /* Contains hints on what has changed when the user is using the VM (config
112 * changes, running the VM, ...). This is used to decide if a config needs
113 * to be written to disk. */
114 flModifications = 0;
115 /* VM modification usually also trigger setting the current state to
116 * "Modified". Although this is not always the case. An e.g. is the VM
117 * initialization phase or when snapshot related data is changed. The
118 * actually behavior is controlled by the following flag. */
119 m_fAllowStateModification = false;
120 mAccessible = FALSE;
121 /* mUuid is initialized in Machine::init() */
122
123 mMachineState = MachineState_PoweredOff;
124 RTTimeNow(&mLastStateChange);
125
126 mMachineStateDeps = 0;
127 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
128 mMachineStateChangePending = 0;
129
130 mCurrentStateModified = TRUE;
131 mGuestPropertiesModified = FALSE;
132
133 mSession.mPID = NIL_RTPROCESS;
134 mSession.mState = SessionState_Unlocked;
135}
136
137Machine::Data::~Data()
138{
139 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
140 {
141 RTSemEventMultiDestroy(mMachineStateDepsSem);
142 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
143 }
144 if (pMachineConfigFile)
145 {
146 delete pMachineConfigFile;
147 pMachineConfigFile = NULL;
148 }
149}
150
151/////////////////////////////////////////////////////////////////////////////
152// Machine::HWData structure
153/////////////////////////////////////////////////////////////////////////////
154
155Machine::HWData::HWData()
156{
157 /* default values for a newly created machine */
158 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
159 mMemorySize = 128;
160 mCPUCount = 1;
161 mCPUHotPlugEnabled = false;
162 mMemoryBalloonSize = 0;
163 mPageFusionEnabled = false;
164 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 mVideoCaptureFile = "Test.webm";
169 mVideoCaptureWidth = 640;
170 mVideoCaptureHeight = 480;
171 mVideoCaptureEnabled = false;
172
173 mHWVirtExEnabled = true;
174 mHWVirtExNestedPagingEnabled = true;
175#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
176 mHWVirtExLargePagesEnabled = true;
177#else
178 /* Not supported on 32 bits hosts. */
179 mHWVirtExLargePagesEnabled = false;
180#endif
181 mHWVirtExVPIDEnabled = true;
182 mHWVirtExForceEnabled = false;
183#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
184 mHWVirtExExclusive = false;
185#else
186 mHWVirtExExclusive = true;
187#endif
188#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
189 mPAEEnabled = true;
190#else
191 mPAEEnabled = false;
192#endif
193 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 mEmulatedUSBWebcamEnabled = FALSE;
212 mEmulatedUSBCardReaderEnabled = FALSE;
213
214 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
215 mCPUAttached[i] = false;
216
217 mIOCacheEnabled = true;
218 mIOCacheSize = 5; /* 5MB */
219
220 /* Maximum CPU execution cap by default. */
221 mCpuExecutionCap = 100;
222}
223
224Machine::HWData::~HWData()
225{
226}
227
228/////////////////////////////////////////////////////////////////////////////
229// Machine::HDData structure
230/////////////////////////////////////////////////////////////////////////////
231
232Machine::MediaData::MediaData()
233{
234}
235
236Machine::MediaData::~MediaData()
237{
238}
239
240/////////////////////////////////////////////////////////////////////////////
241// Machine class
242/////////////////////////////////////////////////////////////////////////////
243
244// constructor / destructor
245/////////////////////////////////////////////////////////////////////////////
246
247Machine::Machine()
248 : mCollectorGuest(NULL),
249 mPeer(NULL),
250 mParent(NULL),
251 mSerialPorts(),
252 mParallelPorts(),
253 uRegistryNeedsSaving(0)
254{}
255
256Machine::~Machine()
257{}
258
259HRESULT Machine::FinalConstruct()
260{
261 LogFlowThisFunc(("\n"));
262 return BaseFinalConstruct();
263}
264
265void Machine::FinalRelease()
266{
267 LogFlowThisFunc(("\n"));
268 uninit();
269 BaseFinalRelease();
270}
271
272/**
273 * Initializes a new machine instance; this init() variant creates a new, empty machine.
274 * This gets called from VirtualBox::CreateMachine().
275 *
276 * @param aParent Associated parent object
277 * @param strConfigFile Local file system path to the VM settings file (can
278 * be relative to the VirtualBox config directory).
279 * @param strName name for the machine
280 * @param llGroups list of groups for the machine
281 * @param aOsType OS Type of this machine or NULL.
282 * @param aId UUID for the new machine.
283 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
284 *
285 * @return Success indicator. if not S_OK, the machine object is invalid
286 */
287HRESULT Machine::init(VirtualBox *aParent,
288 const Utf8Str &strConfigFile,
289 const Utf8Str &strName,
290 const StringsList &llGroups,
291 GuestOSType *aOsType,
292 const Guid &aId,
293 bool fForceOverwrite,
294 bool fDirectoryIncludesUUID)
295{
296 LogFlowThisFuncEnter();
297 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
298
299 /* Enclose the state transition NotReady->InInit->Ready */
300 AutoInitSpan autoInitSpan(this);
301 AssertReturn(autoInitSpan.isOk(), E_FAIL);
302
303 HRESULT rc = initImpl(aParent, strConfigFile);
304 if (FAILED(rc)) return rc;
305
306 rc = tryCreateMachineConfigFile(fForceOverwrite);
307 if (FAILED(rc)) return rc;
308
309 if (SUCCEEDED(rc))
310 {
311 // create an empty machine config
312 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
313
314 rc = initDataAndChildObjects();
315 }
316
317 if (SUCCEEDED(rc))
318 {
319 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
320 mData->mAccessible = TRUE;
321
322 unconst(mData->mUuid) = aId;
323
324 mUserData->s.strName = strName;
325
326 mUserData->s.llGroups = llGroups;
327
328 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
329 // the "name sync" flag determines whether the machine directory gets renamed along
330 // with the machine file; say so if the settings file name is the same as the
331 // settings file parent directory (machine directory)
332 mUserData->s.fNameSync = isInOwnDir();
333
334 // initialize the default snapshots folder
335 rc = COMSETTER(SnapshotFolder)(NULL);
336 AssertComRC(rc);
337
338 if (aOsType)
339 {
340 /* Store OS type */
341 mUserData->s.strOsType = aOsType->id();
342
343 /* Apply BIOS defaults */
344 mBIOSSettings->applyDefaults(aOsType);
345
346 /* Apply network adapters defaults */
347 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
348 mNetworkAdapters[slot]->applyDefaults(aOsType);
349
350 /* Apply serial port defaults */
351 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
352 mSerialPorts[slot]->applyDefaults(aOsType);
353 }
354
355 /* At this point the changing of the current state modification
356 * flag is allowed. */
357 allowStateModification();
358
359 /* commit all changes made during the initialization */
360 commit();
361 }
362
363 /* Confirm a successful initialization when it's the case */
364 if (SUCCEEDED(rc))
365 {
366 if (mData->mAccessible)
367 autoInitSpan.setSucceeded();
368 else
369 autoInitSpan.setLimited();
370 }
371
372 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
373 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
374 mData->mRegistered,
375 mData->mAccessible,
376 rc));
377
378 LogFlowThisFuncLeave();
379
380 return rc;
381}
382
383/**
384 * Initializes a new instance with data from machine XML (formerly Init_Registered).
385 * Gets called in two modes:
386 *
387 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
388 * UUID is specified and we mark the machine as "registered";
389 *
390 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
391 * and the machine remains unregistered until RegisterMachine() is called.
392 *
393 * @param aParent Associated parent object
394 * @param aConfigFile Local file system path to the VM settings file (can
395 * be relative to the VirtualBox config directory).
396 * @param aId UUID of the machine or NULL (see above).
397 *
398 * @return Success indicator. if not S_OK, the machine object is invalid
399 */
400HRESULT Machine::initFromSettings(VirtualBox *aParent,
401 const Utf8Str &strConfigFile,
402 const Guid *aId)
403{
404 LogFlowThisFuncEnter();
405 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
406
407 /* Enclose the state transition NotReady->InInit->Ready */
408 AutoInitSpan autoInitSpan(this);
409 AssertReturn(autoInitSpan.isOk(), E_FAIL);
410
411 HRESULT rc = initImpl(aParent, strConfigFile);
412 if (FAILED(rc)) return rc;
413
414 if (aId)
415 {
416 // loading a registered VM:
417 unconst(mData->mUuid) = *aId;
418 mData->mRegistered = TRUE;
419 // now load the settings from XML:
420 rc = registeredInit();
421 // this calls initDataAndChildObjects() and loadSettings()
422 }
423 else
424 {
425 // opening an unregistered VM (VirtualBox::OpenMachine()):
426 rc = initDataAndChildObjects();
427
428 if (SUCCEEDED(rc))
429 {
430 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
431 mData->mAccessible = TRUE;
432
433 try
434 {
435 // load and parse machine XML; this will throw on XML or logic errors
436 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
437
438 // reject VM UUID duplicates, they can happen if someone
439 // tries to register an already known VM config again
440 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
441 true /* fPermitInaccessible */,
442 false /* aDoSetError */,
443 NULL) != VBOX_E_OBJECT_NOT_FOUND)
444 {
445 throw setError(E_FAIL,
446 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
447 mData->m_strConfigFile.c_str());
448 }
449
450 // use UUID from machine config
451 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
452
453 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
454 NULL /* puuidRegistry */);
455 if (FAILED(rc)) throw rc;
456
457 /* At this point the changing of the current state modification
458 * flag is allowed. */
459 allowStateModification();
460
461 commit();
462 }
463 catch (HRESULT err)
464 {
465 /* we assume that error info is set by the thrower */
466 rc = err;
467 }
468 catch (...)
469 {
470 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
471 }
472 }
473 }
474
475 /* Confirm a successful initialization when it's the case */
476 if (SUCCEEDED(rc))
477 {
478 if (mData->mAccessible)
479 autoInitSpan.setSucceeded();
480 else
481 {
482 autoInitSpan.setLimited();
483
484 // uninit media from this machine's media registry, or else
485 // reloading the settings will fail
486 mParent->unregisterMachineMedia(getId());
487 }
488 }
489
490 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
491 "rc=%08X\n",
492 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
493 mData->mRegistered, mData->mAccessible, rc));
494
495 LogFlowThisFuncLeave();
496
497 return rc;
498}
499
500/**
501 * Initializes a new instance from a machine config that is already in memory
502 * (import OVF case). Since we are importing, the UUID in the machine
503 * config is ignored and we always generate a fresh one.
504 *
505 * @param strName Name for the new machine; this overrides what is specified in config and is used
506 * for the settings file as well.
507 * @param config Machine configuration loaded and parsed from XML.
508 *
509 * @return Success indicator. if not S_OK, the machine object is invalid
510 */
511HRESULT Machine::init(VirtualBox *aParent,
512 const Utf8Str &strName,
513 const settings::MachineConfigFile &config)
514{
515 LogFlowThisFuncEnter();
516
517 /* Enclose the state transition NotReady->InInit->Ready */
518 AutoInitSpan autoInitSpan(this);
519 AssertReturn(autoInitSpan.isOk(), E_FAIL);
520
521 Utf8Str strConfigFile;
522 aParent->getDefaultMachineFolder(strConfigFile);
523 strConfigFile.append(RTPATH_DELIMITER);
524 strConfigFile.append(strName);
525 strConfigFile.append(RTPATH_DELIMITER);
526 strConfigFile.append(strName);
527 strConfigFile.append(".vbox");
528
529 HRESULT rc = initImpl(aParent, strConfigFile);
530 if (FAILED(rc)) return rc;
531
532 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
533 if (FAILED(rc)) return rc;
534
535 rc = initDataAndChildObjects();
536
537 if (SUCCEEDED(rc))
538 {
539 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
540 mData->mAccessible = TRUE;
541
542 // create empty machine config for instance data
543 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
544
545 // generate fresh UUID, ignore machine config
546 unconst(mData->mUuid).create();
547
548 rc = loadMachineDataFromSettings(config,
549 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
550
551 // override VM name as well, it may be different
552 mUserData->s.strName = strName;
553
554 if (SUCCEEDED(rc))
555 {
556 /* At this point the changing of the current state modification
557 * flag is allowed. */
558 allowStateModification();
559
560 /* commit all changes made during the initialization */
561 commit();
562 }
563 }
564
565 /* Confirm a successful initialization when it's the case */
566 if (SUCCEEDED(rc))
567 {
568 if (mData->mAccessible)
569 autoInitSpan.setSucceeded();
570 else
571 {
572 autoInitSpan.setLimited();
573
574 // uninit media from this machine's media registry, or else
575 // reloading the settings will fail
576 mParent->unregisterMachineMedia(getId());
577 }
578 }
579
580 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
581 "rc=%08X\n",
582 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
583 mData->mRegistered, mData->mAccessible, rc));
584
585 LogFlowThisFuncLeave();
586
587 return rc;
588}
589
590/**
591 * Shared code between the various init() implementations.
592 * @param aParent
593 * @return
594 */
595HRESULT Machine::initImpl(VirtualBox *aParent,
596 const Utf8Str &strConfigFile)
597{
598 LogFlowThisFuncEnter();
599
600 AssertReturn(aParent, E_INVALIDARG);
601 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
602
603 HRESULT rc = S_OK;
604
605 /* share the parent weakly */
606 unconst(mParent) = aParent;
607
608 /* allocate the essential machine data structure (the rest will be
609 * allocated later by initDataAndChildObjects() */
610 mData.allocate();
611
612 /* memorize the config file name (as provided) */
613 mData->m_strConfigFile = strConfigFile;
614
615 /* get the full file name */
616 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
617 if (RT_FAILURE(vrc1))
618 return setError(VBOX_E_FILE_ERROR,
619 tr("Invalid machine settings file name '%s' (%Rrc)"),
620 strConfigFile.c_str(),
621 vrc1);
622
623 LogFlowThisFuncLeave();
624
625 return rc;
626}
627
628/**
629 * Tries to create a machine settings file in the path stored in the machine
630 * instance data. Used when a new machine is created to fail gracefully if
631 * the settings file could not be written (e.g. because machine dir is read-only).
632 * @return
633 */
634HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
635{
636 HRESULT rc = S_OK;
637
638 // when we create a new machine, we must be able to create the settings file
639 RTFILE f = NIL_RTFILE;
640 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
641 if ( RT_SUCCESS(vrc)
642 || vrc == VERR_SHARING_VIOLATION
643 )
644 {
645 if (RT_SUCCESS(vrc))
646 RTFileClose(f);
647 if (!fForceOverwrite)
648 rc = setError(VBOX_E_FILE_ERROR,
649 tr("Machine settings file '%s' already exists"),
650 mData->m_strConfigFileFull.c_str());
651 else
652 {
653 /* try to delete the config file, as otherwise the creation
654 * of a new settings file will fail. */
655 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
656 if (RT_FAILURE(vrc2))
657 rc = setError(VBOX_E_FILE_ERROR,
658 tr("Could not delete the existing settings file '%s' (%Rrc)"),
659 mData->m_strConfigFileFull.c_str(), vrc2);
660 }
661 }
662 else if ( vrc != VERR_FILE_NOT_FOUND
663 && vrc != VERR_PATH_NOT_FOUND
664 )
665 rc = setError(VBOX_E_FILE_ERROR,
666 tr("Invalid machine settings file name '%s' (%Rrc)"),
667 mData->m_strConfigFileFull.c_str(),
668 vrc);
669 return rc;
670}
671
672/**
673 * Initializes the registered machine by loading the settings file.
674 * This method is separated from #init() in order to make it possible to
675 * retry the operation after VirtualBox startup instead of refusing to
676 * startup the whole VirtualBox server in case if the settings file of some
677 * registered VM is invalid or inaccessible.
678 *
679 * @note Must be always called from this object's write lock
680 * (unless called from #init() that doesn't need any locking).
681 * @note Locks the mUSBController method for writing.
682 * @note Subclasses must not call this method.
683 */
684HRESULT Machine::registeredInit()
685{
686 AssertReturn(!isSessionMachine(), E_FAIL);
687 AssertReturn(!isSnapshotMachine(), E_FAIL);
688 AssertReturn(mData->mUuid.isValid(), E_FAIL);
689 AssertReturn(!mData->mAccessible, E_FAIL);
690
691 HRESULT rc = initDataAndChildObjects();
692
693 if (SUCCEEDED(rc))
694 {
695 /* Temporarily reset the registered flag in order to let setters
696 * potentially called from loadSettings() succeed (isMutable() used in
697 * all setters will return FALSE for a Machine instance if mRegistered
698 * is TRUE). */
699 mData->mRegistered = FALSE;
700
701 try
702 {
703 // load and parse machine XML; this will throw on XML or logic errors
704 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
705
706 if (mData->mUuid != mData->pMachineConfigFile->uuid)
707 throw setError(E_FAIL,
708 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
709 mData->pMachineConfigFile->uuid.raw(),
710 mData->m_strConfigFileFull.c_str(),
711 mData->mUuid.toString().c_str(),
712 mParent->settingsFilePath().c_str());
713
714 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
715 NULL /* const Guid *puuidRegistry */);
716 if (FAILED(rc)) throw rc;
717 }
718 catch (HRESULT err)
719 {
720 /* we assume that error info is set by the thrower */
721 rc = err;
722 }
723 catch (...)
724 {
725 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
726 }
727
728 /* Restore the registered flag (even on failure) */
729 mData->mRegistered = TRUE;
730 }
731
732 if (SUCCEEDED(rc))
733 {
734 /* Set mAccessible to TRUE only if we successfully locked and loaded
735 * the settings file */
736 mData->mAccessible = TRUE;
737
738 /* commit all changes made during loading the settings file */
739 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
740 /// @todo r=klaus for some reason the settings loading logic backs up
741 // the settings, and therefore a commit is needed. Should probably be changed.
742 }
743 else
744 {
745 /* If the machine is registered, then, instead of returning a
746 * failure, we mark it as inaccessible and set the result to
747 * success to give it a try later */
748
749 /* fetch the current error info */
750 mData->mAccessError = com::ErrorInfo();
751 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
752 mData->mUuid.raw(),
753 mData->mAccessError.getText().raw()));
754
755 /* rollback all changes */
756 rollback(false /* aNotify */);
757
758 // uninit media from this machine's media registry, or else
759 // reloading the settings will fail
760 mParent->unregisterMachineMedia(getId());
761
762 /* uninitialize the common part to make sure all data is reset to
763 * default (null) values */
764 uninitDataAndChildObjects();
765
766 rc = S_OK;
767 }
768
769 return rc;
770}
771
772/**
773 * Uninitializes the instance.
774 * Called either from FinalRelease() or by the parent when it gets destroyed.
775 *
776 * @note The caller of this method must make sure that this object
777 * a) doesn't have active callers on the current thread and b) is not locked
778 * by the current thread; otherwise uninit() will hang either a) due to
779 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
780 * a dead-lock caused by this thread waiting for all callers on the other
781 * threads are done but preventing them from doing so by holding a lock.
782 */
783void Machine::uninit()
784{
785 LogFlowThisFuncEnter();
786
787 Assert(!isWriteLockOnCurrentThread());
788
789 Assert(!uRegistryNeedsSaving);
790 if (uRegistryNeedsSaving)
791 {
792 AutoCaller autoCaller(this);
793 if (SUCCEEDED(autoCaller.rc()))
794 {
795 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
796 saveSettings(NULL, Machine::SaveS_Force);
797 }
798 }
799
800 /* Enclose the state transition Ready->InUninit->NotReady */
801 AutoUninitSpan autoUninitSpan(this);
802 if (autoUninitSpan.uninitDone())
803 return;
804
805 Assert(!isSnapshotMachine());
806 Assert(!isSessionMachine());
807 Assert(!!mData);
808
809 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
810 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
811
812 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
813
814 if (!mData->mSession.mMachine.isNull())
815 {
816 /* Theoretically, this can only happen if the VirtualBox server has been
817 * terminated while there were clients running that owned open direct
818 * sessions. Since in this case we are definitely called by
819 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
820 * won't happen on the client watcher thread (because it does
821 * VirtualBox::addCaller() for the duration of the
822 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
823 * cannot happen until the VirtualBox caller is released). This is
824 * important, because SessionMachine::uninit() cannot correctly operate
825 * after we return from this method (it expects the Machine instance is
826 * still valid). We'll call it ourselves below.
827 */
828 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
829 (SessionMachine*)mData->mSession.mMachine));
830
831 if (Global::IsOnlineOrTransient(mData->mMachineState))
832 {
833 LogWarningThisFunc(("Setting state to Aborted!\n"));
834 /* set machine state using SessionMachine reimplementation */
835 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
836 }
837
838 /*
839 * Uninitialize SessionMachine using public uninit() to indicate
840 * an unexpected uninitialization.
841 */
842 mData->mSession.mMachine->uninit();
843 /* SessionMachine::uninit() must set mSession.mMachine to null */
844 Assert(mData->mSession.mMachine.isNull());
845 }
846
847 // uninit media from this machine's media registry, if they're still there
848 Guid uuidMachine(getId());
849
850 /* the lock is no more necessary (SessionMachine is uninitialized) */
851 alock.release();
852
853 /* XXX This will fail with
854 * "cannot be closed because it is still attached to 1 virtual machines"
855 * because at this point we did not call uninitDataAndChildObjects() yet
856 * and therefore also removeBackReference() for all these mediums was not called! */
857
858 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
859 mParent->unregisterMachineMedia(uuidMachine);
860
861 // has machine been modified?
862 if (mData->flModifications)
863 {
864 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
865 rollback(false /* aNotify */);
866 }
867
868 if (mData->mAccessible)
869 uninitDataAndChildObjects();
870
871 /* free the essential data structure last */
872 mData.free();
873
874 LogFlowThisFuncLeave();
875}
876
877// IMachine properties
878/////////////////////////////////////////////////////////////////////////////
879
880STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
881{
882 CheckComArgOutPointerValid(aParent);
883
884 AutoLimitedCaller autoCaller(this);
885 if (FAILED(autoCaller.rc())) return autoCaller.rc();
886
887 /* mParent is constant during life time, no need to lock */
888 ComObjPtr<VirtualBox> pVirtualBox(mParent);
889 pVirtualBox.queryInterfaceTo(aParent);
890
891 return S_OK;
892}
893
894STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
895{
896 CheckComArgOutPointerValid(aAccessible);
897
898 AutoLimitedCaller autoCaller(this);
899 if (FAILED(autoCaller.rc())) return autoCaller.rc();
900
901 LogFlowThisFunc(("ENTER\n"));
902
903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
904
905 HRESULT rc = S_OK;
906
907 if (!mData->mAccessible)
908 {
909 /* try to initialize the VM once more if not accessible */
910
911 AutoReinitSpan autoReinitSpan(this);
912 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
913
914#ifdef DEBUG
915 LogFlowThisFunc(("Dumping media backreferences\n"));
916 mParent->dumpAllBackRefs();
917#endif
918
919 if (mData->pMachineConfigFile)
920 {
921 // reset the XML file to force loadSettings() (called from registeredInit())
922 // to parse it again; the file might have changed
923 delete mData->pMachineConfigFile;
924 mData->pMachineConfigFile = NULL;
925 }
926
927 rc = registeredInit();
928
929 if (SUCCEEDED(rc) && mData->mAccessible)
930 {
931 autoReinitSpan.setSucceeded();
932
933 /* make sure interesting parties will notice the accessibility
934 * state change */
935 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
936 mParent->onMachineDataChange(mData->mUuid);
937 }
938 }
939
940 if (SUCCEEDED(rc))
941 *aAccessible = mData->mAccessible;
942
943 LogFlowThisFuncLeave();
944
945 return rc;
946}
947
948STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
949{
950 CheckComArgOutPointerValid(aAccessError);
951
952 AutoLimitedCaller autoCaller(this);
953 if (FAILED(autoCaller.rc())) return autoCaller.rc();
954
955 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
956
957 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
958 {
959 /* return shortly */
960 aAccessError = NULL;
961 return S_OK;
962 }
963
964 HRESULT rc = S_OK;
965
966 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
967 rc = errorInfo.createObject();
968 if (SUCCEEDED(rc))
969 {
970 errorInfo->init(mData->mAccessError.getResultCode(),
971 mData->mAccessError.getInterfaceID().ref(),
972 Utf8Str(mData->mAccessError.getComponent()).c_str(),
973 Utf8Str(mData->mAccessError.getText()));
974 rc = errorInfo.queryInterfaceTo(aAccessError);
975 }
976
977 return rc;
978}
979
980STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
981{
982 CheckComArgOutPointerValid(aName);
983
984 AutoCaller autoCaller(this);
985 if (FAILED(autoCaller.rc())) return autoCaller.rc();
986
987 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
988
989 mUserData->s.strName.cloneTo(aName);
990
991 return S_OK;
992}
993
994STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
995{
996 CheckComArgStrNotEmptyOrNull(aName);
997
998 AutoCaller autoCaller(this);
999 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1000
1001 // prohibit setting a UUID only as the machine name, or else it can
1002 // never be found by findMachine()
1003 Guid test(aName);
1004
1005 if (test.isValid())
1006 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1007
1008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1009
1010 HRESULT rc = checkStateDependency(MutableStateDep);
1011 if (FAILED(rc)) return rc;
1012
1013 setModified(IsModified_MachineData);
1014 mUserData.backup();
1015 mUserData->s.strName = aName;
1016
1017 return S_OK;
1018}
1019
1020STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1021{
1022 CheckComArgOutPointerValid(aDescription);
1023
1024 AutoCaller autoCaller(this);
1025 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1026
1027 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1028
1029 mUserData->s.strDescription.cloneTo(aDescription);
1030
1031 return S_OK;
1032}
1033
1034STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1035{
1036 AutoCaller autoCaller(this);
1037 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1038
1039 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1040
1041 // this can be done in principle in any state as it doesn't affect the VM
1042 // significantly, but play safe by not messing around while complex
1043 // activities are going on
1044 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1045 if (FAILED(rc)) return rc;
1046
1047 setModified(IsModified_MachineData);
1048 mUserData.backup();
1049 mUserData->s.strDescription = aDescription;
1050
1051 return S_OK;
1052}
1053
1054STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1055{
1056 CheckComArgOutPointerValid(aId);
1057
1058 AutoLimitedCaller autoCaller(this);
1059 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1060
1061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1062
1063 mData->mUuid.toUtf16().cloneTo(aId);
1064
1065 return S_OK;
1066}
1067
1068STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1069{
1070 CheckComArgOutSafeArrayPointerValid(aGroups);
1071
1072 AutoCaller autoCaller(this);
1073 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1074
1075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1076 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1077 size_t i = 0;
1078 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1079 it != mUserData->s.llGroups.end();
1080 ++it, i++)
1081 {
1082 Bstr tmp = *it;
1083 tmp.cloneTo(&groups[i]);
1084 }
1085 groups.detachTo(ComSafeArrayOutArg(aGroups));
1086
1087 return S_OK;
1088}
1089
1090STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1091{
1092 AutoCaller autoCaller(this);
1093 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1094
1095 StringsList llGroups;
1096 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1097 if (FAILED(rc))
1098 return rc;
1099
1100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1101
1102 // changing machine groups is possible while the VM is offline
1103 rc = checkStateDependency(OfflineStateDep);
1104 if (FAILED(rc)) return rc;
1105
1106 setModified(IsModified_MachineData);
1107 mUserData.backup();
1108 mUserData->s.llGroups = llGroups;
1109
1110 return S_OK;
1111}
1112
1113STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1114{
1115 CheckComArgOutPointerValid(aOSTypeId);
1116
1117 AutoCaller autoCaller(this);
1118 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1119
1120 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1121
1122 mUserData->s.strOsType.cloneTo(aOSTypeId);
1123
1124 return S_OK;
1125}
1126
1127STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1128{
1129 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1130
1131 AutoCaller autoCaller(this);
1132 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1133
1134 /* look up the object by Id to check it is valid */
1135 ComPtr<IGuestOSType> guestOSType;
1136 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1137 if (FAILED(rc)) return rc;
1138
1139 /* when setting, always use the "etalon" value for consistency -- lookup
1140 * by ID is case-insensitive and the input value may have different case */
1141 Bstr osTypeId;
1142 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1143 if (FAILED(rc)) return rc;
1144
1145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1146
1147 rc = checkStateDependency(MutableStateDep);
1148 if (FAILED(rc)) return rc;
1149
1150 setModified(IsModified_MachineData);
1151 mUserData.backup();
1152 mUserData->s.strOsType = osTypeId;
1153
1154 return S_OK;
1155}
1156
1157
1158STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1159{
1160 CheckComArgOutPointerValid(aFirmwareType);
1161
1162 AutoCaller autoCaller(this);
1163 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1164
1165 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1166
1167 *aFirmwareType = mHWData->mFirmwareType;
1168
1169 return S_OK;
1170}
1171
1172STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1173{
1174 AutoCaller autoCaller(this);
1175 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1176 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1177
1178 HRESULT rc = checkStateDependency(MutableStateDep);
1179 if (FAILED(rc)) return rc;
1180
1181 setModified(IsModified_MachineData);
1182 mHWData.backup();
1183 mHWData->mFirmwareType = aFirmwareType;
1184
1185 return S_OK;
1186}
1187
1188STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1189{
1190 CheckComArgOutPointerValid(aKeyboardHIDType);
1191
1192 AutoCaller autoCaller(this);
1193 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1194
1195 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1196
1197 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1198
1199 return S_OK;
1200}
1201
1202STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1203{
1204 AutoCaller autoCaller(this);
1205 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1206 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1207
1208 HRESULT rc = checkStateDependency(MutableStateDep);
1209 if (FAILED(rc)) return rc;
1210
1211 setModified(IsModified_MachineData);
1212 mHWData.backup();
1213 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1214
1215 return S_OK;
1216}
1217
1218STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1219{
1220 CheckComArgOutPointerValid(aPointingHIDType);
1221
1222 AutoCaller autoCaller(this);
1223 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1224
1225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1226
1227 *aPointingHIDType = mHWData->mPointingHIDType;
1228
1229 return S_OK;
1230}
1231
1232STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1233{
1234 AutoCaller autoCaller(this);
1235 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1236 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1237
1238 HRESULT rc = checkStateDependency(MutableStateDep);
1239 if (FAILED(rc)) return rc;
1240
1241 setModified(IsModified_MachineData);
1242 mHWData.backup();
1243 mHWData->mPointingHIDType = aPointingHIDType;
1244
1245 return S_OK;
1246}
1247
1248STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1249{
1250 CheckComArgOutPointerValid(aChipsetType);
1251
1252 AutoCaller autoCaller(this);
1253 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1254
1255 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1256
1257 *aChipsetType = mHWData->mChipsetType;
1258
1259 return S_OK;
1260}
1261
1262STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1263{
1264 AutoCaller autoCaller(this);
1265 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1266 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1267
1268 HRESULT rc = checkStateDependency(MutableStateDep);
1269 if (FAILED(rc)) return rc;
1270
1271 if (aChipsetType != mHWData->mChipsetType)
1272 {
1273 setModified(IsModified_MachineData);
1274 mHWData.backup();
1275 mHWData->mChipsetType = aChipsetType;
1276
1277 // Resize network adapter array, to be finalized on commit/rollback.
1278 // We must not throw away entries yet, otherwise settings are lost
1279 // without a way to roll back.
1280 uint32_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1281 uint32_t oldCount = mNetworkAdapters.size();
1282 if (newCount > oldCount)
1283 {
1284 mNetworkAdapters.resize(newCount);
1285 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1286 {
1287 unconst(mNetworkAdapters[slot]).createObject();
1288 mNetworkAdapters[slot]->init(this, slot);
1289 }
1290 }
1291 }
1292
1293 return S_OK;
1294}
1295
1296STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1297{
1298 CheckComArgOutPointerValid(aHWVersion);
1299
1300 AutoCaller autoCaller(this);
1301 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1302
1303 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1304
1305 mHWData->mHWVersion.cloneTo(aHWVersion);
1306
1307 return S_OK;
1308}
1309
1310STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1311{
1312 /* check known version */
1313 Utf8Str hwVersion = aHWVersion;
1314 if ( hwVersion.compare("1") != 0
1315 && hwVersion.compare("2") != 0)
1316 return setError(E_INVALIDARG,
1317 tr("Invalid hardware version: %ls\n"), aHWVersion);
1318
1319 AutoCaller autoCaller(this);
1320 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1321
1322 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1323
1324 HRESULT rc = checkStateDependency(MutableStateDep);
1325 if (FAILED(rc)) return rc;
1326
1327 setModified(IsModified_MachineData);
1328 mHWData.backup();
1329 mHWData->mHWVersion = hwVersion;
1330
1331 return S_OK;
1332}
1333
1334STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1335{
1336 CheckComArgOutPointerValid(aUUID);
1337
1338 AutoCaller autoCaller(this);
1339 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1340
1341 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1342
1343 if (mHWData->mHardwareUUID.isValid())
1344 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1345 else
1346 mData->mUuid.toUtf16().cloneTo(aUUID);
1347
1348 return S_OK;
1349}
1350
1351STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1352{
1353 Guid hardwareUUID(aUUID);
1354 if (!hardwareUUID.isValid())
1355 return E_INVALIDARG;
1356
1357 AutoCaller autoCaller(this);
1358 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1359
1360 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1361
1362 HRESULT rc = checkStateDependency(MutableStateDep);
1363 if (FAILED(rc)) return rc;
1364
1365 setModified(IsModified_MachineData);
1366 mHWData.backup();
1367 if (hardwareUUID == mData->mUuid)
1368 mHWData->mHardwareUUID.clear();
1369 else
1370 mHWData->mHardwareUUID = hardwareUUID;
1371
1372 return S_OK;
1373}
1374
1375STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1376{
1377 CheckComArgOutPointerValid(memorySize);
1378
1379 AutoCaller autoCaller(this);
1380 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1381
1382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1383
1384 *memorySize = mHWData->mMemorySize;
1385
1386 return S_OK;
1387}
1388
1389STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1390{
1391 /* check RAM limits */
1392 if ( memorySize < MM_RAM_MIN_IN_MB
1393 || memorySize > MM_RAM_MAX_IN_MB
1394 )
1395 return setError(E_INVALIDARG,
1396 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1397 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1398
1399 AutoCaller autoCaller(this);
1400 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1401
1402 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1403
1404 HRESULT rc = checkStateDependency(MutableStateDep);
1405 if (FAILED(rc)) return rc;
1406
1407 setModified(IsModified_MachineData);
1408 mHWData.backup();
1409 mHWData->mMemorySize = memorySize;
1410
1411 return S_OK;
1412}
1413
1414STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1415{
1416 CheckComArgOutPointerValid(CPUCount);
1417
1418 AutoCaller autoCaller(this);
1419 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1420
1421 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1422
1423 *CPUCount = mHWData->mCPUCount;
1424
1425 return S_OK;
1426}
1427
1428STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1429{
1430 /* check CPU limits */
1431 if ( CPUCount < SchemaDefs::MinCPUCount
1432 || CPUCount > SchemaDefs::MaxCPUCount
1433 )
1434 return setError(E_INVALIDARG,
1435 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1436 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1437
1438 AutoCaller autoCaller(this);
1439 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1440
1441 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1442
1443 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1444 if (mHWData->mCPUHotPlugEnabled)
1445 {
1446 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1447 {
1448 if (mHWData->mCPUAttached[idx])
1449 return setError(E_INVALIDARG,
1450 tr("There is still a CPU attached to socket %lu."
1451 "Detach the CPU before removing the socket"),
1452 CPUCount, idx+1);
1453 }
1454 }
1455
1456 HRESULT rc = checkStateDependency(MutableStateDep);
1457 if (FAILED(rc)) return rc;
1458
1459 setModified(IsModified_MachineData);
1460 mHWData.backup();
1461 mHWData->mCPUCount = CPUCount;
1462
1463 return S_OK;
1464}
1465
1466STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1467{
1468 CheckComArgOutPointerValid(aExecutionCap);
1469
1470 AutoCaller autoCaller(this);
1471 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1472
1473 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1474
1475 *aExecutionCap = mHWData->mCpuExecutionCap;
1476
1477 return S_OK;
1478}
1479
1480STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1481{
1482 HRESULT rc = S_OK;
1483
1484 /* check throttle limits */
1485 if ( aExecutionCap < 1
1486 || aExecutionCap > 100
1487 )
1488 return setError(E_INVALIDARG,
1489 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1490 aExecutionCap, 1, 100);
1491
1492 AutoCaller autoCaller(this);
1493 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1494
1495 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1496
1497 alock.release();
1498 rc = onCPUExecutionCapChange(aExecutionCap);
1499 alock.acquire();
1500 if (FAILED(rc)) return rc;
1501
1502 setModified(IsModified_MachineData);
1503 mHWData.backup();
1504 mHWData->mCpuExecutionCap = aExecutionCap;
1505
1506 /* Save settings if online - todo why is this required?? */
1507 if (Global::IsOnline(mData->mMachineState))
1508 saveSettings(NULL);
1509
1510 return S_OK;
1511}
1512
1513
1514STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *enabled)
1515{
1516 CheckComArgOutPointerValid(enabled);
1517
1518 AutoCaller autoCaller(this);
1519 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1520
1521 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1522
1523 *enabled = mHWData->mCPUHotPlugEnabled;
1524
1525 return S_OK;
1526}
1527
1528STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled)
1529{
1530 HRESULT rc = S_OK;
1531
1532 AutoCaller autoCaller(this);
1533 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1534
1535 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1536
1537 rc = checkStateDependency(MutableStateDep);
1538 if (FAILED(rc)) return rc;
1539
1540 if (mHWData->mCPUHotPlugEnabled != enabled)
1541 {
1542 if (enabled)
1543 {
1544 setModified(IsModified_MachineData);
1545 mHWData.backup();
1546
1547 /* Add the amount of CPUs currently attached */
1548 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1549 {
1550 mHWData->mCPUAttached[i] = true;
1551 }
1552 }
1553 else
1554 {
1555 /*
1556 * We can disable hotplug only if the amount of maximum CPUs is equal
1557 * to the amount of attached CPUs
1558 */
1559 unsigned cCpusAttached = 0;
1560 unsigned iHighestId = 0;
1561
1562 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1563 {
1564 if (mHWData->mCPUAttached[i])
1565 {
1566 cCpusAttached++;
1567 iHighestId = i;
1568 }
1569 }
1570
1571 if ( (cCpusAttached != mHWData->mCPUCount)
1572 || (iHighestId >= mHWData->mCPUCount))
1573 return setError(E_INVALIDARG,
1574 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1575
1576 setModified(IsModified_MachineData);
1577 mHWData.backup();
1578 }
1579 }
1580
1581 mHWData->mCPUHotPlugEnabled = enabled;
1582
1583 return rc;
1584}
1585
1586STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *enabled)
1587{
1588#ifdef VBOX_WITH_USB_CARDREADER
1589 CheckComArgOutPointerValid(enabled);
1590
1591 AutoCaller autoCaller(this);
1592 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1593
1594 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1595
1596 *enabled = mHWData->mEmulatedUSBCardReaderEnabled;
1597
1598 return S_OK;
1599#else
1600 NOREF(enabled);
1601 return E_NOTIMPL;
1602#endif
1603}
1604
1605STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL enabled)
1606{
1607#ifdef VBOX_WITH_USB_CARDREADER
1608 AutoCaller autoCaller(this);
1609 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1610 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1611
1612 HRESULT rc = checkStateDependency(MutableStateDep);
1613 if (FAILED(rc)) return rc;
1614
1615 setModified(IsModified_MachineData);
1616 mHWData.backup();
1617 mHWData->mEmulatedUSBCardReaderEnabled = enabled;
1618
1619 return S_OK;
1620#else
1621 NOREF(enabled);
1622 return E_NOTIMPL;
1623#endif
1624}
1625
1626STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *enabled)
1627{
1628#ifdef VBOX_WITH_USB_VIDEO
1629 CheckComArgOutPointerValid(enabled);
1630
1631 AutoCaller autoCaller(this);
1632 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1633
1634 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1635
1636 *enabled = mHWData->mEmulatedUSBWebcamEnabled;
1637
1638 return S_OK;
1639#else
1640 NOREF(enabled);
1641 return E_NOTIMPL;
1642#endif
1643}
1644
1645STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL enabled)
1646{
1647#ifdef VBOX_WITH_USB_VIDEO
1648 AutoCaller autoCaller(this);
1649 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1650 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1651
1652 HRESULT rc = checkStateDependency(MutableStateDep);
1653 if (FAILED(rc)) return rc;
1654
1655 setModified(IsModified_MachineData);
1656 mHWData.backup();
1657 mHWData->mEmulatedUSBWebcamEnabled = enabled;
1658
1659 return S_OK;
1660#else
1661 NOREF(enabled);
1662 return E_NOTIMPL;
1663#endif
1664}
1665
1666STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *enabled)
1667{
1668 CheckComArgOutPointerValid(enabled);
1669
1670 AutoCaller autoCaller(this);
1671 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1672 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1673
1674 *enabled = mHWData->mHPETEnabled;
1675
1676 return S_OK;
1677}
1678
1679STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL enabled)
1680{
1681 HRESULT rc = S_OK;
1682
1683 AutoCaller autoCaller(this);
1684 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1685 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1686
1687 rc = checkStateDependency(MutableStateDep);
1688 if (FAILED(rc)) return rc;
1689
1690 setModified(IsModified_MachineData);
1691 mHWData.backup();
1692
1693 mHWData->mHPETEnabled = enabled;
1694
1695 return rc;
1696}
1697
1698STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
1699{
1700 AutoCaller autoCaller(this);
1701 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1702
1703 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1704
1705 *fEnabled = mHWData->mVideoCaptureEnabled;
1706 return S_OK;
1707}
1708
1709STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1710{
1711 AutoCaller autoCaller(this);
1712 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1713
1714 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1715 mHWData->mVideoCaptureEnabled = fEnabled;
1716 return S_OK;
1717}
1718
1719STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR * apFile)
1720{
1721 AutoCaller autoCaller(this);
1722 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1723
1724 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1725 mHWData->mVideoCaptureFile.cloneTo(apFile);
1726 return S_OK;
1727}
1728
1729STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1730{
1731 Utf8Str strFile(aFile);
1732 AutoCaller autoCaller(this);
1733 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1734
1735 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1736 if (strFile.isEmpty())
1737 strFile = "VideoCap.webm";
1738 mHWData->mVideoCaptureFile = strFile;
1739 return S_OK;
1740}
1741
1742
1743STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *ulHorzRes)
1744{
1745 AutoCaller autoCaller(this);
1746 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1747
1748 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1749 *ulHorzRes = mHWData->mVideoCaptureWidth;
1750 return S_OK;
1751}
1752
1753STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG ulHorzRes)
1754{
1755 AutoCaller autoCaller(this);
1756 if (FAILED(autoCaller.rc()))
1757 {
1758 LogFlow(("Autolocked failed\n"));
1759 return autoCaller.rc();
1760 }
1761
1762 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1763 mHWData->mVideoCaptureWidth = ulHorzRes;
1764 return S_OK;
1765}
1766
1767STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *ulVertRes)
1768{
1769 AutoCaller autoCaller(this);
1770 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1771
1772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1773 *ulVertRes = mHWData->mVideoCaptureHeight;
1774 return S_OK;
1775}
1776
1777STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG ulVertRes)
1778{
1779 AutoCaller autoCaller(this);
1780 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1781
1782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1783 mHWData->mVideoCaptureHeight = ulVertRes;
1784 return S_OK;
1785}
1786
1787STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1788{
1789 CheckComArgOutPointerValid(memorySize);
1790
1791 AutoCaller autoCaller(this);
1792 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1793
1794 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1795
1796 *memorySize = mHWData->mVRAMSize;
1797
1798 return S_OK;
1799}
1800
1801STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1802{
1803 /* check VRAM limits */
1804 if (memorySize < SchemaDefs::MinGuestVRAM ||
1805 memorySize > SchemaDefs::MaxGuestVRAM)
1806 return setError(E_INVALIDARG,
1807 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1808 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1809
1810 AutoCaller autoCaller(this);
1811 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1812
1813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1814
1815 HRESULT rc = checkStateDependency(MutableStateDep);
1816 if (FAILED(rc)) return rc;
1817
1818 setModified(IsModified_MachineData);
1819 mHWData.backup();
1820 mHWData->mVRAMSize = memorySize;
1821
1822 return S_OK;
1823}
1824
1825/** @todo this method should not be public */
1826STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1827{
1828 CheckComArgOutPointerValid(memoryBalloonSize);
1829
1830 AutoCaller autoCaller(this);
1831 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1832
1833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1834
1835 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
1836
1837 return S_OK;
1838}
1839
1840/**
1841 * Set the memory balloon size.
1842 *
1843 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1844 * we have to make sure that we never call IGuest from here.
1845 */
1846STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
1847{
1848 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1849#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1850 /* check limits */
1851 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1852 return setError(E_INVALIDARG,
1853 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1854 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1855
1856 AutoCaller autoCaller(this);
1857 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1858
1859 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1860
1861 setModified(IsModified_MachineData);
1862 mHWData.backup();
1863 mHWData->mMemoryBalloonSize = memoryBalloonSize;
1864
1865 return S_OK;
1866#else
1867 NOREF(memoryBalloonSize);
1868 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1869#endif
1870}
1871
1872STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *enabled)
1873{
1874 CheckComArgOutPointerValid(enabled);
1875
1876 AutoCaller autoCaller(this);
1877 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1878
1879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1880
1881 *enabled = mHWData->mPageFusionEnabled;
1882 return S_OK;
1883}
1884
1885STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL enabled)
1886{
1887#ifdef VBOX_WITH_PAGE_SHARING
1888 AutoCaller autoCaller(this);
1889 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1890
1891 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1892
1893 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1894 setModified(IsModified_MachineData);
1895 mHWData.backup();
1896 mHWData->mPageFusionEnabled = enabled;
1897 return S_OK;
1898#else
1899 NOREF(enabled);
1900 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1901#endif
1902}
1903
1904STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *enabled)
1905{
1906 CheckComArgOutPointerValid(enabled);
1907
1908 AutoCaller autoCaller(this);
1909 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1910
1911 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1912
1913 *enabled = mHWData->mAccelerate3DEnabled;
1914
1915 return S_OK;
1916}
1917
1918STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
1919{
1920 AutoCaller autoCaller(this);
1921 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1922
1923 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1924
1925 HRESULT rc = checkStateDependency(MutableStateDep);
1926 if (FAILED(rc)) return rc;
1927
1928 /** @todo check validity! */
1929
1930 setModified(IsModified_MachineData);
1931 mHWData.backup();
1932 mHWData->mAccelerate3DEnabled = enable;
1933
1934 return S_OK;
1935}
1936
1937
1938STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *enabled)
1939{
1940 CheckComArgOutPointerValid(enabled);
1941
1942 AutoCaller autoCaller(this);
1943 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1944
1945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1946
1947 *enabled = mHWData->mAccelerate2DVideoEnabled;
1948
1949 return S_OK;
1950}
1951
1952STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
1953{
1954 AutoCaller autoCaller(this);
1955 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1956
1957 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1958
1959 HRESULT rc = checkStateDependency(MutableStateDep);
1960 if (FAILED(rc)) return rc;
1961
1962 /** @todo check validity! */
1963
1964 setModified(IsModified_MachineData);
1965 mHWData.backup();
1966 mHWData->mAccelerate2DVideoEnabled = enable;
1967
1968 return S_OK;
1969}
1970
1971STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
1972{
1973 CheckComArgOutPointerValid(monitorCount);
1974
1975 AutoCaller autoCaller(this);
1976 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1977
1978 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1979
1980 *monitorCount = mHWData->mMonitorCount;
1981
1982 return S_OK;
1983}
1984
1985STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
1986{
1987 /* make sure monitor count is a sensible number */
1988 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
1989 return setError(E_INVALIDARG,
1990 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1991 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
1992
1993 AutoCaller autoCaller(this);
1994 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1995
1996 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1997
1998 HRESULT rc = checkStateDependency(MutableStateDep);
1999 if (FAILED(rc)) return rc;
2000
2001 setModified(IsModified_MachineData);
2002 mHWData.backup();
2003 mHWData->mMonitorCount = monitorCount;
2004
2005 return S_OK;
2006}
2007
2008STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2009{
2010 CheckComArgOutPointerValid(biosSettings);
2011
2012 AutoCaller autoCaller(this);
2013 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2014
2015 /* mBIOSSettings is constant during life time, no need to lock */
2016 mBIOSSettings.queryInterfaceTo(biosSettings);
2017
2018 return S_OK;
2019}
2020
2021STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2022{
2023 CheckComArgOutPointerValid(aVal);
2024
2025 AutoCaller autoCaller(this);
2026 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2027
2028 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2029
2030 switch(property)
2031 {
2032 case CPUPropertyType_PAE:
2033 *aVal = mHWData->mPAEEnabled;
2034 break;
2035
2036 case CPUPropertyType_Synthetic:
2037 *aVal = mHWData->mSyntheticCpu;
2038 break;
2039
2040 default:
2041 return E_INVALIDARG;
2042 }
2043 return S_OK;
2044}
2045
2046STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2047{
2048 AutoCaller autoCaller(this);
2049 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2050
2051 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2052
2053 HRESULT rc = checkStateDependency(MutableStateDep);
2054 if (FAILED(rc)) return rc;
2055
2056 switch(property)
2057 {
2058 case CPUPropertyType_PAE:
2059 setModified(IsModified_MachineData);
2060 mHWData.backup();
2061 mHWData->mPAEEnabled = !!aVal;
2062 break;
2063
2064 case CPUPropertyType_Synthetic:
2065 setModified(IsModified_MachineData);
2066 mHWData.backup();
2067 mHWData->mSyntheticCpu = !!aVal;
2068 break;
2069
2070 default:
2071 return E_INVALIDARG;
2072 }
2073 return S_OK;
2074}
2075
2076STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2077{
2078 CheckComArgOutPointerValid(aValEax);
2079 CheckComArgOutPointerValid(aValEbx);
2080 CheckComArgOutPointerValid(aValEcx);
2081 CheckComArgOutPointerValid(aValEdx);
2082
2083 AutoCaller autoCaller(this);
2084 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2085
2086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2087
2088 switch(aId)
2089 {
2090 case 0x0:
2091 case 0x1:
2092 case 0x2:
2093 case 0x3:
2094 case 0x4:
2095 case 0x5:
2096 case 0x6:
2097 case 0x7:
2098 case 0x8:
2099 case 0x9:
2100 case 0xA:
2101 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2102 return E_INVALIDARG;
2103
2104 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2105 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2106 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2107 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2108 break;
2109
2110 case 0x80000000:
2111 case 0x80000001:
2112 case 0x80000002:
2113 case 0x80000003:
2114 case 0x80000004:
2115 case 0x80000005:
2116 case 0x80000006:
2117 case 0x80000007:
2118 case 0x80000008:
2119 case 0x80000009:
2120 case 0x8000000A:
2121 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2122 return E_INVALIDARG;
2123
2124 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2125 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2126 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2127 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2128 break;
2129
2130 default:
2131 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2132 }
2133 return S_OK;
2134}
2135
2136STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2137{
2138 AutoCaller autoCaller(this);
2139 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2140
2141 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2142
2143 HRESULT rc = checkStateDependency(MutableStateDep);
2144 if (FAILED(rc)) return rc;
2145
2146 switch(aId)
2147 {
2148 case 0x0:
2149 case 0x1:
2150 case 0x2:
2151 case 0x3:
2152 case 0x4:
2153 case 0x5:
2154 case 0x6:
2155 case 0x7:
2156 case 0x8:
2157 case 0x9:
2158 case 0xA:
2159 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2160 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2161 setModified(IsModified_MachineData);
2162 mHWData.backup();
2163 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2164 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2165 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2166 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2167 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2168 break;
2169
2170 case 0x80000000:
2171 case 0x80000001:
2172 case 0x80000002:
2173 case 0x80000003:
2174 case 0x80000004:
2175 case 0x80000005:
2176 case 0x80000006:
2177 case 0x80000007:
2178 case 0x80000008:
2179 case 0x80000009:
2180 case 0x8000000A:
2181 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2182 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2183 setModified(IsModified_MachineData);
2184 mHWData.backup();
2185 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2186 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2187 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2188 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2189 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2190 break;
2191
2192 default:
2193 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2194 }
2195 return S_OK;
2196}
2197
2198STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2199{
2200 AutoCaller autoCaller(this);
2201 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2202
2203 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2204
2205 HRESULT rc = checkStateDependency(MutableStateDep);
2206 if (FAILED(rc)) return rc;
2207
2208 switch(aId)
2209 {
2210 case 0x0:
2211 case 0x1:
2212 case 0x2:
2213 case 0x3:
2214 case 0x4:
2215 case 0x5:
2216 case 0x6:
2217 case 0x7:
2218 case 0x8:
2219 case 0x9:
2220 case 0xA:
2221 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2222 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2223 setModified(IsModified_MachineData);
2224 mHWData.backup();
2225 /* Invalidate leaf. */
2226 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2227 break;
2228
2229 case 0x80000000:
2230 case 0x80000001:
2231 case 0x80000002:
2232 case 0x80000003:
2233 case 0x80000004:
2234 case 0x80000005:
2235 case 0x80000006:
2236 case 0x80000007:
2237 case 0x80000008:
2238 case 0x80000009:
2239 case 0x8000000A:
2240 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2241 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2242 setModified(IsModified_MachineData);
2243 mHWData.backup();
2244 /* Invalidate leaf. */
2245 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2246 break;
2247
2248 default:
2249 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2250 }
2251 return S_OK;
2252}
2253
2254STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2255{
2256 AutoCaller autoCaller(this);
2257 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2258
2259 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2260
2261 HRESULT rc = checkStateDependency(MutableStateDep);
2262 if (FAILED(rc)) return rc;
2263
2264 setModified(IsModified_MachineData);
2265 mHWData.backup();
2266
2267 /* Invalidate all standard leafs. */
2268 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2269 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2270
2271 /* Invalidate all extended leafs. */
2272 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2273 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2274
2275 return S_OK;
2276}
2277
2278STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2279{
2280 CheckComArgOutPointerValid(aVal);
2281
2282 AutoCaller autoCaller(this);
2283 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2284
2285 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2286
2287 switch(property)
2288 {
2289 case HWVirtExPropertyType_Enabled:
2290 *aVal = mHWData->mHWVirtExEnabled;
2291 break;
2292
2293 case HWVirtExPropertyType_Exclusive:
2294 *aVal = mHWData->mHWVirtExExclusive;
2295 break;
2296
2297 case HWVirtExPropertyType_VPID:
2298 *aVal = mHWData->mHWVirtExVPIDEnabled;
2299 break;
2300
2301 case HWVirtExPropertyType_NestedPaging:
2302 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2303 break;
2304
2305 case HWVirtExPropertyType_LargePages:
2306 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2307#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2308 *aVal = FALSE;
2309#endif
2310 break;
2311
2312 case HWVirtExPropertyType_Force:
2313 *aVal = mHWData->mHWVirtExForceEnabled;
2314 break;
2315
2316 default:
2317 return E_INVALIDARG;
2318 }
2319 return S_OK;
2320}
2321
2322STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2323{
2324 AutoCaller autoCaller(this);
2325 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2326
2327 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2328
2329 HRESULT rc = checkStateDependency(MutableStateDep);
2330 if (FAILED(rc)) return rc;
2331
2332 switch(property)
2333 {
2334 case HWVirtExPropertyType_Enabled:
2335 setModified(IsModified_MachineData);
2336 mHWData.backup();
2337 mHWData->mHWVirtExEnabled = !!aVal;
2338 break;
2339
2340 case HWVirtExPropertyType_Exclusive:
2341 setModified(IsModified_MachineData);
2342 mHWData.backup();
2343 mHWData->mHWVirtExExclusive = !!aVal;
2344 break;
2345
2346 case HWVirtExPropertyType_VPID:
2347 setModified(IsModified_MachineData);
2348 mHWData.backup();
2349 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2350 break;
2351
2352 case HWVirtExPropertyType_NestedPaging:
2353 setModified(IsModified_MachineData);
2354 mHWData.backup();
2355 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2356 break;
2357
2358 case HWVirtExPropertyType_LargePages:
2359 setModified(IsModified_MachineData);
2360 mHWData.backup();
2361 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2362 break;
2363
2364 case HWVirtExPropertyType_Force:
2365 setModified(IsModified_MachineData);
2366 mHWData.backup();
2367 mHWData->mHWVirtExForceEnabled = !!aVal;
2368 break;
2369
2370 default:
2371 return E_INVALIDARG;
2372 }
2373
2374 return S_OK;
2375}
2376
2377STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2378{
2379 CheckComArgOutPointerValid(aSnapshotFolder);
2380
2381 AutoCaller autoCaller(this);
2382 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2383
2384 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2385
2386 Utf8Str strFullSnapshotFolder;
2387 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2388 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2389
2390 return S_OK;
2391}
2392
2393STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2394{
2395 /* @todo (r=dmik):
2396 * 1. Allow to change the name of the snapshot folder containing snapshots
2397 * 2. Rename the folder on disk instead of just changing the property
2398 * value (to be smart and not to leave garbage). Note that it cannot be
2399 * done here because the change may be rolled back. Thus, the right
2400 * place is #saveSettings().
2401 */
2402
2403 AutoCaller autoCaller(this);
2404 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2405
2406 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2407
2408 HRESULT rc = checkStateDependency(MutableStateDep);
2409 if (FAILED(rc)) return rc;
2410
2411 if (!mData->mCurrentSnapshot.isNull())
2412 return setError(E_FAIL,
2413 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2414
2415 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2416
2417 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2418 if (strSnapshotFolder.isEmpty())
2419 strSnapshotFolder = "Snapshots";
2420 int vrc = calculateFullPath(strSnapshotFolder,
2421 strSnapshotFolder);
2422 if (RT_FAILURE(vrc))
2423 return setError(E_FAIL,
2424 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2425 aSnapshotFolder, vrc);
2426
2427 setModified(IsModified_MachineData);
2428 mUserData.backup();
2429
2430 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2431
2432 return S_OK;
2433}
2434
2435STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2436{
2437 CheckComArgOutSafeArrayPointerValid(aAttachments);
2438
2439 AutoCaller autoCaller(this);
2440 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2441
2442 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2443
2444 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2445 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2446
2447 return S_OK;
2448}
2449
2450STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2451{
2452 CheckComArgOutPointerValid(vrdeServer);
2453
2454 AutoCaller autoCaller(this);
2455 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2456
2457 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2458
2459 Assert(!!mVRDEServer);
2460 mVRDEServer.queryInterfaceTo(vrdeServer);
2461
2462 return S_OK;
2463}
2464
2465STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2466{
2467 CheckComArgOutPointerValid(audioAdapter);
2468
2469 AutoCaller autoCaller(this);
2470 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2471
2472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2473
2474 mAudioAdapter.queryInterfaceTo(audioAdapter);
2475 return S_OK;
2476}
2477
2478STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2479{
2480#ifdef VBOX_WITH_VUSB
2481 CheckComArgOutPointerValid(aUSBController);
2482
2483 AutoCaller autoCaller(this);
2484 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2485
2486 clearError();
2487 MultiResult rc(S_OK);
2488
2489# ifdef VBOX_WITH_USB
2490 rc = mParent->host()->checkUSBProxyService();
2491 if (FAILED(rc)) return rc;
2492# endif
2493
2494 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2495
2496 return rc = mUSBController.queryInterfaceTo(aUSBController);
2497#else
2498 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2499 * extended error info to indicate that USB is simply not available
2500 * (w/o treating it as a failure), for example, as in OSE */
2501 NOREF(aUSBController);
2502 ReturnComNotImplemented();
2503#endif /* VBOX_WITH_VUSB */
2504}
2505
2506STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2507{
2508 CheckComArgOutPointerValid(aFilePath);
2509
2510 AutoLimitedCaller autoCaller(this);
2511 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2512
2513 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2514
2515 mData->m_strConfigFileFull.cloneTo(aFilePath);
2516 return S_OK;
2517}
2518
2519STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2520{
2521 CheckComArgOutPointerValid(aModified);
2522
2523 AutoCaller autoCaller(this);
2524 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2525
2526 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2527
2528 HRESULT rc = checkStateDependency(MutableStateDep);
2529 if (FAILED(rc)) return rc;
2530
2531 if (!mData->pMachineConfigFile->fileExists())
2532 // this is a new machine, and no config file exists yet:
2533 *aModified = TRUE;
2534 else
2535 *aModified = (mData->flModifications != 0);
2536
2537 return S_OK;
2538}
2539
2540STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2541{
2542 CheckComArgOutPointerValid(aSessionState);
2543
2544 AutoCaller autoCaller(this);
2545 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2546
2547 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2548
2549 *aSessionState = mData->mSession.mState;
2550
2551 return S_OK;
2552}
2553
2554STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2555{
2556 CheckComArgOutPointerValid(aSessionType);
2557
2558 AutoCaller autoCaller(this);
2559 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2560
2561 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2562
2563 mData->mSession.mType.cloneTo(aSessionType);
2564
2565 return S_OK;
2566}
2567
2568STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2569{
2570 CheckComArgOutPointerValid(aSessionPID);
2571
2572 AutoCaller autoCaller(this);
2573 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2574
2575 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2576
2577 *aSessionPID = mData->mSession.mPID;
2578
2579 return S_OK;
2580}
2581
2582STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2583{
2584 CheckComArgOutPointerValid(machineState);
2585
2586 AutoCaller autoCaller(this);
2587 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2588
2589 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2590
2591 *machineState = mData->mMachineState;
2592
2593 return S_OK;
2594}
2595
2596STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2597{
2598 CheckComArgOutPointerValid(aLastStateChange);
2599
2600 AutoCaller autoCaller(this);
2601 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2602
2603 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2604
2605 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2606
2607 return S_OK;
2608}
2609
2610STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2611{
2612 CheckComArgOutPointerValid(aStateFilePath);
2613
2614 AutoCaller autoCaller(this);
2615 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2616
2617 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2618
2619 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2620
2621 return S_OK;
2622}
2623
2624STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2625{
2626 CheckComArgOutPointerValid(aLogFolder);
2627
2628 AutoCaller autoCaller(this);
2629 AssertComRCReturnRC(autoCaller.rc());
2630
2631 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2632
2633 Utf8Str logFolder;
2634 getLogFolder(logFolder);
2635 logFolder.cloneTo(aLogFolder);
2636
2637 return S_OK;
2638}
2639
2640STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2641{
2642 CheckComArgOutPointerValid(aCurrentSnapshot);
2643
2644 AutoCaller autoCaller(this);
2645 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2646
2647 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2648
2649 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2650
2651 return S_OK;
2652}
2653
2654STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2655{
2656 CheckComArgOutPointerValid(aSnapshotCount);
2657
2658 AutoCaller autoCaller(this);
2659 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2660
2661 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2662
2663 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2664 ? 0
2665 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2666
2667 return S_OK;
2668}
2669
2670STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2671{
2672 CheckComArgOutPointerValid(aCurrentStateModified);
2673
2674 AutoCaller autoCaller(this);
2675 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2676
2677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2678
2679 /* Note: for machines with no snapshots, we always return FALSE
2680 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2681 * reasons :) */
2682
2683 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2684 ? FALSE
2685 : mData->mCurrentStateModified;
2686
2687 return S_OK;
2688}
2689
2690STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2691{
2692 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2693
2694 AutoCaller autoCaller(this);
2695 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2696
2697 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2698
2699 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2700 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2701
2702 return S_OK;
2703}
2704
2705STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2706{
2707 CheckComArgOutPointerValid(aClipboardMode);
2708
2709 AutoCaller autoCaller(this);
2710 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2711
2712 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2713
2714 *aClipboardMode = mHWData->mClipboardMode;
2715
2716 return S_OK;
2717}
2718
2719STDMETHODIMP
2720Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2721{
2722 HRESULT rc = S_OK;
2723
2724 AutoCaller autoCaller(this);
2725 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2726
2727 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2728
2729 alock.release();
2730 rc = onClipboardModeChange(aClipboardMode);
2731 alock.acquire();
2732 if (FAILED(rc)) return rc;
2733
2734 setModified(IsModified_MachineData);
2735 mHWData.backup();
2736 mHWData->mClipboardMode = aClipboardMode;
2737
2738 /* Save settings if online - todo why is this required?? */
2739 if (Global::IsOnline(mData->mMachineState))
2740 saveSettings(NULL);
2741
2742 return S_OK;
2743}
2744
2745STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
2746{
2747 CheckComArgOutPointerValid(aDragAndDropMode);
2748
2749 AutoCaller autoCaller(this);
2750 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2751
2752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2753
2754 *aDragAndDropMode = mHWData->mDragAndDropMode;
2755
2756 return S_OK;
2757}
2758
2759STDMETHODIMP
2760Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
2761{
2762 HRESULT rc = S_OK;
2763
2764 AutoCaller autoCaller(this);
2765 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2766
2767 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2768
2769 alock.release();
2770 rc = onDragAndDropModeChange(aDragAndDropMode);
2771 alock.acquire();
2772 if (FAILED(rc)) return rc;
2773
2774 setModified(IsModified_MachineData);
2775 mHWData.backup();
2776 mHWData->mDragAndDropMode = aDragAndDropMode;
2777
2778 /* Save settings if online - todo why is this required?? */
2779 if (Global::IsOnline(mData->mMachineState))
2780 saveSettings(NULL);
2781
2782 return S_OK;
2783}
2784
2785STDMETHODIMP
2786Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
2787{
2788 CheckComArgOutPointerValid(aPatterns);
2789
2790 AutoCaller autoCaller(this);
2791 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2792
2793 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2794
2795 try
2796 {
2797 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
2798 }
2799 catch (...)
2800 {
2801 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2802 }
2803
2804 return S_OK;
2805}
2806
2807STDMETHODIMP
2808Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
2809{
2810 AutoCaller autoCaller(this);
2811 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2812
2813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2814
2815 HRESULT rc = checkStateDependency(MutableStateDep);
2816 if (FAILED(rc)) return rc;
2817
2818 setModified(IsModified_MachineData);
2819 mHWData.backup();
2820 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
2821 return rc;
2822}
2823
2824STDMETHODIMP
2825Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
2826{
2827 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
2828
2829 AutoCaller autoCaller(this);
2830 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2831
2832 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2833
2834 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
2835 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
2836
2837 return S_OK;
2838}
2839
2840STDMETHODIMP
2841Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
2842{
2843 CheckComArgOutPointerValid(aEnabled);
2844
2845 AutoCaller autoCaller(this);
2846 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2847
2848 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2849
2850 *aEnabled = mUserData->s.fTeleporterEnabled;
2851
2852 return S_OK;
2853}
2854
2855STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
2856{
2857 AutoCaller autoCaller(this);
2858 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2859
2860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2861
2862 /* Only allow it to be set to true when PoweredOff or Aborted.
2863 (Clearing it is always permitted.) */
2864 if ( aEnabled
2865 && mData->mRegistered
2866 && ( !isSessionMachine()
2867 || ( mData->mMachineState != MachineState_PoweredOff
2868 && mData->mMachineState != MachineState_Teleported
2869 && mData->mMachineState != MachineState_Aborted
2870 )
2871 )
2872 )
2873 return setError(VBOX_E_INVALID_VM_STATE,
2874 tr("The machine is not powered off (state is %s)"),
2875 Global::stringifyMachineState(mData->mMachineState));
2876
2877 setModified(IsModified_MachineData);
2878 mUserData.backup();
2879 mUserData->s.fTeleporterEnabled = !!aEnabled;
2880
2881 return S_OK;
2882}
2883
2884STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
2885{
2886 CheckComArgOutPointerValid(aPort);
2887
2888 AutoCaller autoCaller(this);
2889 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2890
2891 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2892
2893 *aPort = (ULONG)mUserData->s.uTeleporterPort;
2894
2895 return S_OK;
2896}
2897
2898STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
2899{
2900 if (aPort >= _64K)
2901 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
2902
2903 AutoCaller autoCaller(this);
2904 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2905
2906 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2907
2908 HRESULT rc = checkStateDependency(MutableStateDep);
2909 if (FAILED(rc)) return rc;
2910
2911 setModified(IsModified_MachineData);
2912 mUserData.backup();
2913 mUserData->s.uTeleporterPort = (uint32_t)aPort;
2914
2915 return S_OK;
2916}
2917
2918STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
2919{
2920 CheckComArgOutPointerValid(aAddress);
2921
2922 AutoCaller autoCaller(this);
2923 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2924
2925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2926
2927 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
2928
2929 return S_OK;
2930}
2931
2932STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
2933{
2934 AutoCaller autoCaller(this);
2935 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2936
2937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2938
2939 HRESULT rc = checkStateDependency(MutableStateDep);
2940 if (FAILED(rc)) return rc;
2941
2942 setModified(IsModified_MachineData);
2943 mUserData.backup();
2944 mUserData->s.strTeleporterAddress = aAddress;
2945
2946 return S_OK;
2947}
2948
2949STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
2950{
2951 CheckComArgOutPointerValid(aPassword);
2952
2953 AutoCaller autoCaller(this);
2954 HRESULT hrc = autoCaller.rc();
2955 if (SUCCEEDED(hrc))
2956 {
2957 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2958 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
2959 }
2960
2961 return hrc;
2962}
2963
2964STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
2965{
2966 /*
2967 * Hash the password first.
2968 */
2969 Utf8Str strPassword(aPassword);
2970 if (!strPassword.isEmpty())
2971 {
2972 if (VBoxIsPasswordHashed(&strPassword))
2973 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2974 VBoxHashPassword(&strPassword);
2975 }
2976
2977 /*
2978 * Do the update.
2979 */
2980 AutoCaller autoCaller(this);
2981 HRESULT hrc = autoCaller.rc();
2982 if (SUCCEEDED(hrc))
2983 {
2984 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2985 hrc = checkStateDependency(MutableStateDep);
2986 if (SUCCEEDED(hrc))
2987 {
2988 setModified(IsModified_MachineData);
2989 mUserData.backup();
2990 mUserData->s.strTeleporterPassword = strPassword;
2991 }
2992 }
2993
2994 return hrc;
2995}
2996
2997STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
2998{
2999 CheckComArgOutPointerValid(aState);
3000
3001 AutoCaller autoCaller(this);
3002 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3003
3004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3005
3006 *aState = mUserData->s.enmFaultToleranceState;
3007 return S_OK;
3008}
3009
3010STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
3011{
3012 AutoCaller autoCaller(this);
3013 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3014
3015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3016
3017 /* @todo deal with running state change. */
3018 HRESULT rc = checkStateDependency(MutableStateDep);
3019 if (FAILED(rc)) return rc;
3020
3021 setModified(IsModified_MachineData);
3022 mUserData.backup();
3023 mUserData->s.enmFaultToleranceState = aState;
3024 return S_OK;
3025}
3026
3027STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3028{
3029 CheckComArgOutPointerValid(aAddress);
3030
3031 AutoCaller autoCaller(this);
3032 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3033
3034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3035
3036 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3037 return S_OK;
3038}
3039
3040STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3041{
3042 AutoCaller autoCaller(this);
3043 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3044
3045 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3046
3047 /* @todo deal with running state change. */
3048 HRESULT rc = checkStateDependency(MutableStateDep);
3049 if (FAILED(rc)) return rc;
3050
3051 setModified(IsModified_MachineData);
3052 mUserData.backup();
3053 mUserData->s.strFaultToleranceAddress = aAddress;
3054 return S_OK;
3055}
3056
3057STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3058{
3059 CheckComArgOutPointerValid(aPort);
3060
3061 AutoCaller autoCaller(this);
3062 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3063
3064 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3065
3066 *aPort = mUserData->s.uFaultTolerancePort;
3067 return S_OK;
3068}
3069
3070STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3071{
3072 AutoCaller autoCaller(this);
3073 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3074
3075 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3076
3077 /* @todo deal with running state change. */
3078 HRESULT rc = checkStateDependency(MutableStateDep);
3079 if (FAILED(rc)) return rc;
3080
3081 setModified(IsModified_MachineData);
3082 mUserData.backup();
3083 mUserData->s.uFaultTolerancePort = aPort;
3084 return S_OK;
3085}
3086
3087STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3088{
3089 CheckComArgOutPointerValid(aPassword);
3090
3091 AutoCaller autoCaller(this);
3092 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3093
3094 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3095
3096 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3097
3098 return S_OK;
3099}
3100
3101STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3102{
3103 AutoCaller autoCaller(this);
3104 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3105
3106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3107
3108 /* @todo deal with running state change. */
3109 HRESULT rc = checkStateDependency(MutableStateDep);
3110 if (FAILED(rc)) return rc;
3111
3112 setModified(IsModified_MachineData);
3113 mUserData.backup();
3114 mUserData->s.strFaultTolerancePassword = aPassword;
3115
3116 return S_OK;
3117}
3118
3119STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3120{
3121 CheckComArgOutPointerValid(aInterval);
3122
3123 AutoCaller autoCaller(this);
3124 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3125
3126 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3127
3128 *aInterval = mUserData->s.uFaultToleranceInterval;
3129 return S_OK;
3130}
3131
3132STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3133{
3134 AutoCaller autoCaller(this);
3135 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3136
3137 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3138
3139 /* @todo deal with running state change. */
3140 HRESULT rc = checkStateDependency(MutableStateDep);
3141 if (FAILED(rc)) return rc;
3142
3143 setModified(IsModified_MachineData);
3144 mUserData.backup();
3145 mUserData->s.uFaultToleranceInterval = aInterval;
3146 return S_OK;
3147}
3148
3149STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3150{
3151 CheckComArgOutPointerValid(aEnabled);
3152
3153 AutoCaller autoCaller(this);
3154 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3155
3156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3157
3158 *aEnabled = mUserData->s.fRTCUseUTC;
3159
3160 return S_OK;
3161}
3162
3163STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3164{
3165 AutoCaller autoCaller(this);
3166 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3167
3168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3169
3170 /* Only allow it to be set to true when PoweredOff or Aborted.
3171 (Clearing it is always permitted.) */
3172 if ( aEnabled
3173 && mData->mRegistered
3174 && ( !isSessionMachine()
3175 || ( mData->mMachineState != MachineState_PoweredOff
3176 && mData->mMachineState != MachineState_Teleported
3177 && mData->mMachineState != MachineState_Aborted
3178 )
3179 )
3180 )
3181 return setError(VBOX_E_INVALID_VM_STATE,
3182 tr("The machine is not powered off (state is %s)"),
3183 Global::stringifyMachineState(mData->mMachineState));
3184
3185 setModified(IsModified_MachineData);
3186 mUserData.backup();
3187 mUserData->s.fRTCUseUTC = !!aEnabled;
3188
3189 return S_OK;
3190}
3191
3192STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3193{
3194 CheckComArgOutPointerValid(aEnabled);
3195
3196 AutoCaller autoCaller(this);
3197 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3198
3199 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3200
3201 *aEnabled = mHWData->mIOCacheEnabled;
3202
3203 return S_OK;
3204}
3205
3206STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3207{
3208 AutoCaller autoCaller(this);
3209 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3210
3211 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3212
3213 HRESULT rc = checkStateDependency(MutableStateDep);
3214 if (FAILED(rc)) return rc;
3215
3216 setModified(IsModified_MachineData);
3217 mHWData.backup();
3218 mHWData->mIOCacheEnabled = aEnabled;
3219
3220 return S_OK;
3221}
3222
3223STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3224{
3225 CheckComArgOutPointerValid(aIOCacheSize);
3226
3227 AutoCaller autoCaller(this);
3228 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3229
3230 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3231
3232 *aIOCacheSize = mHWData->mIOCacheSize;
3233
3234 return S_OK;
3235}
3236
3237STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3238{
3239 AutoCaller autoCaller(this);
3240 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3241
3242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3243
3244 HRESULT rc = checkStateDependency(MutableStateDep);
3245 if (FAILED(rc)) return rc;
3246
3247 setModified(IsModified_MachineData);
3248 mHWData.backup();
3249 mHWData->mIOCacheSize = aIOCacheSize;
3250
3251 return S_OK;
3252}
3253
3254
3255/**
3256 * @note Locks objects!
3257 */
3258STDMETHODIMP Machine::LockMachine(ISession *aSession,
3259 LockType_T lockType)
3260{
3261 CheckComArgNotNull(aSession);
3262
3263 AutoCaller autoCaller(this);
3264 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3265
3266 /* check the session state */
3267 SessionState_T state;
3268 HRESULT rc = aSession->COMGETTER(State)(&state);
3269 if (FAILED(rc)) return rc;
3270
3271 if (state != SessionState_Unlocked)
3272 return setError(VBOX_E_INVALID_OBJECT_STATE,
3273 tr("The given session is busy"));
3274
3275 // get the client's IInternalSessionControl interface
3276 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3277 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3278 E_INVALIDARG);
3279
3280 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3281
3282 if (!mData->mRegistered)
3283 return setError(E_UNEXPECTED,
3284 tr("The machine '%s' is not registered"),
3285 mUserData->s.strName.c_str());
3286
3287 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3288
3289 SessionState_T oldState = mData->mSession.mState;
3290 /* Hack: in case the session is closing and there is a progress object
3291 * which allows waiting for the session to be closed, take the opportunity
3292 * and do a limited wait (max. 1 second). This helps a lot when the system
3293 * is busy and thus session closing can take a little while. */
3294 if ( mData->mSession.mState == SessionState_Unlocking
3295 && mData->mSession.mProgress)
3296 {
3297 alock.release();
3298 mData->mSession.mProgress->WaitForCompletion(1000);
3299 alock.acquire();
3300 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3301 }
3302
3303 // try again now
3304 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3305 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3306 )
3307 {
3308 // OK, share the session... we are now dealing with three processes:
3309 // 1) VBoxSVC (where this code runs);
3310 // 2) process C: the caller's client process (who wants a shared session);
3311 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3312
3313 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3314 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3315 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3316 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3317 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3318
3319 /*
3320 * Release the lock before calling the client process. It's safe here
3321 * since the only thing to do after we get the lock again is to add
3322 * the remote control to the list (which doesn't directly influence
3323 * anything).
3324 */
3325 alock.release();
3326
3327 // get the console of the session holding the write lock (this is a remote call)
3328 ComPtr<IConsole> pConsoleW;
3329 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3330 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3331 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3332 if (FAILED(rc))
3333 // the failure may occur w/o any error info (from RPC), so provide one
3334 return setError(VBOX_E_VM_ERROR,
3335 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3336
3337 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3338
3339 // share the session machine and W's console with the caller's session
3340 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3341 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3342 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3343
3344 if (FAILED(rc))
3345 // the failure may occur w/o any error info (from RPC), so provide one
3346 return setError(VBOX_E_VM_ERROR,
3347 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3348 alock.acquire();
3349
3350 // need to revalidate the state after acquiring the lock again
3351 if (mData->mSession.mState != SessionState_Locked)
3352 {
3353 pSessionControl->Uninitialize();
3354 return setError(VBOX_E_INVALID_SESSION_STATE,
3355 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3356 mUserData->s.strName.c_str());
3357 }
3358
3359 // add the caller's session to the list
3360 mData->mSession.mRemoteControls.push_back(pSessionControl);
3361 }
3362 else if ( mData->mSession.mState == SessionState_Locked
3363 || mData->mSession.mState == SessionState_Unlocking
3364 )
3365 {
3366 // sharing not permitted, or machine still unlocking:
3367 return setError(VBOX_E_INVALID_OBJECT_STATE,
3368 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3369 mUserData->s.strName.c_str());
3370 }
3371 else
3372 {
3373 // machine is not locked: then write-lock the machine (create the session machine)
3374
3375 // must not be busy
3376 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3377
3378 // get the caller's session PID
3379 RTPROCESS pid = NIL_RTPROCESS;
3380 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3381 pSessionControl->GetPID((ULONG*)&pid);
3382 Assert(pid != NIL_RTPROCESS);
3383
3384 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3385
3386 if (fLaunchingVMProcess)
3387 {
3388 // this machine is awaiting for a spawning session to be opened:
3389 // then the calling process must be the one that got started by
3390 // LaunchVMProcess()
3391
3392 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3393 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3394
3395 if (mData->mSession.mPID != pid)
3396 return setError(E_ACCESSDENIED,
3397 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3398 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3399 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3400 }
3401
3402 // create the mutable SessionMachine from the current machine
3403 ComObjPtr<SessionMachine> sessionMachine;
3404 sessionMachine.createObject();
3405 rc = sessionMachine->init(this);
3406 AssertComRC(rc);
3407
3408 /* NOTE: doing return from this function after this point but
3409 * before the end is forbidden since it may call SessionMachine::uninit()
3410 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3411 * lock while still holding the Machine lock in alock so that a deadlock
3412 * is possible due to the wrong lock order. */
3413
3414 if (SUCCEEDED(rc))
3415 {
3416 /*
3417 * Set the session state to Spawning to protect against subsequent
3418 * attempts to open a session and to unregister the machine after
3419 * we release the lock.
3420 */
3421 SessionState_T origState = mData->mSession.mState;
3422 mData->mSession.mState = SessionState_Spawning;
3423
3424 /*
3425 * Release the lock before calling the client process -- it will call
3426 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3427 * because the state is Spawning, so that LaunchVMProcess() and
3428 * LockMachine() calls will fail. This method, called before we
3429 * acquire the lock again, will fail because of the wrong PID.
3430 *
3431 * Note that mData->mSession.mRemoteControls accessed outside
3432 * the lock may not be modified when state is Spawning, so it's safe.
3433 */
3434 alock.release();
3435
3436 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3437 rc = pSessionControl->AssignMachine(sessionMachine, lockType);
3438 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3439
3440 /* The failure may occur w/o any error info (from RPC), so provide one */
3441 if (FAILED(rc))
3442 setError(VBOX_E_VM_ERROR,
3443 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3444
3445 if ( SUCCEEDED(rc)
3446 && fLaunchingVMProcess
3447 )
3448 {
3449 /* complete the remote session initialization */
3450
3451 /* get the console from the direct session */
3452 ComPtr<IConsole> console;
3453 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3454 ComAssertComRC(rc);
3455
3456 if (SUCCEEDED(rc) && !console)
3457 {
3458 ComAssert(!!console);
3459 rc = E_FAIL;
3460 }
3461
3462 /* assign machine & console to the remote session */
3463 if (SUCCEEDED(rc))
3464 {
3465 /*
3466 * after LaunchVMProcess(), the first and the only
3467 * entry in remoteControls is that remote session
3468 */
3469 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3470 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3471 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3472
3473 /* The failure may occur w/o any error info (from RPC), so provide one */
3474 if (FAILED(rc))
3475 setError(VBOX_E_VM_ERROR,
3476 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3477 }
3478
3479 if (FAILED(rc))
3480 pSessionControl->Uninitialize();
3481 }
3482
3483 /* acquire the lock again */
3484 alock.acquire();
3485
3486 /* Restore the session state */
3487 mData->mSession.mState = origState;
3488 }
3489
3490 // finalize spawning anyway (this is why we don't return on errors above)
3491 if (fLaunchingVMProcess)
3492 {
3493 /* Note that the progress object is finalized later */
3494 /** @todo Consider checking mData->mSession.mProgress for cancellation
3495 * around here. */
3496
3497 /* We don't reset mSession.mPID here because it is necessary for
3498 * SessionMachine::uninit() to reap the child process later. */
3499
3500 if (FAILED(rc))
3501 {
3502 /* Close the remote session, remove the remote control from the list
3503 * and reset session state to Closed (@note keep the code in sync
3504 * with the relevant part in openSession()). */
3505
3506 Assert(mData->mSession.mRemoteControls.size() == 1);
3507 if (mData->mSession.mRemoteControls.size() == 1)
3508 {
3509 ErrorInfoKeeper eik;
3510 mData->mSession.mRemoteControls.front()->Uninitialize();
3511 }
3512
3513 mData->mSession.mRemoteControls.clear();
3514 mData->mSession.mState = SessionState_Unlocked;
3515 }
3516 }
3517 else
3518 {
3519 /* memorize PID of the directly opened session */
3520 if (SUCCEEDED(rc))
3521 mData->mSession.mPID = pid;
3522 }
3523
3524 if (SUCCEEDED(rc))
3525 {
3526 /* memorize the direct session control and cache IUnknown for it */
3527 mData->mSession.mDirectControl = pSessionControl;
3528 mData->mSession.mState = SessionState_Locked;
3529 /* associate the SessionMachine with this Machine */
3530 mData->mSession.mMachine = sessionMachine;
3531
3532 /* request an IUnknown pointer early from the remote party for later
3533 * identity checks (it will be internally cached within mDirectControl
3534 * at least on XPCOM) */
3535 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3536 NOREF(unk);
3537 }
3538
3539 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3540 * would break the lock order */
3541 alock.release();
3542
3543 /* uninitialize the created session machine on failure */
3544 if (FAILED(rc))
3545 sessionMachine->uninit();
3546
3547 }
3548
3549 if (SUCCEEDED(rc))
3550 {
3551 /*
3552 * tell the client watcher thread to update the set of
3553 * machines that have open sessions
3554 */
3555 mParent->updateClientWatcher();
3556
3557 if (oldState != SessionState_Locked)
3558 /* fire an event */
3559 mParent->onSessionStateChange(getId(), SessionState_Locked);
3560 }
3561
3562 return rc;
3563}
3564
3565/**
3566 * @note Locks objects!
3567 */
3568STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3569 IN_BSTR aFrontend,
3570 IN_BSTR aEnvironment,
3571 IProgress **aProgress)
3572{
3573 CheckComArgStr(aFrontend);
3574 Utf8Str strFrontend(aFrontend);
3575 Utf8Str strEnvironment(aEnvironment);
3576 /* "emergencystop" doesn't need the session, so skip the checks/interface
3577 * retrieval. This code doesn't quite fit in here, but introducing a
3578 * special API method would be even more effort, and would require explicit
3579 * support by every API client. It's better to hide the feature a bit. */
3580 if (strFrontend != "emergencystop")
3581 CheckComArgNotNull(aSession);
3582 CheckComArgOutPointerValid(aProgress);
3583
3584 AutoCaller autoCaller(this);
3585 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3586
3587 HRESULT rc = S_OK;
3588 if (strFrontend.isEmpty())
3589 {
3590 Bstr bstrFrontend;
3591 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3592 if (FAILED(rc))
3593 return rc;
3594 strFrontend = bstrFrontend;
3595 if (strFrontend.isEmpty())
3596 {
3597 ComPtr<ISystemProperties> systemProperties;
3598 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3599 if (FAILED(rc))
3600 return rc;
3601 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3602 if (FAILED(rc))
3603 return rc;
3604 strFrontend = bstrFrontend;
3605 }
3606 /* paranoia - emergencystop is not a valid default */
3607 if (strFrontend == "emergencystop")
3608 strFrontend = Utf8Str::Empty;
3609 }
3610
3611 if (strFrontend != "emergencystop")
3612 {
3613 /* check the session state */
3614 SessionState_T state;
3615 rc = aSession->COMGETTER(State)(&state);
3616 if (FAILED(rc))
3617 return rc;
3618
3619 if (state != SessionState_Unlocked)
3620 return setError(VBOX_E_INVALID_OBJECT_STATE,
3621 tr("The given session is busy"));
3622
3623 /* get the IInternalSessionControl interface */
3624 ComPtr<IInternalSessionControl> control(aSession);
3625 ComAssertMsgRet(!control.isNull(),
3626 ("No IInternalSessionControl interface"),
3627 E_INVALIDARG);
3628
3629 /* get the teleporter enable state for the progress object init. */
3630 BOOL fTeleporterEnabled;
3631 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3632 if (FAILED(rc))
3633 return rc;
3634
3635 /* create a progress object */
3636 ComObjPtr<ProgressProxy> progress;
3637 progress.createObject();
3638 rc = progress->init(mParent,
3639 static_cast<IMachine*>(this),
3640 Bstr(tr("Starting VM")).raw(),
3641 TRUE /* aCancelable */,
3642 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3643 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3644 2 /* uFirstOperationWeight */,
3645 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3646
3647 if (SUCCEEDED(rc))
3648 {
3649 rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
3650 if (SUCCEEDED(rc))
3651 {
3652 progress.queryInterfaceTo(aProgress);
3653
3654 /* signal the client watcher thread */
3655 mParent->updateClientWatcher();
3656
3657 /* fire an event */
3658 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3659 }
3660 }
3661 }
3662 else
3663 {
3664 /* no progress object - either instant success or failure */
3665 *aProgress = NULL;
3666
3667 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3668
3669 if (mData->mSession.mState != SessionState_Locked)
3670 return setError(VBOX_E_INVALID_OBJECT_STATE,
3671 tr("The machine '%s' is not locked by a session"),
3672 mUserData->s.strName.c_str());
3673
3674 /* must have a VM process associated - do not kill normal API clients
3675 * with an open session */
3676 if (!Global::IsOnline(mData->mMachineState))
3677 return setError(VBOX_E_INVALID_OBJECT_STATE,
3678 tr("The machine '%s' does not have a VM process"),
3679 mUserData->s.strName.c_str());
3680
3681 /* forcibly terminate the VM process */
3682 if (mData->mSession.mPID != NIL_RTPROCESS)
3683 RTProcTerminate(mData->mSession.mPID);
3684
3685 /* signal the client watcher thread, as most likely the client has
3686 * been terminated */
3687 mParent->updateClientWatcher();
3688 }
3689
3690 return rc;
3691}
3692
3693STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3694{
3695 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3696 return setError(E_INVALIDARG,
3697 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3698 aPosition, SchemaDefs::MaxBootPosition);
3699
3700 if (aDevice == DeviceType_USB)
3701 return setError(E_NOTIMPL,
3702 tr("Booting from USB device is currently not supported"));
3703
3704 AutoCaller autoCaller(this);
3705 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3706
3707 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3708
3709 HRESULT rc = checkStateDependency(MutableStateDep);
3710 if (FAILED(rc)) return rc;
3711
3712 setModified(IsModified_MachineData);
3713 mHWData.backup();
3714 mHWData->mBootOrder[aPosition - 1] = aDevice;
3715
3716 return S_OK;
3717}
3718
3719STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3720{
3721 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3722 return setError(E_INVALIDARG,
3723 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3724 aPosition, SchemaDefs::MaxBootPosition);
3725
3726 AutoCaller autoCaller(this);
3727 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3728
3729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3730
3731 *aDevice = mHWData->mBootOrder[aPosition - 1];
3732
3733 return S_OK;
3734}
3735
3736STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
3737 LONG aControllerPort,
3738 LONG aDevice,
3739 DeviceType_T aType,
3740 IMedium *aMedium)
3741{
3742 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3743 aControllerName, aControllerPort, aDevice, aType, aMedium));
3744
3745 CheckComArgStrNotEmptyOrNull(aControllerName);
3746
3747 AutoCaller autoCaller(this);
3748 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3749
3750 // request the host lock first, since might be calling Host methods for getting host drives;
3751 // next, protect the media tree all the while we're in here, as well as our member variables
3752 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
3753 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3754
3755 HRESULT rc = checkStateDependency(MutableStateDep);
3756 if (FAILED(rc)) return rc;
3757
3758 /// @todo NEWMEDIA implicit machine registration
3759 if (!mData->mRegistered)
3760 return setError(VBOX_E_INVALID_OBJECT_STATE,
3761 tr("Cannot attach storage devices to an unregistered machine"));
3762
3763 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3764
3765 /* Check for an existing controller. */
3766 ComObjPtr<StorageController> ctl;
3767 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
3768 if (FAILED(rc)) return rc;
3769
3770 StorageControllerType_T ctrlType;
3771 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3772 if (FAILED(rc))
3773 return setError(E_FAIL,
3774 tr("Could not get type of controller '%ls'"),
3775 aControllerName);
3776
3777 bool fSilent = false;
3778 Bstr strReconfig;
3779
3780 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3781 rc = mParent->GetExtraData(Bstr("StorageMgmt/SilentReconfigureWhilePaused").raw(), strReconfig.asOutParam());
3782 if (FAILED(rc))
3783 return rc;
3784 if ( mData->mMachineState == MachineState_Paused
3785 && strReconfig == "1")
3786 fSilent = true;
3787
3788 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3789 bool fHotplug = false;
3790 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3791 fHotplug = true;
3792
3793 if (fHotplug && !isControllerHotplugCapable(ctrlType))
3794 return setError(VBOX_E_INVALID_VM_STATE,
3795 tr("Controller '%ls' does not support hotplugging"),
3796 aControllerName);
3797
3798 // check that the port and device are not out of range
3799 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
3800 if (FAILED(rc)) return rc;
3801
3802 /* check if the device slot is already busy */
3803 MediumAttachment *pAttachTemp;
3804 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
3805 aControllerName,
3806 aControllerPort,
3807 aDevice)))
3808 {
3809 Medium *pMedium = pAttachTemp->getMedium();
3810 if (pMedium)
3811 {
3812 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3813 return setError(VBOX_E_OBJECT_IN_USE,
3814 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3815 pMedium->getLocationFull().c_str(),
3816 aControllerPort,
3817 aDevice,
3818 aControllerName);
3819 }
3820 else
3821 return setError(VBOX_E_OBJECT_IN_USE,
3822 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3823 aControllerPort, aDevice, aControllerName);
3824 }
3825
3826 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
3827 if (aMedium && medium.isNull())
3828 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3829
3830 AutoCaller mediumCaller(medium);
3831 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3832
3833 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3834
3835 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
3836 && !medium.isNull()
3837 )
3838 return setError(VBOX_E_OBJECT_IN_USE,
3839 tr("Medium '%s' is already attached to this virtual machine"),
3840 medium->getLocationFull().c_str());
3841
3842 if (!medium.isNull())
3843 {
3844 MediumType_T mtype = medium->getType();
3845 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3846 // For DVDs it's not written to the config file, so needs no global config
3847 // version bump. For floppies it's a new attribute "type", which is ignored
3848 // by older VirtualBox version, so needs no global config version bump either.
3849 // For hard disks this type is not accepted.
3850 if (mtype == MediumType_MultiAttach)
3851 {
3852 // This type is new with VirtualBox 4.0 and therefore requires settings
3853 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3854 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3855 // two reasons: The medium type is a property of the media registry tree, which
3856 // can reside in the global config file (for pre-4.0 media); we would therefore
3857 // possibly need to bump the global config version. We don't want to do that though
3858 // because that might make downgrading to pre-4.0 impossible.
3859 // As a result, we can only use these two new types if the medium is NOT in the
3860 // global registry:
3861 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
3862 if ( medium->isInRegistry(uuidGlobalRegistry)
3863 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3864 )
3865 return setError(VBOX_E_INVALID_OBJECT_STATE,
3866 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3867 "to machines that were created with VirtualBox 4.0 or later"),
3868 medium->getLocationFull().c_str());
3869 }
3870 }
3871
3872 bool fIndirect = false;
3873 if (!medium.isNull())
3874 fIndirect = medium->isReadOnly();
3875 bool associate = true;
3876
3877 do
3878 {
3879 if ( aType == DeviceType_HardDisk
3880 && mMediaData.isBackedUp())
3881 {
3882 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3883
3884 /* check if the medium was attached to the VM before we started
3885 * changing attachments in which case the attachment just needs to
3886 * be restored */
3887 if ((pAttachTemp = findAttachment(oldAtts, medium)))
3888 {
3889 AssertReturn(!fIndirect, E_FAIL);
3890
3891 /* see if it's the same bus/channel/device */
3892 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
3893 {
3894 /* the simplest case: restore the whole attachment
3895 * and return, nothing else to do */
3896 mMediaData->mAttachments.push_back(pAttachTemp);
3897 return S_OK;
3898 }
3899
3900 /* bus/channel/device differ; we need a new attachment object,
3901 * but don't try to associate it again */
3902 associate = false;
3903 break;
3904 }
3905 }
3906
3907 /* go further only if the attachment is to be indirect */
3908 if (!fIndirect)
3909 break;
3910
3911 /* perform the so called smart attachment logic for indirect
3912 * attachments. Note that smart attachment is only applicable to base
3913 * hard disks. */
3914
3915 if (medium->getParent().isNull())
3916 {
3917 /* first, investigate the backup copy of the current hard disk
3918 * attachments to make it possible to re-attach existing diffs to
3919 * another device slot w/o losing their contents */
3920 if (mMediaData.isBackedUp())
3921 {
3922 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3923
3924 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3925 uint32_t foundLevel = 0;
3926
3927 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
3928 it != oldAtts.end();
3929 ++it)
3930 {
3931 uint32_t level = 0;
3932 MediumAttachment *pAttach = *it;
3933 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3934 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3935 if (pMedium.isNull())
3936 continue;
3937
3938 if (pMedium->getBase(&level) == medium)
3939 {
3940 /* skip the hard disk if its currently attached (we
3941 * cannot attach the same hard disk twice) */
3942 if (findAttachment(mMediaData->mAttachments,
3943 pMedium))
3944 continue;
3945
3946 /* matched device, channel and bus (i.e. attached to the
3947 * same place) will win and immediately stop the search;
3948 * otherwise the attachment that has the youngest
3949 * descendant of medium will be used
3950 */
3951 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
3952 {
3953 /* the simplest case: restore the whole attachment
3954 * and return, nothing else to do */
3955 mMediaData->mAttachments.push_back(*it);
3956 return S_OK;
3957 }
3958 else if ( foundIt == oldAtts.end()
3959 || level > foundLevel /* prefer younger */
3960 )
3961 {
3962 foundIt = it;
3963 foundLevel = level;
3964 }
3965 }
3966 }
3967
3968 if (foundIt != oldAtts.end())
3969 {
3970 /* use the previously attached hard disk */
3971 medium = (*foundIt)->getMedium();
3972 mediumCaller.attach(medium);
3973 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3974 mediumLock.attach(medium);
3975 /* not implicit, doesn't require association with this VM */
3976 fIndirect = false;
3977 associate = false;
3978 /* go right to the MediumAttachment creation */
3979 break;
3980 }
3981 }
3982
3983 /* must give up the medium lock and medium tree lock as below we
3984 * go over snapshots, which needs a lock with higher lock order. */
3985 mediumLock.release();
3986 treeLock.release();
3987
3988 /* then, search through snapshots for the best diff in the given
3989 * hard disk's chain to base the new diff on */
3990
3991 ComObjPtr<Medium> base;
3992 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3993 while (snap)
3994 {
3995 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3996
3997 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
3998
3999 MediumAttachment *pAttachFound = NULL;
4000 uint32_t foundLevel = 0;
4001
4002 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
4003 it != snapAtts.end();
4004 ++it)
4005 {
4006 MediumAttachment *pAttach = *it;
4007 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4008 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4009 if (pMedium.isNull())
4010 continue;
4011
4012 uint32_t level = 0;
4013 if (pMedium->getBase(&level) == medium)
4014 {
4015 /* matched device, channel and bus (i.e. attached to the
4016 * same place) will win and immediately stop the search;
4017 * otherwise the attachment that has the youngest
4018 * descendant of medium will be used
4019 */
4020 if ( pAttach->getDevice() == aDevice
4021 && pAttach->getPort() == aControllerPort
4022 && pAttach->getControllerName() == aControllerName
4023 )
4024 {
4025 pAttachFound = pAttach;
4026 break;
4027 }
4028 else if ( !pAttachFound
4029 || level > foundLevel /* prefer younger */
4030 )
4031 {
4032 pAttachFound = pAttach;
4033 foundLevel = level;
4034 }
4035 }
4036 }
4037
4038 if (pAttachFound)
4039 {
4040 base = pAttachFound->getMedium();
4041 break;
4042 }
4043
4044 snap = snap->getParent();
4045 }
4046
4047 /* re-lock medium tree and the medium, as we need it below */
4048 treeLock.acquire();
4049 mediumLock.acquire();
4050
4051 /* found a suitable diff, use it as a base */
4052 if (!base.isNull())
4053 {
4054 medium = base;
4055 mediumCaller.attach(medium);
4056 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4057 mediumLock.attach(medium);
4058 }
4059 }
4060
4061 Utf8Str strFullSnapshotFolder;
4062 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4063
4064 ComObjPtr<Medium> diff;
4065 diff.createObject();
4066 // store this diff in the same registry as the parent
4067 Guid uuidRegistryParent;
4068 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4069 {
4070 // parent image has no registry: this can happen if we're attaching a new immutable
4071 // image that has not yet been attached (medium then points to the base and we're
4072 // creating the diff image for the immutable, and the parent is not yet registered);
4073 // put the parent in the machine registry then
4074 mediumLock.release();
4075 treeLock.release();
4076 alock.release();
4077 addMediumToRegistry(medium);
4078 alock.acquire();
4079 treeLock.acquire();
4080 mediumLock.acquire();
4081 medium->getFirstRegistryMachineId(uuidRegistryParent);
4082 }
4083 rc = diff->init(mParent,
4084 medium->getPreferredDiffFormat(),
4085 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4086 uuidRegistryParent);
4087 if (FAILED(rc)) return rc;
4088
4089 /* Apply the normal locking logic to the entire chain. */
4090 MediumLockList *pMediumLockList(new MediumLockList());
4091 mediumLock.release();
4092 treeLock.release();
4093 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4094 true /* fMediumLockWrite */,
4095 medium,
4096 *pMediumLockList);
4097 treeLock.acquire();
4098 mediumLock.acquire();
4099 if (SUCCEEDED(rc))
4100 {
4101 mediumLock.release();
4102 treeLock.release();
4103 rc = pMediumLockList->Lock();
4104 treeLock.acquire();
4105 mediumLock.acquire();
4106 if (FAILED(rc))
4107 setError(rc,
4108 tr("Could not lock medium when creating diff '%s'"),
4109 diff->getLocationFull().c_str());
4110 else
4111 {
4112 /* will release the lock before the potentially lengthy
4113 * operation, so protect with the special state */
4114 MachineState_T oldState = mData->mMachineState;
4115 setMachineState(MachineState_SettingUp);
4116
4117 mediumLock.release();
4118 treeLock.release();
4119 alock.release();
4120
4121 rc = medium->createDiffStorage(diff,
4122 MediumVariant_Standard,
4123 pMediumLockList,
4124 NULL /* aProgress */,
4125 true /* aWait */);
4126
4127 alock.acquire();
4128 treeLock.acquire();
4129 mediumLock.acquire();
4130
4131 setMachineState(oldState);
4132 }
4133 }
4134
4135 /* Unlock the media and free the associated memory. */
4136 delete pMediumLockList;
4137
4138 if (FAILED(rc)) return rc;
4139
4140 /* use the created diff for the actual attachment */
4141 medium = diff;
4142 mediumCaller.attach(medium);
4143 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4144 mediumLock.attach(medium);
4145 }
4146 while (0);
4147
4148 ComObjPtr<MediumAttachment> attachment;
4149 attachment.createObject();
4150 rc = attachment->init(this,
4151 medium,
4152 aControllerName,
4153 aControllerPort,
4154 aDevice,
4155 aType,
4156 fIndirect,
4157 false /* fPassthrough */,
4158 false /* fTempEject */,
4159 false /* fNonRotational */,
4160 false /* fDiscard */,
4161 Utf8Str::Empty);
4162 if (FAILED(rc)) return rc;
4163
4164 if (associate && !medium.isNull())
4165 {
4166 // as the last step, associate the medium to the VM
4167 rc = medium->addBackReference(mData->mUuid);
4168 // here we can fail because of Deleting, or being in process of creating a Diff
4169 if (FAILED(rc)) return rc;
4170
4171 mediumLock.release();
4172 treeLock.release();
4173 alock.release();
4174 addMediumToRegistry(medium);
4175 alock.acquire();
4176 treeLock.acquire();
4177 mediumLock.acquire();
4178 }
4179
4180 /* success: finally remember the attachment */
4181 setModified(IsModified_Storage);
4182 mMediaData.backup();
4183 mMediaData->mAttachments.push_back(attachment);
4184
4185 mediumLock.release();
4186 treeLock.release();
4187 alock.release();
4188
4189 if (fHotplug || fSilent)
4190 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4191
4192 mParent->saveModifiedRegistries();
4193
4194 return rc;
4195}
4196
4197STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4198 LONG aDevice)
4199{
4200 CheckComArgStrNotEmptyOrNull(aControllerName);
4201
4202 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4203 aControllerName, aControllerPort, aDevice));
4204
4205 AutoCaller autoCaller(this);
4206 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4207
4208 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4209
4210 HRESULT rc = checkStateDependency(MutableStateDep);
4211 if (FAILED(rc)) return rc;
4212
4213 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4214
4215 /* Check for an existing controller. */
4216 ComObjPtr<StorageController> ctl;
4217 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4218 if (FAILED(rc)) return rc;
4219
4220 StorageControllerType_T ctrlType;
4221 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4222 if (FAILED(rc))
4223 return setError(E_FAIL,
4224 tr("Could not get type of controller '%ls'"),
4225 aControllerName);
4226
4227 bool fSilent = false;
4228 Bstr strReconfig;
4229
4230 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4231 rc = mParent->GetExtraData(Bstr("StorageMgmt/SilentReconfigureWhilePaused").raw(), strReconfig.asOutParam());
4232 if (FAILED(rc))
4233 return rc;
4234 if ( mData->mMachineState == MachineState_Paused
4235 && strReconfig == "1")
4236 fSilent = true;
4237
4238 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4239 bool fHotplug = false;
4240 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4241 fHotplug = true;
4242
4243 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4244 return setError(VBOX_E_INVALID_VM_STATE,
4245 tr("Controller '%ls' does not support hotplugging"),
4246 aControllerName);
4247
4248 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4249 aControllerName,
4250 aControllerPort,
4251 aDevice);
4252 if (!pAttach)
4253 return setError(VBOX_E_OBJECT_NOT_FOUND,
4254 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4255 aDevice, aControllerPort, aControllerName);
4256
4257 /*
4258 * The VM has to detach the device before we delete any implicit diffs.
4259 * If this fails we can roll back without loosing data.
4260 */
4261 if (fHotplug || fSilent)
4262 {
4263 alock.release();
4264 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4265 alock.acquire();
4266 }
4267 if (FAILED(rc)) return rc;
4268
4269 /* If we are here everything went well and we can delete the implicit now. */
4270 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4271
4272 alock.release();
4273
4274 mParent->saveModifiedRegistries();
4275
4276 return rc;
4277}
4278
4279STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4280 LONG aDevice, BOOL aPassthrough)
4281{
4282 CheckComArgStrNotEmptyOrNull(aControllerName);
4283
4284 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4285 aControllerName, aControllerPort, aDevice, aPassthrough));
4286
4287 AutoCaller autoCaller(this);
4288 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4289
4290 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4291
4292 HRESULT rc = checkStateDependency(MutableStateDep);
4293 if (FAILED(rc)) return rc;
4294
4295 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4296
4297 if (Global::IsOnlineOrTransient(mData->mMachineState))
4298 return setError(VBOX_E_INVALID_VM_STATE,
4299 tr("Invalid machine state: %s"),
4300 Global::stringifyMachineState(mData->mMachineState));
4301
4302 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4303 aControllerName,
4304 aControllerPort,
4305 aDevice);
4306 if (!pAttach)
4307 return setError(VBOX_E_OBJECT_NOT_FOUND,
4308 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4309 aDevice, aControllerPort, aControllerName);
4310
4311
4312 setModified(IsModified_Storage);
4313 mMediaData.backup();
4314
4315 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4316
4317 if (pAttach->getType() != DeviceType_DVD)
4318 return setError(E_INVALIDARG,
4319 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4320 aDevice, aControllerPort, aControllerName);
4321 pAttach->updatePassthrough(!!aPassthrough);
4322
4323 return S_OK;
4324}
4325
4326STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4327 LONG aDevice, BOOL aTemporaryEject)
4328{
4329 CheckComArgStrNotEmptyOrNull(aControllerName);
4330
4331 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4332 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4333
4334 AutoCaller autoCaller(this);
4335 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4336
4337 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4338
4339 HRESULT rc = checkStateDependency(MutableStateDep);
4340 if (FAILED(rc)) return rc;
4341
4342 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4343 aControllerName,
4344 aControllerPort,
4345 aDevice);
4346 if (!pAttach)
4347 return setError(VBOX_E_OBJECT_NOT_FOUND,
4348 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4349 aDevice, aControllerPort, aControllerName);
4350
4351
4352 setModified(IsModified_Storage);
4353 mMediaData.backup();
4354
4355 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4356
4357 if (pAttach->getType() != DeviceType_DVD)
4358 return setError(E_INVALIDARG,
4359 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4360 aDevice, aControllerPort, aControllerName);
4361 pAttach->updateTempEject(!!aTemporaryEject);
4362
4363 return S_OK;
4364}
4365
4366STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4367 LONG aDevice, BOOL aNonRotational)
4368{
4369 CheckComArgStrNotEmptyOrNull(aControllerName);
4370
4371 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4372 aControllerName, aControllerPort, aDevice, aNonRotational));
4373
4374 AutoCaller autoCaller(this);
4375 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4376
4377 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4378
4379 HRESULT rc = checkStateDependency(MutableStateDep);
4380 if (FAILED(rc)) return rc;
4381
4382 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4383
4384 if (Global::IsOnlineOrTransient(mData->mMachineState))
4385 return setError(VBOX_E_INVALID_VM_STATE,
4386 tr("Invalid machine state: %s"),
4387 Global::stringifyMachineState(mData->mMachineState));
4388
4389 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4390 aControllerName,
4391 aControllerPort,
4392 aDevice);
4393 if (!pAttach)
4394 return setError(VBOX_E_OBJECT_NOT_FOUND,
4395 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4396 aDevice, aControllerPort, aControllerName);
4397
4398
4399 setModified(IsModified_Storage);
4400 mMediaData.backup();
4401
4402 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4403
4404 if (pAttach->getType() != DeviceType_HardDisk)
4405 return setError(E_INVALIDARG,
4406 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"),
4407 aDevice, aControllerPort, aControllerName);
4408 pAttach->updateNonRotational(!!aNonRotational);
4409
4410 return S_OK;
4411}
4412
4413STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4414 LONG aDevice, BOOL aDiscard)
4415{
4416 CheckComArgStrNotEmptyOrNull(aControllerName);
4417
4418 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4419 aControllerName, aControllerPort, aDevice, aDiscard));
4420
4421 AutoCaller autoCaller(this);
4422 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4423
4424 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4425
4426 HRESULT rc = checkStateDependency(MutableStateDep);
4427 if (FAILED(rc)) return rc;
4428
4429 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4430
4431 if (Global::IsOnlineOrTransient(mData->mMachineState))
4432 return setError(VBOX_E_INVALID_VM_STATE,
4433 tr("Invalid machine state: %s"),
4434 Global::stringifyMachineState(mData->mMachineState));
4435
4436 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4437 aControllerName,
4438 aControllerPort,
4439 aDevice);
4440 if (!pAttach)
4441 return setError(VBOX_E_OBJECT_NOT_FOUND,
4442 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4443 aDevice, aControllerPort, aControllerName);
4444
4445
4446 setModified(IsModified_Storage);
4447 mMediaData.backup();
4448
4449 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4450
4451 if (pAttach->getType() != DeviceType_HardDisk)
4452 return setError(E_INVALIDARG,
4453 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"),
4454 aDevice, aControllerPort, aControllerName);
4455 pAttach->updateDiscard(!!aDiscard);
4456
4457 return S_OK;
4458}
4459
4460STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4461 LONG aDevice)
4462{
4463 int rc = S_OK;
4464 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4465 aControllerName, aControllerPort, aDevice));
4466
4467 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4468
4469 return rc;
4470}
4471
4472STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4473 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4474{
4475 CheckComArgStrNotEmptyOrNull(aControllerName);
4476
4477 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4478 aControllerName, aControllerPort, aDevice));
4479
4480 AutoCaller autoCaller(this);
4481 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4482
4483 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4484
4485 HRESULT rc = checkStateDependency(MutableStateDep);
4486 if (FAILED(rc)) return rc;
4487
4488 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4489
4490 if (Global::IsOnlineOrTransient(mData->mMachineState))
4491 return setError(VBOX_E_INVALID_VM_STATE,
4492 tr("Invalid machine state: %s"),
4493 Global::stringifyMachineState(mData->mMachineState));
4494
4495 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4496 aControllerName,
4497 aControllerPort,
4498 aDevice);
4499 if (!pAttach)
4500 return setError(VBOX_E_OBJECT_NOT_FOUND,
4501 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4502 aDevice, aControllerPort, aControllerName);
4503
4504
4505 setModified(IsModified_Storage);
4506 mMediaData.backup();
4507
4508 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4509 if (aBandwidthGroup && group.isNull())
4510 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4511
4512 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4513
4514 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4515 if (strBandwidthGroupOld.isNotEmpty())
4516 {
4517 /* Get the bandwidth group object and release it - this must not fail. */
4518 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4519 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4520 Assert(SUCCEEDED(rc));
4521
4522 pBandwidthGroupOld->release();
4523 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4524 }
4525
4526 if (!group.isNull())
4527 {
4528 group->reference();
4529 pAttach->updateBandwidthGroup(group->getName());
4530 }
4531
4532 return S_OK;
4533}
4534
4535STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4536 LONG aControllerPort,
4537 LONG aDevice,
4538 DeviceType_T aType)
4539{
4540 HRESULT rc = S_OK;
4541
4542 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4543 aControllerName, aControllerPort, aDevice, aType));
4544
4545 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4546
4547 return rc;
4548}
4549
4550
4551
4552STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4553 LONG aControllerPort,
4554 LONG aDevice,
4555 BOOL aForce)
4556{
4557 int rc = S_OK;
4558 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4559 aControllerName, aControllerPort, aForce));
4560
4561 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4562
4563 return rc;
4564}
4565
4566STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4567 LONG aControllerPort,
4568 LONG aDevice,
4569 IMedium *aMedium,
4570 BOOL aForce)
4571{
4572 int rc = S_OK;
4573 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4574 aControllerName, aControllerPort, aDevice, aForce));
4575
4576 CheckComArgStrNotEmptyOrNull(aControllerName);
4577
4578 AutoCaller autoCaller(this);
4579 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4580
4581 // request the host lock first, since might be calling Host methods for getting host drives;
4582 // next, protect the media tree all the while we're in here, as well as our member variables
4583 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4584 this->lockHandle(),
4585 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4586
4587 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4588 aControllerName,
4589 aControllerPort,
4590 aDevice);
4591 if (pAttach.isNull())
4592 return setError(VBOX_E_OBJECT_NOT_FOUND,
4593 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4594 aDevice, aControllerPort, aControllerName);
4595
4596 /* Remember previously mounted medium. The medium before taking the
4597 * backup is not necessarily the same thing. */
4598 ComObjPtr<Medium> oldmedium;
4599 oldmedium = pAttach->getMedium();
4600
4601 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4602 if (aMedium && pMedium.isNull())
4603 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4604
4605 AutoCaller mediumCaller(pMedium);
4606 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4607
4608 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4609 if (pMedium)
4610 {
4611 DeviceType_T mediumType = pAttach->getType();
4612 switch (mediumType)
4613 {
4614 case DeviceType_DVD:
4615 case DeviceType_Floppy:
4616 break;
4617
4618 default:
4619 return setError(VBOX_E_INVALID_OBJECT_STATE,
4620 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
4621 aControllerPort,
4622 aDevice,
4623 aControllerName);
4624 }
4625 }
4626
4627 setModified(IsModified_Storage);
4628 mMediaData.backup();
4629
4630 {
4631 // The backup operation makes the pAttach reference point to the
4632 // old settings. Re-get the correct reference.
4633 pAttach = findAttachment(mMediaData->mAttachments,
4634 aControllerName,
4635 aControllerPort,
4636 aDevice);
4637 if (!oldmedium.isNull())
4638 oldmedium->removeBackReference(mData->mUuid);
4639 if (!pMedium.isNull())
4640 {
4641 pMedium->addBackReference(mData->mUuid);
4642
4643 mediumLock.release();
4644 multiLock.release();
4645 addMediumToRegistry(pMedium);
4646 multiLock.acquire();
4647 mediumLock.acquire();
4648 }
4649
4650 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4651 pAttach->updateMedium(pMedium);
4652 }
4653
4654 setModified(IsModified_Storage);
4655
4656 mediumLock.release();
4657 multiLock.release();
4658 rc = onMediumChange(pAttach, aForce);
4659 multiLock.acquire();
4660 mediumLock.acquire();
4661
4662 /* On error roll back this change only. */
4663 if (FAILED(rc))
4664 {
4665 if (!pMedium.isNull())
4666 pMedium->removeBackReference(mData->mUuid);
4667 pAttach = findAttachment(mMediaData->mAttachments,
4668 aControllerName,
4669 aControllerPort,
4670 aDevice);
4671 /* If the attachment is gone in the meantime, bail out. */
4672 if (pAttach.isNull())
4673 return rc;
4674 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4675 if (!oldmedium.isNull())
4676 oldmedium->addBackReference(mData->mUuid);
4677 pAttach->updateMedium(oldmedium);
4678 }
4679
4680 mediumLock.release();
4681 multiLock.release();
4682
4683 mParent->saveModifiedRegistries();
4684
4685 return rc;
4686}
4687
4688STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
4689 LONG aControllerPort,
4690 LONG aDevice,
4691 IMedium **aMedium)
4692{
4693 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4694 aControllerName, aControllerPort, aDevice));
4695
4696 CheckComArgStrNotEmptyOrNull(aControllerName);
4697 CheckComArgOutPointerValid(aMedium);
4698
4699 AutoCaller autoCaller(this);
4700 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4701
4702 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4703
4704 *aMedium = NULL;
4705
4706 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4707 aControllerName,
4708 aControllerPort,
4709 aDevice);
4710 if (pAttach.isNull())
4711 return setError(VBOX_E_OBJECT_NOT_FOUND,
4712 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4713 aDevice, aControllerPort, aControllerName);
4714
4715 pAttach->getMedium().queryInterfaceTo(aMedium);
4716
4717 return S_OK;
4718}
4719
4720STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
4721{
4722 CheckComArgOutPointerValid(port);
4723 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
4724
4725 AutoCaller autoCaller(this);
4726 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4727
4728 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4729
4730 mSerialPorts[slot].queryInterfaceTo(port);
4731
4732 return S_OK;
4733}
4734
4735STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
4736{
4737 CheckComArgOutPointerValid(port);
4738 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
4739
4740 AutoCaller autoCaller(this);
4741 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4742
4743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4744
4745 mParallelPorts[slot].queryInterfaceTo(port);
4746
4747 return S_OK;
4748}
4749
4750STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
4751{
4752 CheckComArgOutPointerValid(adapter);
4753 /* Do not assert if slot is out of range, just return the advertised
4754 status. testdriver/vbox.py triggers this in logVmInfo. */
4755 if (slot >= mNetworkAdapters.size())
4756 return setError(E_INVALIDARG,
4757 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4758 slot, mNetworkAdapters.size());
4759
4760 AutoCaller autoCaller(this);
4761 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4762
4763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4764
4765 mNetworkAdapters[slot].queryInterfaceTo(adapter);
4766
4767 return S_OK;
4768}
4769
4770STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
4771{
4772 CheckComArgOutSafeArrayPointerValid(aKeys);
4773
4774 AutoCaller autoCaller(this);
4775 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4776
4777 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4778
4779 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4780 int i = 0;
4781 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4782 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4783 ++it, ++i)
4784 {
4785 const Utf8Str &strKey = it->first;
4786 strKey.cloneTo(&saKeys[i]);
4787 }
4788 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
4789
4790 return S_OK;
4791 }
4792
4793 /**
4794 * @note Locks this object for reading.
4795 */
4796STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
4797 BSTR *aValue)
4798{
4799 CheckComArgStrNotEmptyOrNull(aKey);
4800 CheckComArgOutPointerValid(aValue);
4801
4802 AutoCaller autoCaller(this);
4803 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4804
4805 /* start with nothing found */
4806 Bstr bstrResult("");
4807
4808 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4809
4810 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
4811 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4812 // found:
4813 bstrResult = it->second; // source is a Utf8Str
4814
4815 /* return the result to caller (may be empty) */
4816 bstrResult.cloneTo(aValue);
4817
4818 return S_OK;
4819}
4820
4821 /**
4822 * @note Locks mParent for writing + this object for writing.
4823 */
4824STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
4825{
4826 CheckComArgStrNotEmptyOrNull(aKey);
4827
4828 AutoCaller autoCaller(this);
4829 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4830
4831 Utf8Str strKey(aKey);
4832 Utf8Str strValue(aValue);
4833 Utf8Str strOldValue; // empty
4834
4835 // locking note: we only hold the read lock briefly to look up the old value,
4836 // then release it and call the onExtraCanChange callbacks. There is a small
4837 // chance of a race insofar as the callback might be called twice if two callers
4838 // change the same key at the same time, but that's a much better solution
4839 // than the deadlock we had here before. The actual changing of the extradata
4840 // is then performed under the write lock and race-free.
4841
4842 // look up the old value first; if nothing has changed then we need not do anything
4843 {
4844 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4845 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
4846 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4847 strOldValue = it->second;
4848 }
4849
4850 bool fChanged;
4851 if ((fChanged = (strOldValue != strValue)))
4852 {
4853 // ask for permission from all listeners outside the locks;
4854 // onExtraDataCanChange() only briefly requests the VirtualBox
4855 // lock to copy the list of callbacks to invoke
4856 Bstr error;
4857 Bstr bstrValue(aValue);
4858
4859 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
4860 {
4861 const char *sep = error.isEmpty() ? "" : ": ";
4862 CBSTR err = error.raw();
4863 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4864 sep, err));
4865 return setError(E_ACCESSDENIED,
4866 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
4867 aKey,
4868 bstrValue.raw(),
4869 sep,
4870 err);
4871 }
4872
4873 // data is changing and change not vetoed: then write it out under the lock
4874 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4875
4876 if (isSnapshotMachine())
4877 {
4878 HRESULT rc = checkStateDependency(MutableStateDep);
4879 if (FAILED(rc)) return rc;
4880 }
4881
4882 if (strValue.isEmpty())
4883 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
4884 else
4885 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
4886 // creates a new key if needed
4887
4888 bool fNeedsGlobalSaveSettings = false;
4889 saveSettings(&fNeedsGlobalSaveSettings);
4890
4891 if (fNeedsGlobalSaveSettings)
4892 {
4893 // save the global settings; for that we should hold only the VirtualBox lock
4894 alock.release();
4895 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4896 mParent->saveSettings();
4897 }
4898 }
4899
4900 // fire notification outside the lock
4901 if (fChanged)
4902 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
4903
4904 return S_OK;
4905}
4906
4907STDMETHODIMP Machine::SaveSettings()
4908{
4909 AutoCaller autoCaller(this);
4910 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4911
4912 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4913
4914 /* when there was auto-conversion, we want to save the file even if
4915 * the VM is saved */
4916 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
4917 if (FAILED(rc)) return rc;
4918
4919 /* the settings file path may never be null */
4920 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4921
4922 /* save all VM data excluding snapshots */
4923 bool fNeedsGlobalSaveSettings = false;
4924 rc = saveSettings(&fNeedsGlobalSaveSettings);
4925 mlock.release();
4926
4927 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4928 {
4929 // save the global settings; for that we should hold only the VirtualBox lock
4930 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4931 rc = mParent->saveSettings();
4932 }
4933
4934 return rc;
4935}
4936
4937STDMETHODIMP Machine::DiscardSettings()
4938{
4939 AutoCaller autoCaller(this);
4940 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4941
4942 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4943
4944 HRESULT rc = checkStateDependency(MutableStateDep);
4945 if (FAILED(rc)) return rc;
4946
4947 /*
4948 * during this rollback, the session will be notified if data has
4949 * been actually changed
4950 */
4951 rollback(true /* aNotify */);
4952
4953 return S_OK;
4954}
4955
4956/** @note Locks objects! */
4957STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
4958 ComSafeArrayOut(IMedium*, aMedia))
4959{
4960 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
4961 AutoLimitedCaller autoCaller(this);
4962 AssertComRCReturnRC(autoCaller.rc());
4963
4964 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4965
4966 Guid id(getId());
4967
4968 if (mData->mSession.mState != SessionState_Unlocked)
4969 return setError(VBOX_E_INVALID_OBJECT_STATE,
4970 tr("Cannot unregister the machine '%s' while it is locked"),
4971 mUserData->s.strName.c_str());
4972
4973 // wait for state dependents to drop to zero
4974 ensureNoStateDependencies();
4975
4976 if (!mData->mAccessible)
4977 {
4978 // inaccessible maschines can only be unregistered; uninitialize ourselves
4979 // here because currently there may be no unregistered that are inaccessible
4980 // (this state combination is not supported). Note releasing the caller and
4981 // leaving the lock before calling uninit()
4982 alock.release();
4983 autoCaller.release();
4984
4985 uninit();
4986
4987 mParent->unregisterMachine(this, id);
4988 // calls VirtualBox::saveSettings()
4989
4990 return S_OK;
4991 }
4992
4993 HRESULT rc = S_OK;
4994
4995 // discard saved state
4996 if (mData->mMachineState == MachineState_Saved)
4997 {
4998 // add the saved state file to the list of files the caller should delete
4999 Assert(!mSSData->strStateFilePath.isEmpty());
5000 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5001
5002 mSSData->strStateFilePath.setNull();
5003
5004 // unconditionally set the machine state to powered off, we now
5005 // know no session has locked the machine
5006 mData->mMachineState = MachineState_PoweredOff;
5007 }
5008
5009 size_t cSnapshots = 0;
5010 if (mData->mFirstSnapshot)
5011 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5012 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
5013 // fail now before we start detaching media
5014 return setError(VBOX_E_INVALID_OBJECT_STATE,
5015 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5016 mUserData->s.strName.c_str(), cSnapshots);
5017
5018 // This list collects the medium objects from all medium attachments
5019 // which we will detach from the machine and its snapshots, in a specific
5020 // order which allows for closing all media without getting "media in use"
5021 // errors, simply by going through the list from the front to the back:
5022 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5023 // and must be closed before the parent media from the snapshots, or closing the parents
5024 // will fail because they still have children);
5025 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5026 // the root ("first") snapshot of the machine.
5027 MediaList llMedia;
5028
5029 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5030 && mMediaData->mAttachments.size()
5031 )
5032 {
5033 // we have media attachments: detach them all and add the Medium objects to our list
5034 if (cleanupMode != CleanupMode_UnregisterOnly)
5035 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5036 else
5037 return setError(VBOX_E_INVALID_OBJECT_STATE,
5038 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5039 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5040 }
5041
5042 if (cSnapshots)
5043 {
5044 // autoCleanup must be true here, or we would have failed above
5045
5046 // add the media from the medium attachments of the snapshots to llMedia
5047 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5048 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5049 // into the children first
5050
5051 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5052 MachineState_T oldState = mData->mMachineState;
5053 mData->mMachineState = MachineState_DeletingSnapshot;
5054
5055 // make a copy of the first snapshot so the refcount does not drop to 0
5056 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5057 // because of the AutoCaller voodoo)
5058 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5059
5060 // GO!
5061 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5062
5063 mData->mMachineState = oldState;
5064 }
5065
5066 if (FAILED(rc))
5067 {
5068 rollbackMedia();
5069 return rc;
5070 }
5071
5072 // commit all the media changes made above
5073 commitMedia();
5074
5075 mData->mRegistered = false;
5076
5077 // machine lock no longer needed
5078 alock.release();
5079
5080 // return media to caller
5081 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5082 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5083
5084 mParent->unregisterMachine(this, id);
5085 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5086
5087 return S_OK;
5088}
5089
5090struct Machine::DeleteTask
5091{
5092 ComObjPtr<Machine> pMachine;
5093 RTCList<ComPtr<IMedium> > llMediums;
5094 StringsList llFilesToDelete;
5095 ComObjPtr<Progress> pProgress;
5096};
5097
5098STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5099{
5100 LogFlowFuncEnter();
5101
5102 AutoCaller autoCaller(this);
5103 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5104
5105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5106
5107 HRESULT rc = checkStateDependency(MutableStateDep);
5108 if (FAILED(rc)) return rc;
5109
5110 if (mData->mRegistered)
5111 return setError(VBOX_E_INVALID_VM_STATE,
5112 tr("Cannot delete settings of a registered machine"));
5113
5114 DeleteTask *pTask = new DeleteTask;
5115 pTask->pMachine = this;
5116 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5117
5118 // collect files to delete
5119 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5120
5121 for (size_t i = 0; i < sfaMedia.size(); ++i)
5122 {
5123 IMedium *pIMedium(sfaMedia[i]);
5124 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5125 if (pMedium.isNull())
5126 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5127 SafeArray<BSTR> ids;
5128 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5129 if (FAILED(rc)) return rc;
5130 /* At this point the medium should not have any back references
5131 * anymore. If it has it is attached to another VM and *must* not
5132 * deleted. */
5133 if (ids.size() < 1)
5134 pTask->llMediums.append(pMedium);
5135 }
5136 if (mData->pMachineConfigFile->fileExists())
5137 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5138
5139 pTask->pProgress.createObject();
5140 pTask->pProgress->init(getVirtualBox(),
5141 static_cast<IMachine*>(this) /* aInitiator */,
5142 Bstr(tr("Deleting files")).raw(),
5143 true /* fCancellable */,
5144 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5145 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5146
5147 int vrc = RTThreadCreate(NULL,
5148 Machine::deleteThread,
5149 (void*)pTask,
5150 0,
5151 RTTHREADTYPE_MAIN_WORKER,
5152 0,
5153 "MachineDelete");
5154
5155 pTask->pProgress.queryInterfaceTo(aProgress);
5156
5157 if (RT_FAILURE(vrc))
5158 {
5159 delete pTask;
5160 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5161 }
5162
5163 LogFlowFuncLeave();
5164
5165 return S_OK;
5166}
5167
5168/**
5169 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5170 * calls Machine::deleteTaskWorker() on the actual machine object.
5171 * @param Thread
5172 * @param pvUser
5173 * @return
5174 */
5175/*static*/
5176DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5177{
5178 LogFlowFuncEnter();
5179
5180 DeleteTask *pTask = (DeleteTask*)pvUser;
5181 Assert(pTask);
5182 Assert(pTask->pMachine);
5183 Assert(pTask->pProgress);
5184
5185 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5186 pTask->pProgress->notifyComplete(rc);
5187
5188 delete pTask;
5189
5190 LogFlowFuncLeave();
5191
5192 NOREF(Thread);
5193
5194 return VINF_SUCCESS;
5195}
5196
5197/**
5198 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5199 * @param task
5200 * @return
5201 */
5202HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5203{
5204 AutoCaller autoCaller(this);
5205 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5206
5207 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5208
5209 HRESULT rc = S_OK;
5210
5211 try
5212 {
5213 ULONG uLogHistoryCount = 3;
5214 ComPtr<ISystemProperties> systemProperties;
5215 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5216 if (FAILED(rc)) throw rc;
5217
5218 if (!systemProperties.isNull())
5219 {
5220 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5221 if (FAILED(rc)) throw rc;
5222 }
5223
5224 MachineState_T oldState = mData->mMachineState;
5225 setMachineState(MachineState_SettingUp);
5226 alock.release();
5227 for (size_t i = 0; i < task.llMediums.size(); ++i)
5228 {
5229 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5230 {
5231 AutoCaller mac(pMedium);
5232 if (FAILED(mac.rc())) throw mac.rc();
5233 Utf8Str strLocation = pMedium->getLocationFull();
5234 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5235 if (FAILED(rc)) throw rc;
5236 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5237 }
5238 ComPtr<IProgress> pProgress2;
5239 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5240 if (FAILED(rc)) throw rc;
5241 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5242 if (FAILED(rc)) throw rc;
5243 /* Check the result of the asynchrony process. */
5244 LONG iRc;
5245 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5246 if (FAILED(rc)) throw rc;
5247 /* If the thread of the progress object has an error, then
5248 * retrieve the error info from there, or it'll be lost. */
5249 if (FAILED(iRc))
5250 throw setError(ProgressErrorInfo(pProgress2));
5251 }
5252 setMachineState(oldState);
5253 alock.acquire();
5254
5255 // delete the files pushed on the task list by Machine::Delete()
5256 // (this includes saved states of the machine and snapshots and
5257 // medium storage files from the IMedium list passed in, and the
5258 // machine XML file)
5259 StringsList::const_iterator it = task.llFilesToDelete.begin();
5260 while (it != task.llFilesToDelete.end())
5261 {
5262 const Utf8Str &strFile = *it;
5263 LogFunc(("Deleting file %s\n", strFile.c_str()));
5264 int vrc = RTFileDelete(strFile.c_str());
5265 if (RT_FAILURE(vrc))
5266 throw setError(VBOX_E_IPRT_ERROR,
5267 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5268
5269 ++it;
5270 if (it == task.llFilesToDelete.end())
5271 {
5272 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5273 if (FAILED(rc)) throw rc;
5274 break;
5275 }
5276
5277 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5278 if (FAILED(rc)) throw rc;
5279 }
5280
5281 /* delete the settings only when the file actually exists */
5282 if (mData->pMachineConfigFile->fileExists())
5283 {
5284 /* Delete any backup or uncommitted XML files. Ignore failures.
5285 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5286 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5287 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5288 RTFileDelete(otherXml.c_str());
5289 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5290 RTFileDelete(otherXml.c_str());
5291
5292 /* delete the Logs folder, nothing important should be left
5293 * there (we don't check for errors because the user might have
5294 * some private files there that we don't want to delete) */
5295 Utf8Str logFolder;
5296 getLogFolder(logFolder);
5297 Assert(logFolder.length());
5298 if (RTDirExists(logFolder.c_str()))
5299 {
5300 /* Delete all VBox.log[.N] files from the Logs folder
5301 * (this must be in sync with the rotation logic in
5302 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5303 * files that may have been created by the GUI. */
5304 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5305 logFolder.c_str(), RTPATH_DELIMITER);
5306 RTFileDelete(log.c_str());
5307 log = Utf8StrFmt("%s%cVBox.png",
5308 logFolder.c_str(), RTPATH_DELIMITER);
5309 RTFileDelete(log.c_str());
5310 for (int i = uLogHistoryCount; i > 0; i--)
5311 {
5312 log = Utf8StrFmt("%s%cVBox.log.%d",
5313 logFolder.c_str(), RTPATH_DELIMITER, i);
5314 RTFileDelete(log.c_str());
5315 log = Utf8StrFmt("%s%cVBox.png.%d",
5316 logFolder.c_str(), RTPATH_DELIMITER, i);
5317 RTFileDelete(log.c_str());
5318 }
5319
5320 RTDirRemove(logFolder.c_str());
5321 }
5322
5323 /* delete the Snapshots folder, nothing important should be left
5324 * there (we don't check for errors because the user might have
5325 * some private files there that we don't want to delete) */
5326 Utf8Str strFullSnapshotFolder;
5327 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5328 Assert(!strFullSnapshotFolder.isEmpty());
5329 if (RTDirExists(strFullSnapshotFolder.c_str()))
5330 RTDirRemove(strFullSnapshotFolder.c_str());
5331
5332 // delete the directory that contains the settings file, but only
5333 // if it matches the VM name
5334 Utf8Str settingsDir;
5335 if (isInOwnDir(&settingsDir))
5336 RTDirRemove(settingsDir.c_str());
5337 }
5338
5339 alock.release();
5340
5341 mParent->saveModifiedRegistries();
5342 }
5343 catch (HRESULT aRC) { rc = aRC; }
5344
5345 return rc;
5346}
5347
5348STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5349{
5350 CheckComArgOutPointerValid(aSnapshot);
5351
5352 AutoCaller autoCaller(this);
5353 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5354
5355 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5356
5357 ComObjPtr<Snapshot> pSnapshot;
5358 HRESULT rc;
5359
5360 if (!aNameOrId || !*aNameOrId)
5361 // null case (caller wants root snapshot): findSnapshotById() handles this
5362 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5363 else
5364 {
5365 Guid uuid(aNameOrId);
5366 if (uuid.isValid())
5367 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5368 else
5369 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5370 }
5371 pSnapshot.queryInterfaceTo(aSnapshot);
5372
5373 return rc;
5374}
5375
5376STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5377{
5378 CheckComArgStrNotEmptyOrNull(aName);
5379 CheckComArgStrNotEmptyOrNull(aHostPath);
5380
5381 AutoCaller autoCaller(this);
5382 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5383
5384 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5385
5386 HRESULT rc = checkStateDependency(MutableStateDep);
5387 if (FAILED(rc)) return rc;
5388
5389 Utf8Str strName(aName);
5390
5391 ComObjPtr<SharedFolder> sharedFolder;
5392 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5393 if (SUCCEEDED(rc))
5394 return setError(VBOX_E_OBJECT_IN_USE,
5395 tr("Shared folder named '%s' already exists"),
5396 strName.c_str());
5397
5398 sharedFolder.createObject();
5399 rc = sharedFolder->init(getMachine(),
5400 strName,
5401 aHostPath,
5402 !!aWritable,
5403 !!aAutoMount,
5404 true /* fFailOnError */);
5405 if (FAILED(rc)) return rc;
5406
5407 setModified(IsModified_SharedFolders);
5408 mHWData.backup();
5409 mHWData->mSharedFolders.push_back(sharedFolder);
5410
5411 /* inform the direct session if any */
5412 alock.release();
5413 onSharedFolderChange();
5414
5415 return S_OK;
5416}
5417
5418STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5419{
5420 CheckComArgStrNotEmptyOrNull(aName);
5421
5422 AutoCaller autoCaller(this);
5423 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5424
5425 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5426
5427 HRESULT rc = checkStateDependency(MutableStateDep);
5428 if (FAILED(rc)) return rc;
5429
5430 ComObjPtr<SharedFolder> sharedFolder;
5431 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5432 if (FAILED(rc)) return rc;
5433
5434 setModified(IsModified_SharedFolders);
5435 mHWData.backup();
5436 mHWData->mSharedFolders.remove(sharedFolder);
5437
5438 /* inform the direct session if any */
5439 alock.release();
5440 onSharedFolderChange();
5441
5442 return S_OK;
5443}
5444
5445STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5446{
5447 CheckComArgOutPointerValid(aCanShow);
5448
5449 /* start with No */
5450 *aCanShow = FALSE;
5451
5452 AutoCaller autoCaller(this);
5453 AssertComRCReturnRC(autoCaller.rc());
5454
5455 ComPtr<IInternalSessionControl> directControl;
5456 {
5457 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5458
5459 if (mData->mSession.mState != SessionState_Locked)
5460 return setError(VBOX_E_INVALID_VM_STATE,
5461 tr("Machine is not locked for session (session state: %s)"),
5462 Global::stringifySessionState(mData->mSession.mState));
5463
5464 directControl = mData->mSession.mDirectControl;
5465 }
5466
5467 /* ignore calls made after #OnSessionEnd() is called */
5468 if (!directControl)
5469 return S_OK;
5470
5471 LONG64 dummy;
5472 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5473}
5474
5475STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5476{
5477 CheckComArgOutPointerValid(aWinId);
5478
5479 AutoCaller autoCaller(this);
5480 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5481
5482 ComPtr<IInternalSessionControl> directControl;
5483 {
5484 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5485
5486 if (mData->mSession.mState != SessionState_Locked)
5487 return setError(E_FAIL,
5488 tr("Machine is not locked for session (session state: %s)"),
5489 Global::stringifySessionState(mData->mSession.mState));
5490
5491 directControl = mData->mSession.mDirectControl;
5492 }
5493
5494 /* ignore calls made after #OnSessionEnd() is called */
5495 if (!directControl)
5496 return S_OK;
5497
5498 BOOL dummy;
5499 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5500}
5501
5502#ifdef VBOX_WITH_GUEST_PROPS
5503/**
5504 * Look up a guest property in VBoxSVC's internal structures.
5505 */
5506HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5507 BSTR *aValue,
5508 LONG64 *aTimestamp,
5509 BSTR *aFlags) const
5510{
5511 using namespace guestProp;
5512
5513 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5514 Utf8Str strName(aName);
5515 HWData::GuestPropertyMap::const_iterator it =
5516 mHWData->mGuestProperties.find(strName);
5517
5518 if (it != mHWData->mGuestProperties.end())
5519 {
5520 char szFlags[MAX_FLAGS_LEN + 1];
5521 it->second.strValue.cloneTo(aValue);
5522 *aTimestamp = it->second.mTimestamp;
5523 writeFlags(it->second.mFlags, szFlags);
5524 Bstr(szFlags).cloneTo(aFlags);
5525 }
5526
5527 return S_OK;
5528}
5529
5530/**
5531 * Query the VM that a guest property belongs to for the property.
5532 * @returns E_ACCESSDENIED if the VM process is not available or not
5533 * currently handling queries and the lookup should then be done in
5534 * VBoxSVC.
5535 */
5536HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5537 BSTR *aValue,
5538 LONG64 *aTimestamp,
5539 BSTR *aFlags) const
5540{
5541 HRESULT rc;
5542 ComPtr<IInternalSessionControl> directControl;
5543 directControl = mData->mSession.mDirectControl;
5544
5545 /* fail if we were called after #OnSessionEnd() is called. This is a
5546 * silly race condition. */
5547
5548 if (!directControl)
5549 rc = E_ACCESSDENIED;
5550 else
5551 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5552 false /* isSetter */,
5553 aValue, aTimestamp, aFlags);
5554 return rc;
5555}
5556#endif // VBOX_WITH_GUEST_PROPS
5557
5558STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5559 BSTR *aValue,
5560 LONG64 *aTimestamp,
5561 BSTR *aFlags)
5562{
5563#ifndef VBOX_WITH_GUEST_PROPS
5564 ReturnComNotImplemented();
5565#else // VBOX_WITH_GUEST_PROPS
5566 CheckComArgStrNotEmptyOrNull(aName);
5567 CheckComArgOutPointerValid(aValue);
5568 CheckComArgOutPointerValid(aTimestamp);
5569 CheckComArgOutPointerValid(aFlags);
5570
5571 AutoCaller autoCaller(this);
5572 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5573
5574 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5575 if (rc == E_ACCESSDENIED)
5576 /* The VM is not running or the service is not (yet) accessible */
5577 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5578 return rc;
5579#endif // VBOX_WITH_GUEST_PROPS
5580}
5581
5582STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5583{
5584 LONG64 dummyTimestamp;
5585 Bstr dummyFlags;
5586 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5587}
5588
5589STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5590{
5591 Bstr dummyValue;
5592 Bstr dummyFlags;
5593 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5594}
5595
5596#ifdef VBOX_WITH_GUEST_PROPS
5597/**
5598 * Set a guest property in VBoxSVC's internal structures.
5599 */
5600HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5601 IN_BSTR aFlags)
5602{
5603 using namespace guestProp;
5604
5605 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5606 HRESULT rc = S_OK;
5607 HWData::GuestProperty property;
5608 property.mFlags = NILFLAG;
5609
5610 rc = checkStateDependency(MutableStateDep);
5611 if (FAILED(rc)) return rc;
5612
5613 try
5614 {
5615 Utf8Str utf8Name(aName);
5616 Utf8Str utf8Flags(aFlags);
5617 uint32_t fFlags = NILFLAG;
5618 if ( (aFlags != NULL)
5619 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags))
5620 )
5621 return setError(E_INVALIDARG,
5622 tr("Invalid guest property flag values: '%ls'"),
5623 aFlags);
5624
5625 HWData::GuestPropertyMap::iterator it =
5626 mHWData->mGuestProperties.find(utf8Name);
5627
5628 if (it == mHWData->mGuestProperties.end())
5629 {
5630 setModified(IsModified_MachineData);
5631 mHWData.backupEx();
5632
5633 RTTIMESPEC time;
5634 HWData::GuestProperty prop;
5635 prop.strValue = aValue;
5636 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5637 prop.mFlags = fFlags;
5638
5639 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
5640 }
5641 else
5642 {
5643 if (it->second.mFlags & (RDONLYHOST))
5644 {
5645 rc = setError(E_ACCESSDENIED,
5646 tr("The property '%ls' cannot be changed by the host"),
5647 aName);
5648 }
5649 else
5650 {
5651 setModified(IsModified_MachineData);
5652 mHWData.backupEx();
5653
5654 /* The backupEx() operation invalidates our iterator,
5655 * so get a new one. */
5656 it = mHWData->mGuestProperties.find(utf8Name);
5657 Assert(it != mHWData->mGuestProperties.end());
5658
5659 if (RT_VALID_PTR(aValue) && *(aValue) != '\0')
5660 {
5661 RTTIMESPEC time;
5662 it->second.strValue = aValue;
5663 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5664 if (aFlags != NULL)
5665 it->second.mFlags = fFlags;
5666 }
5667 else
5668 {
5669 mHWData->mGuestProperties.erase(it);
5670 }
5671 }
5672 }
5673
5674 if ( SUCCEEDED(rc)
5675 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5676 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5677 RTSTR_MAX,
5678 utf8Name.c_str(),
5679 RTSTR_MAX,
5680 NULL)
5681 )
5682 )
5683 {
5684 alock.release();
5685
5686 mParent->onGuestPropertyChange(mData->mUuid, aName,
5687 aValue ? aValue : Bstr("").raw(),
5688 aFlags ? aFlags : Bstr("").raw());
5689 }
5690 }
5691 catch (std::bad_alloc &)
5692 {
5693 rc = E_OUTOFMEMORY;
5694 }
5695
5696 return rc;
5697}
5698
5699/**
5700 * Set a property on the VM that that property belongs to.
5701 * @returns E_ACCESSDENIED if the VM process is not available or not
5702 * currently handling queries and the setting should then be done in
5703 * VBoxSVC.
5704 */
5705HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
5706 IN_BSTR aFlags)
5707{
5708 HRESULT rc;
5709
5710 try
5711 {
5712 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5713
5714 BSTR dummy = NULL; /* will not be changed (setter) */
5715 LONG64 dummy64;
5716 if (!directControl)
5717 rc = E_ACCESSDENIED;
5718 else
5719 /** @todo Fix when adding DeleteGuestProperty(),
5720 see defect. */
5721 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
5722 true /* isSetter */,
5723 &dummy, &dummy64, &dummy);
5724 }
5725 catch (std::bad_alloc &)
5726 {
5727 rc = E_OUTOFMEMORY;
5728 }
5729
5730 return rc;
5731}
5732#endif // VBOX_WITH_GUEST_PROPS
5733
5734STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
5735 IN_BSTR aFlags)
5736{
5737#ifndef VBOX_WITH_GUEST_PROPS
5738 ReturnComNotImplemented();
5739#else // VBOX_WITH_GUEST_PROPS
5740 CheckComArgStrNotEmptyOrNull(aName);
5741 CheckComArgMaybeNull(aFlags);
5742 CheckComArgMaybeNull(aValue);
5743
5744 AutoCaller autoCaller(this);
5745 if (FAILED(autoCaller.rc()))
5746 return autoCaller.rc();
5747
5748 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
5749 if (rc == E_ACCESSDENIED)
5750 /* The VM is not running or the service is not (yet) accessible */
5751 rc = setGuestPropertyToService(aName, aValue, aFlags);
5752 return rc;
5753#endif // VBOX_WITH_GUEST_PROPS
5754}
5755
5756STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
5757{
5758 return SetGuestProperty(aName, aValue, NULL);
5759}
5760
5761STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
5762{
5763 return SetGuestProperty(aName, NULL, NULL);
5764}
5765
5766#ifdef VBOX_WITH_GUEST_PROPS
5767/**
5768 * Enumerate the guest properties in VBoxSVC's internal structures.
5769 */
5770HRESULT Machine::enumerateGuestPropertiesInService
5771 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5772 ComSafeArrayOut(BSTR, aValues),
5773 ComSafeArrayOut(LONG64, aTimestamps),
5774 ComSafeArrayOut(BSTR, aFlags))
5775{
5776 using namespace guestProp;
5777
5778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5779 Utf8Str strPatterns(aPatterns);
5780
5781 HWData::GuestPropertyMap propMap;
5782
5783 /*
5784 * Look for matching patterns and build up a list.
5785 */
5786 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5787 while (it != mHWData->mGuestProperties.end())
5788 {
5789 if ( strPatterns.isEmpty()
5790 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5791 RTSTR_MAX,
5792 it->first.c_str(),
5793 RTSTR_MAX,
5794 NULL)
5795 )
5796 {
5797 propMap.insert(*it);
5798 }
5799
5800 it++;
5801 }
5802
5803 alock.release();
5804
5805 /*
5806 * And build up the arrays for returning the property information.
5807 */
5808 size_t cEntries = propMap.size();
5809 SafeArray<BSTR> names(cEntries);
5810 SafeArray<BSTR> values(cEntries);
5811 SafeArray<LONG64> timestamps(cEntries);
5812 SafeArray<BSTR> flags(cEntries);
5813 size_t iProp = 0;
5814
5815 it = propMap.begin();
5816 while (it != propMap.end())
5817 {
5818 char szFlags[MAX_FLAGS_LEN + 1];
5819 it->first.cloneTo(&names[iProp]);
5820 it->second.strValue.cloneTo(&values[iProp]);
5821 timestamps[iProp] = it->second.mTimestamp;
5822 writeFlags(it->second.mFlags, szFlags);
5823 Bstr(szFlags).cloneTo(&flags[iProp++]);
5824 it++;
5825 }
5826 names.detachTo(ComSafeArrayOutArg(aNames));
5827 values.detachTo(ComSafeArrayOutArg(aValues));
5828 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
5829 flags.detachTo(ComSafeArrayOutArg(aFlags));
5830 return S_OK;
5831}
5832
5833/**
5834 * Enumerate the properties managed by a VM.
5835 * @returns E_ACCESSDENIED if the VM process is not available or not
5836 * currently handling queries and the setting should then be done in
5837 * VBoxSVC.
5838 */
5839HRESULT Machine::enumerateGuestPropertiesOnVM
5840 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5841 ComSafeArrayOut(BSTR, aValues),
5842 ComSafeArrayOut(LONG64, aTimestamps),
5843 ComSafeArrayOut(BSTR, aFlags))
5844{
5845 HRESULT rc;
5846 ComPtr<IInternalSessionControl> directControl;
5847 directControl = mData->mSession.mDirectControl;
5848
5849 if (!directControl)
5850 rc = E_ACCESSDENIED;
5851 else
5852 rc = directControl->EnumerateGuestProperties
5853 (aPatterns, ComSafeArrayOutArg(aNames),
5854 ComSafeArrayOutArg(aValues),
5855 ComSafeArrayOutArg(aTimestamps),
5856 ComSafeArrayOutArg(aFlags));
5857 return rc;
5858}
5859#endif // VBOX_WITH_GUEST_PROPS
5860
5861STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
5862 ComSafeArrayOut(BSTR, aNames),
5863 ComSafeArrayOut(BSTR, aValues),
5864 ComSafeArrayOut(LONG64, aTimestamps),
5865 ComSafeArrayOut(BSTR, aFlags))
5866{
5867#ifndef VBOX_WITH_GUEST_PROPS
5868 ReturnComNotImplemented();
5869#else // VBOX_WITH_GUEST_PROPS
5870 CheckComArgMaybeNull(aPatterns);
5871 CheckComArgOutSafeArrayPointerValid(aNames);
5872 CheckComArgOutSafeArrayPointerValid(aValues);
5873 CheckComArgOutSafeArrayPointerValid(aTimestamps);
5874 CheckComArgOutSafeArrayPointerValid(aFlags);
5875
5876 AutoCaller autoCaller(this);
5877 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5878
5879 HRESULT rc = enumerateGuestPropertiesOnVM
5880 (aPatterns, ComSafeArrayOutArg(aNames),
5881 ComSafeArrayOutArg(aValues),
5882 ComSafeArrayOutArg(aTimestamps),
5883 ComSafeArrayOutArg(aFlags));
5884 if (rc == E_ACCESSDENIED)
5885 /* The VM is not running or the service is not (yet) accessible */
5886 rc = enumerateGuestPropertiesInService
5887 (aPatterns, ComSafeArrayOutArg(aNames),
5888 ComSafeArrayOutArg(aValues),
5889 ComSafeArrayOutArg(aTimestamps),
5890 ComSafeArrayOutArg(aFlags));
5891 return rc;
5892#endif // VBOX_WITH_GUEST_PROPS
5893}
5894
5895STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
5896 ComSafeArrayOut(IMediumAttachment*, aAttachments))
5897{
5898 MediaData::AttachmentList atts;
5899
5900 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
5901 if (FAILED(rc)) return rc;
5902
5903 SafeIfaceArray<IMediumAttachment> attachments(atts);
5904 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
5905
5906 return S_OK;
5907}
5908
5909STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
5910 LONG aControllerPort,
5911 LONG aDevice,
5912 IMediumAttachment **aAttachment)
5913{
5914 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5915 aControllerName, aControllerPort, aDevice));
5916
5917 CheckComArgStrNotEmptyOrNull(aControllerName);
5918 CheckComArgOutPointerValid(aAttachment);
5919
5920 AutoCaller autoCaller(this);
5921 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5922
5923 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5924
5925 *aAttachment = NULL;
5926
5927 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5928 aControllerName,
5929 aControllerPort,
5930 aDevice);
5931 if (pAttach.isNull())
5932 return setError(VBOX_E_OBJECT_NOT_FOUND,
5933 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5934 aDevice, aControllerPort, aControllerName);
5935
5936 pAttach.queryInterfaceTo(aAttachment);
5937
5938 return S_OK;
5939}
5940
5941STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
5942 StorageBus_T aConnectionType,
5943 IStorageController **controller)
5944{
5945 CheckComArgStrNotEmptyOrNull(aName);
5946
5947 if ( (aConnectionType <= StorageBus_Null)
5948 || (aConnectionType > StorageBus_SAS))
5949 return setError(E_INVALIDARG,
5950 tr("Invalid connection type: %d"),
5951 aConnectionType);
5952
5953 AutoCaller autoCaller(this);
5954 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5955
5956 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5957
5958 HRESULT rc = checkStateDependency(MutableStateDep);
5959 if (FAILED(rc)) return rc;
5960
5961 /* try to find one with the name first. */
5962 ComObjPtr<StorageController> ctrl;
5963
5964 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
5965 if (SUCCEEDED(rc))
5966 return setError(VBOX_E_OBJECT_IN_USE,
5967 tr("Storage controller named '%ls' already exists"),
5968 aName);
5969
5970 ctrl.createObject();
5971
5972 /* get a new instance number for the storage controller */
5973 ULONG ulInstance = 0;
5974 bool fBootable = true;
5975 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5976 it != mStorageControllers->end();
5977 ++it)
5978 {
5979 if ((*it)->getStorageBus() == aConnectionType)
5980 {
5981 ULONG ulCurInst = (*it)->getInstance();
5982
5983 if (ulCurInst >= ulInstance)
5984 ulInstance = ulCurInst + 1;
5985
5986 /* Only one controller of each type can be marked as bootable. */
5987 if ((*it)->getBootable())
5988 fBootable = false;
5989 }
5990 }
5991
5992 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5993 if (FAILED(rc)) return rc;
5994
5995 setModified(IsModified_Storage);
5996 mStorageControllers.backup();
5997 mStorageControllers->push_back(ctrl);
5998
5999 ctrl.queryInterfaceTo(controller);
6000
6001 /* inform the direct session if any */
6002 alock.release();
6003 onStorageControllerChange();
6004
6005 return S_OK;
6006}
6007
6008STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6009 IStorageController **aStorageController)
6010{
6011 CheckComArgStrNotEmptyOrNull(aName);
6012
6013 AutoCaller autoCaller(this);
6014 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6015
6016 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6017
6018 ComObjPtr<StorageController> ctrl;
6019
6020 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6021 if (SUCCEEDED(rc))
6022 ctrl.queryInterfaceTo(aStorageController);
6023
6024 return rc;
6025}
6026
6027STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6028 IStorageController **aStorageController)
6029{
6030 AutoCaller autoCaller(this);
6031 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6032
6033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6034
6035 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6036 it != mStorageControllers->end();
6037 ++it)
6038 {
6039 if ((*it)->getInstance() == aInstance)
6040 {
6041 (*it).queryInterfaceTo(aStorageController);
6042 return S_OK;
6043 }
6044 }
6045
6046 return setError(VBOX_E_OBJECT_NOT_FOUND,
6047 tr("Could not find a storage controller with instance number '%lu'"),
6048 aInstance);
6049}
6050
6051STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6052{
6053 AutoCaller autoCaller(this);
6054 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6055
6056 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6057
6058 HRESULT rc = checkStateDependency(MutableStateDep);
6059 if (FAILED(rc)) return rc;
6060
6061 ComObjPtr<StorageController> ctrl;
6062
6063 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6064 if (SUCCEEDED(rc))
6065 {
6066 /* Ensure that only one controller of each type is marked as bootable. */
6067 if (fBootable == TRUE)
6068 {
6069 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6070 it != mStorageControllers->end();
6071 ++it)
6072 {
6073 ComObjPtr<StorageController> aCtrl = (*it);
6074
6075 if ( (aCtrl->getName() != Utf8Str(aName))
6076 && aCtrl->getBootable() == TRUE
6077 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6078 && aCtrl->getControllerType() == ctrl->getControllerType())
6079 {
6080 aCtrl->setBootable(FALSE);
6081 break;
6082 }
6083 }
6084 }
6085
6086 if (SUCCEEDED(rc))
6087 {
6088 ctrl->setBootable(fBootable);
6089 setModified(IsModified_Storage);
6090 }
6091 }
6092
6093 if (SUCCEEDED(rc))
6094 {
6095 /* inform the direct session if any */
6096 alock.release();
6097 onStorageControllerChange();
6098 }
6099
6100 return rc;
6101}
6102
6103STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6104{
6105 CheckComArgStrNotEmptyOrNull(aName);
6106
6107 AutoCaller autoCaller(this);
6108 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6109
6110 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6111
6112 HRESULT rc = checkStateDependency(MutableStateDep);
6113 if (FAILED(rc)) return rc;
6114
6115 ComObjPtr<StorageController> ctrl;
6116 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6117 if (FAILED(rc)) return rc;
6118
6119 {
6120 /* find all attached devices to the appropriate storage controller and detach them all */
6121 // make a temporary list because detachDevice invalidates iterators into
6122 // mMediaData->mAttachments
6123 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6124
6125 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6126 it != llAttachments2.end();
6127 ++it)
6128 {
6129 MediumAttachment *pAttachTemp = *it;
6130
6131 AutoCaller localAutoCaller(pAttachTemp);
6132 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6133
6134 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6135
6136 if (pAttachTemp->getControllerName() == aName)
6137 {
6138 rc = detachDevice(pAttachTemp, alock, NULL);
6139 if (FAILED(rc)) return rc;
6140 }
6141 }
6142 }
6143
6144 /* We can remove it now. */
6145 setModified(IsModified_Storage);
6146 mStorageControllers.backup();
6147
6148 ctrl->unshare();
6149
6150 mStorageControllers->remove(ctrl);
6151
6152 /* inform the direct session if any */
6153 alock.release();
6154 onStorageControllerChange();
6155
6156 return S_OK;
6157}
6158
6159STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6160 ULONG *puOriginX,
6161 ULONG *puOriginY,
6162 ULONG *puWidth,
6163 ULONG *puHeight,
6164 BOOL *pfEnabled)
6165{
6166 LogFlowThisFunc(("\n"));
6167
6168 CheckComArgNotNull(puOriginX);
6169 CheckComArgNotNull(puOriginY);
6170 CheckComArgNotNull(puWidth);
6171 CheckComArgNotNull(puHeight);
6172 CheckComArgNotNull(pfEnabled);
6173
6174 uint32_t u32OriginX= 0;
6175 uint32_t u32OriginY= 0;
6176 uint32_t u32Width = 0;
6177 uint32_t u32Height = 0;
6178 uint16_t u16Flags = 0;
6179
6180 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6181 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6182 if (RT_FAILURE(vrc))
6183 {
6184#ifdef RT_OS_WINDOWS
6185 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6186 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6187 * So just assign fEnable to TRUE again.
6188 * The right fix would be to change GUI API wrappers to make sure that parameters
6189 * are changed only if API succeeds.
6190 */
6191 *pfEnabled = TRUE;
6192#endif
6193 return setError(VBOX_E_IPRT_ERROR,
6194 tr("Saved guest size is not available (%Rrc)"),
6195 vrc);
6196 }
6197
6198 *puOriginX = u32OriginX;
6199 *puOriginY = u32OriginY;
6200 *puWidth = u32Width;
6201 *puHeight = u32Height;
6202 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6203
6204 return S_OK;
6205}
6206
6207STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6208{
6209 LogFlowThisFunc(("\n"));
6210
6211 CheckComArgNotNull(aSize);
6212 CheckComArgNotNull(aWidth);
6213 CheckComArgNotNull(aHeight);
6214
6215 if (aScreenId != 0)
6216 return E_NOTIMPL;
6217
6218 AutoCaller autoCaller(this);
6219 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6220
6221 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6222
6223 uint8_t *pu8Data = NULL;
6224 uint32_t cbData = 0;
6225 uint32_t u32Width = 0;
6226 uint32_t u32Height = 0;
6227
6228 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6229
6230 if (RT_FAILURE(vrc))
6231 return setError(VBOX_E_IPRT_ERROR,
6232 tr("Saved screenshot data is not available (%Rrc)"),
6233 vrc);
6234
6235 *aSize = cbData;
6236 *aWidth = u32Width;
6237 *aHeight = u32Height;
6238
6239 freeSavedDisplayScreenshot(pu8Data);
6240
6241 return S_OK;
6242}
6243
6244STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6245{
6246 LogFlowThisFunc(("\n"));
6247
6248 CheckComArgNotNull(aWidth);
6249 CheckComArgNotNull(aHeight);
6250 CheckComArgOutSafeArrayPointerValid(aData);
6251
6252 if (aScreenId != 0)
6253 return E_NOTIMPL;
6254
6255 AutoCaller autoCaller(this);
6256 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6257
6258 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6259
6260 uint8_t *pu8Data = NULL;
6261 uint32_t cbData = 0;
6262 uint32_t u32Width = 0;
6263 uint32_t u32Height = 0;
6264
6265 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6266
6267 if (RT_FAILURE(vrc))
6268 return setError(VBOX_E_IPRT_ERROR,
6269 tr("Saved screenshot data is not available (%Rrc)"),
6270 vrc);
6271
6272 *aWidth = u32Width;
6273 *aHeight = u32Height;
6274
6275 com::SafeArray<BYTE> bitmap(cbData);
6276 /* Convert pixels to format expected by the API caller. */
6277 if (aBGR)
6278 {
6279 /* [0] B, [1] G, [2] R, [3] A. */
6280 for (unsigned i = 0; i < cbData; i += 4)
6281 {
6282 bitmap[i] = pu8Data[i];
6283 bitmap[i + 1] = pu8Data[i + 1];
6284 bitmap[i + 2] = pu8Data[i + 2];
6285 bitmap[i + 3] = 0xff;
6286 }
6287 }
6288 else
6289 {
6290 /* [0] R, [1] G, [2] B, [3] A. */
6291 for (unsigned i = 0; i < cbData; i += 4)
6292 {
6293 bitmap[i] = pu8Data[i + 2];
6294 bitmap[i + 1] = pu8Data[i + 1];
6295 bitmap[i + 2] = pu8Data[i];
6296 bitmap[i + 3] = 0xff;
6297 }
6298 }
6299 bitmap.detachTo(ComSafeArrayOutArg(aData));
6300
6301 freeSavedDisplayScreenshot(pu8Data);
6302
6303 return S_OK;
6304}
6305
6306
6307STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6308{
6309 LogFlowThisFunc(("\n"));
6310
6311 CheckComArgNotNull(aWidth);
6312 CheckComArgNotNull(aHeight);
6313 CheckComArgOutSafeArrayPointerValid(aData);
6314
6315 if (aScreenId != 0)
6316 return E_NOTIMPL;
6317
6318 AutoCaller autoCaller(this);
6319 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6320
6321 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6322
6323 uint8_t *pu8Data = NULL;
6324 uint32_t cbData = 0;
6325 uint32_t u32Width = 0;
6326 uint32_t u32Height = 0;
6327
6328 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6329
6330 if (RT_FAILURE(vrc))
6331 return setError(VBOX_E_IPRT_ERROR,
6332 tr("Saved screenshot data is not available (%Rrc)"),
6333 vrc);
6334
6335 *aWidth = u32Width;
6336 *aHeight = u32Height;
6337
6338 HRESULT rc = S_OK;
6339 uint8_t *pu8PNG = NULL;
6340 uint32_t cbPNG = 0;
6341 uint32_t cxPNG = 0;
6342 uint32_t cyPNG = 0;
6343
6344 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6345
6346 if (RT_SUCCESS(vrc))
6347 {
6348 com::SafeArray<BYTE> screenData(cbPNG);
6349 screenData.initFrom(pu8PNG, cbPNG);
6350 if (pu8PNG)
6351 RTMemFree(pu8PNG);
6352 screenData.detachTo(ComSafeArrayOutArg(aData));
6353 }
6354 else
6355 {
6356 if (pu8PNG)
6357 RTMemFree(pu8PNG);
6358 return setError(VBOX_E_IPRT_ERROR,
6359 tr("Could not convert screenshot to PNG (%Rrc)"),
6360 vrc);
6361 }
6362
6363 freeSavedDisplayScreenshot(pu8Data);
6364
6365 return rc;
6366}
6367
6368STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6369{
6370 LogFlowThisFunc(("\n"));
6371
6372 CheckComArgNotNull(aSize);
6373 CheckComArgNotNull(aWidth);
6374 CheckComArgNotNull(aHeight);
6375
6376 if (aScreenId != 0)
6377 return E_NOTIMPL;
6378
6379 AutoCaller autoCaller(this);
6380 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6381
6382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6383
6384 uint8_t *pu8Data = NULL;
6385 uint32_t cbData = 0;
6386 uint32_t u32Width = 0;
6387 uint32_t u32Height = 0;
6388
6389 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6390
6391 if (RT_FAILURE(vrc))
6392 return setError(VBOX_E_IPRT_ERROR,
6393 tr("Saved screenshot data is not available (%Rrc)"),
6394 vrc);
6395
6396 *aSize = cbData;
6397 *aWidth = u32Width;
6398 *aHeight = u32Height;
6399
6400 freeSavedDisplayScreenshot(pu8Data);
6401
6402 return S_OK;
6403}
6404
6405STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6406{
6407 LogFlowThisFunc(("\n"));
6408
6409 CheckComArgNotNull(aWidth);
6410 CheckComArgNotNull(aHeight);
6411 CheckComArgOutSafeArrayPointerValid(aData);
6412
6413 if (aScreenId != 0)
6414 return E_NOTIMPL;
6415
6416 AutoCaller autoCaller(this);
6417 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6418
6419 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6420
6421 uint8_t *pu8Data = NULL;
6422 uint32_t cbData = 0;
6423 uint32_t u32Width = 0;
6424 uint32_t u32Height = 0;
6425
6426 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6427
6428 if (RT_FAILURE(vrc))
6429 return setError(VBOX_E_IPRT_ERROR,
6430 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6431 vrc);
6432
6433 *aWidth = u32Width;
6434 *aHeight = u32Height;
6435
6436 com::SafeArray<BYTE> png(cbData);
6437 png.initFrom(pu8Data, cbData);
6438 png.detachTo(ComSafeArrayOutArg(aData));
6439
6440 freeSavedDisplayScreenshot(pu8Data);
6441
6442 return S_OK;
6443}
6444
6445STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6446{
6447 HRESULT rc = S_OK;
6448 LogFlowThisFunc(("\n"));
6449
6450 AutoCaller autoCaller(this);
6451 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6452
6453 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6454
6455 if (!mHWData->mCPUHotPlugEnabled)
6456 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6457
6458 if (aCpu >= mHWData->mCPUCount)
6459 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6460
6461 if (mHWData->mCPUAttached[aCpu])
6462 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6463
6464 alock.release();
6465 rc = onCPUChange(aCpu, false);
6466 alock.acquire();
6467 if (FAILED(rc)) return rc;
6468
6469 setModified(IsModified_MachineData);
6470 mHWData.backup();
6471 mHWData->mCPUAttached[aCpu] = true;
6472
6473 /* Save settings if online */
6474 if (Global::IsOnline(mData->mMachineState))
6475 saveSettings(NULL);
6476
6477 return S_OK;
6478}
6479
6480STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6481{
6482 HRESULT rc = S_OK;
6483 LogFlowThisFunc(("\n"));
6484
6485 AutoCaller autoCaller(this);
6486 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6487
6488 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6489
6490 if (!mHWData->mCPUHotPlugEnabled)
6491 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6492
6493 if (aCpu >= SchemaDefs::MaxCPUCount)
6494 return setError(E_INVALIDARG,
6495 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6496 SchemaDefs::MaxCPUCount);
6497
6498 if (!mHWData->mCPUAttached[aCpu])
6499 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6500
6501 /* CPU 0 can't be detached */
6502 if (aCpu == 0)
6503 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6504
6505 alock.release();
6506 rc = onCPUChange(aCpu, true);
6507 alock.acquire();
6508 if (FAILED(rc)) return rc;
6509
6510 setModified(IsModified_MachineData);
6511 mHWData.backup();
6512 mHWData->mCPUAttached[aCpu] = false;
6513
6514 /* Save settings if online */
6515 if (Global::IsOnline(mData->mMachineState))
6516 saveSettings(NULL);
6517
6518 return S_OK;
6519}
6520
6521STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6522{
6523 LogFlowThisFunc(("\n"));
6524
6525 CheckComArgNotNull(aCpuAttached);
6526
6527 *aCpuAttached = false;
6528
6529 AutoCaller autoCaller(this);
6530 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6531
6532 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6533
6534 /* If hotplug is enabled the CPU is always enabled. */
6535 if (!mHWData->mCPUHotPlugEnabled)
6536 {
6537 if (aCpu < mHWData->mCPUCount)
6538 *aCpuAttached = true;
6539 }
6540 else
6541 {
6542 if (aCpu < SchemaDefs::MaxCPUCount)
6543 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6544 }
6545
6546 return S_OK;
6547}
6548
6549STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6550{
6551 CheckComArgOutPointerValid(aName);
6552
6553 AutoCaller autoCaller(this);
6554 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6555
6556 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6557
6558 Utf8Str log = queryLogFilename(aIdx);
6559 if (!RTFileExists(log.c_str()))
6560 log.setNull();
6561 log.cloneTo(aName);
6562
6563 return S_OK;
6564}
6565
6566STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6567{
6568 LogFlowThisFunc(("\n"));
6569 CheckComArgOutSafeArrayPointerValid(aData);
6570 if (aSize < 0)
6571 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6572
6573 AutoCaller autoCaller(this);
6574 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6575
6576 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6577
6578 HRESULT rc = S_OK;
6579 Utf8Str log = queryLogFilename(aIdx);
6580
6581 /* do not unnecessarily hold the lock while doing something which does
6582 * not need the lock and potentially takes a long time. */
6583 alock.release();
6584
6585 /* Limit the chunk size to 32K for now, as that gives better performance
6586 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6587 * One byte expands to approx. 25 bytes of breathtaking XML. */
6588 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6589 com::SafeArray<BYTE> logData(cbData);
6590
6591 RTFILE LogFile;
6592 int vrc = RTFileOpen(&LogFile, log.c_str(),
6593 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6594 if (RT_SUCCESS(vrc))
6595 {
6596 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6597 if (RT_SUCCESS(vrc))
6598 logData.resize(cbData);
6599 else
6600 rc = setError(VBOX_E_IPRT_ERROR,
6601 tr("Could not read log file '%s' (%Rrc)"),
6602 log.c_str(), vrc);
6603 RTFileClose(LogFile);
6604 }
6605 else
6606 rc = setError(VBOX_E_IPRT_ERROR,
6607 tr("Could not open log file '%s' (%Rrc)"),
6608 log.c_str(), vrc);
6609
6610 if (FAILED(rc))
6611 logData.resize(0);
6612 logData.detachTo(ComSafeArrayOutArg(aData));
6613
6614 return rc;
6615}
6616
6617
6618/**
6619 * Currently this method doesn't attach device to the running VM,
6620 * just makes sure it's plugged on next VM start.
6621 */
6622STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6623{
6624 AutoCaller autoCaller(this);
6625 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6626
6627 // lock scope
6628 {
6629 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6630
6631 HRESULT rc = checkStateDependency(MutableStateDep);
6632 if (FAILED(rc)) return rc;
6633
6634 ChipsetType_T aChipset = ChipsetType_PIIX3;
6635 COMGETTER(ChipsetType)(&aChipset);
6636
6637 if (aChipset != ChipsetType_ICH9)
6638 {
6639 return setError(E_INVALIDARG,
6640 tr("Host PCI attachment only supported with ICH9 chipset"));
6641 }
6642
6643 // check if device with this host PCI address already attached
6644 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6645 it != mHWData->mPCIDeviceAssignments.end();
6646 ++it)
6647 {
6648 LONG iHostAddress = -1;
6649 ComPtr<PCIDeviceAttachment> pAttach;
6650 pAttach = *it;
6651 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6652 if (iHostAddress == hostAddress)
6653 return setError(E_INVALIDARG,
6654 tr("Device with host PCI address already attached to this VM"));
6655 }
6656
6657 ComObjPtr<PCIDeviceAttachment> pda;
6658 char name[32];
6659
6660 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6661 Bstr bname(name);
6662 pda.createObject();
6663 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
6664 setModified(IsModified_MachineData);
6665 mHWData.backup();
6666 mHWData->mPCIDeviceAssignments.push_back(pda);
6667 }
6668
6669 return S_OK;
6670}
6671
6672/**
6673 * Currently this method doesn't detach device from the running VM,
6674 * just makes sure it's not plugged on next VM start.
6675 */
6676STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
6677{
6678 AutoCaller autoCaller(this);
6679 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6680
6681 ComObjPtr<PCIDeviceAttachment> pAttach;
6682 bool fRemoved = false;
6683 HRESULT rc;
6684
6685 // lock scope
6686 {
6687 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6688
6689 rc = checkStateDependency(MutableStateDep);
6690 if (FAILED(rc)) return rc;
6691
6692 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6693 it != mHWData->mPCIDeviceAssignments.end();
6694 ++it)
6695 {
6696 LONG iHostAddress = -1;
6697 pAttach = *it;
6698 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6699 if (iHostAddress != -1 && iHostAddress == hostAddress)
6700 {
6701 setModified(IsModified_MachineData);
6702 mHWData.backup();
6703 mHWData->mPCIDeviceAssignments.remove(pAttach);
6704 fRemoved = true;
6705 break;
6706 }
6707 }
6708 }
6709
6710
6711 /* Fire event outside of the lock */
6712 if (fRemoved)
6713 {
6714 Assert(!pAttach.isNull());
6715 ComPtr<IEventSource> es;
6716 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6717 Assert(SUCCEEDED(rc));
6718 Bstr mid;
6719 rc = this->COMGETTER(Id)(mid.asOutParam());
6720 Assert(SUCCEEDED(rc));
6721 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6722 }
6723
6724 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6725 tr("No host PCI device %08x attached"),
6726 hostAddress
6727 );
6728}
6729
6730STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
6731{
6732 CheckComArgOutSafeArrayPointerValid(aAssignments);
6733
6734 AutoCaller autoCaller(this);
6735 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6736
6737 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6738
6739 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
6740 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
6741
6742 return S_OK;
6743}
6744
6745STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
6746{
6747 CheckComArgOutPointerValid(aBandwidthControl);
6748
6749 AutoCaller autoCaller(this);
6750 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6751
6752 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
6753
6754 return S_OK;
6755}
6756
6757STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
6758{
6759 CheckComArgOutPointerValid(pfEnabled);
6760 AutoCaller autoCaller(this);
6761 HRESULT hrc = autoCaller.rc();
6762 if (SUCCEEDED(hrc))
6763 {
6764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6765 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
6766 }
6767 return hrc;
6768}
6769
6770STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
6771{
6772 AutoCaller autoCaller(this);
6773 HRESULT hrc = autoCaller.rc();
6774 if (SUCCEEDED(hrc))
6775 {
6776 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6777 hrc = checkStateDependency(MutableStateDep);
6778 if (SUCCEEDED(hrc))
6779 {
6780 hrc = mHWData.backupEx();
6781 if (SUCCEEDED(hrc))
6782 {
6783 setModified(IsModified_MachineData);
6784 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
6785 }
6786 }
6787 }
6788 return hrc;
6789}
6790
6791STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
6792{
6793 CheckComArgOutPointerValid(pbstrConfig);
6794 AutoCaller autoCaller(this);
6795 HRESULT hrc = autoCaller.rc();
6796 if (SUCCEEDED(hrc))
6797 {
6798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6799 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
6800 }
6801 return hrc;
6802}
6803
6804STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
6805{
6806 CheckComArgStr(bstrConfig);
6807 AutoCaller autoCaller(this);
6808 HRESULT hrc = autoCaller.rc();
6809 if (SUCCEEDED(hrc))
6810 {
6811 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6812 hrc = checkStateDependency(MutableStateDep);
6813 if (SUCCEEDED(hrc))
6814 {
6815 hrc = mHWData.backupEx();
6816 if (SUCCEEDED(hrc))
6817 {
6818 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
6819 if (SUCCEEDED(hrc))
6820 setModified(IsModified_MachineData);
6821 }
6822 }
6823 }
6824 return hrc;
6825
6826}
6827
6828STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
6829{
6830 CheckComArgOutPointerValid(pfAllow);
6831 AutoCaller autoCaller(this);
6832 HRESULT hrc = autoCaller.rc();
6833 if (SUCCEEDED(hrc))
6834 {
6835 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6836 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
6837 }
6838 return hrc;
6839}
6840
6841STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
6842{
6843 AutoCaller autoCaller(this);
6844 HRESULT hrc = autoCaller.rc();
6845 if (SUCCEEDED(hrc))
6846 {
6847 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6848 hrc = checkStateDependency(MutableStateDep);
6849 if (SUCCEEDED(hrc))
6850 {
6851 hrc = mHWData.backupEx();
6852 if (SUCCEEDED(hrc))
6853 {
6854 setModified(IsModified_MachineData);
6855 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
6856 }
6857 }
6858 }
6859 return hrc;
6860}
6861
6862STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
6863{
6864 CheckComArgOutPointerValid(pfEnabled);
6865 AutoCaller autoCaller(this);
6866 HRESULT hrc = autoCaller.rc();
6867 if (SUCCEEDED(hrc))
6868 {
6869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6870 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
6871 }
6872 return hrc;
6873}
6874
6875STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
6876{
6877 AutoCaller autoCaller(this);
6878 HRESULT hrc = autoCaller.rc();
6879 if (SUCCEEDED(hrc))
6880 {
6881 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6882 hrc = checkStateDependency(MutableStateDep);
6883 if ( SUCCEEDED(hrc)
6884 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
6885 {
6886 AutostartDb *autostartDb = mParent->getAutostartDb();
6887 int vrc;
6888
6889 if (fEnabled)
6890 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6891 else
6892 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6893
6894 if (RT_SUCCESS(vrc))
6895 {
6896 hrc = mHWData.backupEx();
6897 if (SUCCEEDED(hrc))
6898 {
6899 setModified(IsModified_MachineData);
6900 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
6901 }
6902 }
6903 else if (vrc == VERR_NOT_SUPPORTED)
6904 hrc = setError(VBOX_E_NOT_SUPPORTED,
6905 tr("The VM autostart feature is not supported on this platform"));
6906 else if (vrc == VERR_PATH_NOT_FOUND)
6907 hrc = setError(E_FAIL,
6908 tr("The path to the autostart database is not set"));
6909 else
6910 hrc = setError(E_UNEXPECTED,
6911 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6912 fEnabled ? "Adding" : "Removing",
6913 mUserData->s.strName.c_str(), vrc);
6914 }
6915 }
6916 return hrc;
6917}
6918
6919STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
6920{
6921 CheckComArgOutPointerValid(puDelay);
6922 AutoCaller autoCaller(this);
6923 HRESULT hrc = autoCaller.rc();
6924 if (SUCCEEDED(hrc))
6925 {
6926 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6927 *puDelay = mHWData->mAutostart.uAutostartDelay;
6928 }
6929 return hrc;
6930}
6931
6932STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
6933{
6934 AutoCaller autoCaller(this);
6935 HRESULT hrc = autoCaller.rc();
6936 if (SUCCEEDED(hrc))
6937 {
6938 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6939 hrc = checkStateDependency(MutableStateDep);
6940 if (SUCCEEDED(hrc))
6941 {
6942 hrc = mHWData.backupEx();
6943 if (SUCCEEDED(hrc))
6944 {
6945 setModified(IsModified_MachineData);
6946 mHWData->mAutostart.uAutostartDelay = uDelay;
6947 }
6948 }
6949 }
6950 return hrc;
6951}
6952
6953STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
6954{
6955 CheckComArgOutPointerValid(penmAutostopType);
6956 AutoCaller autoCaller(this);
6957 HRESULT hrc = autoCaller.rc();
6958 if (SUCCEEDED(hrc))
6959 {
6960 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6961 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
6962 }
6963 return hrc;
6964}
6965
6966STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
6967{
6968 AutoCaller autoCaller(this);
6969 HRESULT hrc = autoCaller.rc();
6970 if (SUCCEEDED(hrc))
6971 {
6972 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6973 hrc = checkStateDependency(MutableStateDep);
6974 if ( SUCCEEDED(hrc)
6975 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
6976 {
6977 AutostartDb *autostartDb = mParent->getAutostartDb();
6978 int vrc;
6979
6980 if (enmAutostopType != AutostopType_Disabled)
6981 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6982 else
6983 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6984
6985 if (RT_SUCCESS(vrc))
6986 {
6987 hrc = mHWData.backupEx();
6988 if (SUCCEEDED(hrc))
6989 {
6990 setModified(IsModified_MachineData);
6991 mHWData->mAutostart.enmAutostopType = enmAutostopType;
6992 }
6993 }
6994 else if (vrc == VERR_NOT_SUPPORTED)
6995 hrc = setError(VBOX_E_NOT_SUPPORTED,
6996 tr("The VM autostop feature is not supported on this platform"));
6997 else if (vrc == VERR_PATH_NOT_FOUND)
6998 hrc = setError(E_FAIL,
6999 tr("The path to the autostart database is not set"));
7000 else
7001 hrc = setError(E_UNEXPECTED,
7002 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7003 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7004 mUserData->s.strName.c_str(), vrc);
7005 }
7006 }
7007 return hrc;
7008}
7009
7010STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7011{
7012 CheckComArgOutPointerValid(aDefaultFrontend);
7013 AutoCaller autoCaller(this);
7014 HRESULT hrc = autoCaller.rc();
7015 if (SUCCEEDED(hrc))
7016 {
7017 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7018 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7019 }
7020 return hrc;
7021}
7022
7023STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7024{
7025 CheckComArgStr(aDefaultFrontend);
7026 AutoCaller autoCaller(this);
7027 HRESULT hrc = autoCaller.rc();
7028 if (SUCCEEDED(hrc))
7029 {
7030 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7031 hrc = checkStateDependency(MutableOrSavedStateDep);
7032 if (SUCCEEDED(hrc))
7033 {
7034 hrc = mHWData.backupEx();
7035 if (SUCCEEDED(hrc))
7036 {
7037 setModified(IsModified_MachineData);
7038 mHWData->mDefaultFrontend = aDefaultFrontend;
7039 }
7040 }
7041 }
7042 return hrc;
7043}
7044
7045
7046STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7047{
7048 LogFlowFuncEnter();
7049
7050 CheckComArgNotNull(pTarget);
7051 CheckComArgOutPointerValid(pProgress);
7052
7053 /* Convert the options. */
7054 RTCList<CloneOptions_T> optList;
7055 if (options != NULL)
7056 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7057
7058 if (optList.contains(CloneOptions_Link))
7059 {
7060 if (!isSnapshotMachine())
7061 return setError(E_INVALIDARG,
7062 tr("Linked clone can only be created from a snapshot"));
7063 if (mode != CloneMode_MachineState)
7064 return setError(E_INVALIDARG,
7065 tr("Linked clone can only be created for a single machine state"));
7066 }
7067 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7068
7069 AutoCaller autoCaller(this);
7070 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7071
7072
7073 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7074
7075 HRESULT rc = pWorker->start(pProgress);
7076
7077 LogFlowFuncLeave();
7078
7079 return rc;
7080}
7081
7082// public methods for internal purposes
7083/////////////////////////////////////////////////////////////////////////////
7084
7085/**
7086 * Adds the given IsModified_* flag to the dirty flags of the machine.
7087 * This must be called either during loadSettings or under the machine write lock.
7088 * @param fl
7089 */
7090void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7091{
7092 mData->flModifications |= fl;
7093 if (fAllowStateModification && isStateModificationAllowed())
7094 mData->mCurrentStateModified = true;
7095}
7096
7097/**
7098 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7099 * care of the write locking.
7100 *
7101 * @param fModifications The flag to add.
7102 */
7103void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7104{
7105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7106 setModified(fModification, fAllowStateModification);
7107}
7108
7109/**
7110 * Saves the registry entry of this machine to the given configuration node.
7111 *
7112 * @param aEntryNode Node to save the registry entry to.
7113 *
7114 * @note locks this object for reading.
7115 */
7116HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7117{
7118 AutoLimitedCaller autoCaller(this);
7119 AssertComRCReturnRC(autoCaller.rc());
7120
7121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7122
7123 data.uuid = mData->mUuid;
7124 data.strSettingsFile = mData->m_strConfigFile;
7125
7126 return S_OK;
7127}
7128
7129/**
7130 * Calculates the absolute path of the given path taking the directory of the
7131 * machine settings file as the current directory.
7132 *
7133 * @param aPath Path to calculate the absolute path for.
7134 * @param aResult Where to put the result (used only on success, can be the
7135 * same Utf8Str instance as passed in @a aPath).
7136 * @return IPRT result.
7137 *
7138 * @note Locks this object for reading.
7139 */
7140int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7141{
7142 AutoCaller autoCaller(this);
7143 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7144
7145 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7146
7147 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7148
7149 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7150
7151 strSettingsDir.stripFilename();
7152 char folder[RTPATH_MAX];
7153 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7154 if (RT_SUCCESS(vrc))
7155 aResult = folder;
7156
7157 return vrc;
7158}
7159
7160/**
7161 * Copies strSource to strTarget, making it relative to the machine folder
7162 * if it is a subdirectory thereof, or simply copying it otherwise.
7163 *
7164 * @param strSource Path to evaluate and copy.
7165 * @param strTarget Buffer to receive target path.
7166 *
7167 * @note Locks this object for reading.
7168 */
7169void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7170 Utf8Str &strTarget)
7171{
7172 AutoCaller autoCaller(this);
7173 AssertComRCReturn(autoCaller.rc(), (void)0);
7174
7175 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7176
7177 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7178 // use strTarget as a temporary buffer to hold the machine settings dir
7179 strTarget = mData->m_strConfigFileFull;
7180 strTarget.stripFilename();
7181 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7182 {
7183 // is relative: then append what's left
7184 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7185 // for empty paths (only possible for subdirs) use "." to avoid
7186 // triggering default settings for not present config attributes.
7187 if (strTarget.isEmpty())
7188 strTarget = ".";
7189 }
7190 else
7191 // is not relative: then overwrite
7192 strTarget = strSource;
7193}
7194
7195/**
7196 * Returns the full path to the machine's log folder in the
7197 * \a aLogFolder argument.
7198 */
7199void Machine::getLogFolder(Utf8Str &aLogFolder)
7200{
7201 AutoCaller autoCaller(this);
7202 AssertComRCReturnVoid(autoCaller.rc());
7203
7204 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7205
7206 char szTmp[RTPATH_MAX];
7207 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7208 if (RT_SUCCESS(vrc))
7209 {
7210 if (szTmp[0] && !mUserData.isNull())
7211 {
7212 char szTmp2[RTPATH_MAX];
7213 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7214 if (RT_SUCCESS(vrc))
7215 aLogFolder = BstrFmt("%s%c%s",
7216 szTmp2,
7217 RTPATH_DELIMITER,
7218 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7219 }
7220 else
7221 vrc = VERR_PATH_IS_RELATIVE;
7222 }
7223
7224 if (RT_FAILURE(vrc))
7225 {
7226 // fallback if VBOX_USER_LOGHOME is not set or invalid
7227 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7228 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7229 aLogFolder.append(RTPATH_DELIMITER);
7230 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7231 }
7232}
7233
7234/**
7235 * Returns the full path to the machine's log file for an given index.
7236 */
7237Utf8Str Machine::queryLogFilename(ULONG idx)
7238{
7239 Utf8Str logFolder;
7240 getLogFolder(logFolder);
7241 Assert(logFolder.length());
7242 Utf8Str log;
7243 if (idx == 0)
7244 log = Utf8StrFmt("%s%cVBox.log",
7245 logFolder.c_str(), RTPATH_DELIMITER);
7246 else
7247 log = Utf8StrFmt("%s%cVBox.log.%d",
7248 logFolder.c_str(), RTPATH_DELIMITER, idx);
7249 return log;
7250}
7251
7252/**
7253 * Composes a unique saved state filename based on the current system time. The filename is
7254 * granular to the second so this will work so long as no more than one snapshot is taken on
7255 * a machine per second.
7256 *
7257 * Before version 4.1, we used this formula for saved state files:
7258 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7259 * which no longer works because saved state files can now be shared between the saved state of the
7260 * "saved" machine and an online snapshot, and the following would cause problems:
7261 * 1) save machine
7262 * 2) create online snapshot from that machine state --> reusing saved state file
7263 * 3) save machine again --> filename would be reused, breaking the online snapshot
7264 *
7265 * So instead we now use a timestamp.
7266 *
7267 * @param str
7268 */
7269void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7270{
7271 AutoCaller autoCaller(this);
7272 AssertComRCReturnVoid(autoCaller.rc());
7273
7274 {
7275 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7276 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7277 }
7278
7279 RTTIMESPEC ts;
7280 RTTimeNow(&ts);
7281 RTTIME time;
7282 RTTimeExplode(&time, &ts);
7283
7284 strStateFilePath += RTPATH_DELIMITER;
7285 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7286 time.i32Year, time.u8Month, time.u8MonthDay,
7287 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7288}
7289
7290/**
7291 * @note Locks this object for writing, calls the client process
7292 * (inside the lock).
7293 */
7294HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7295 const Utf8Str &strFrontend,
7296 const Utf8Str &strEnvironment,
7297 ProgressProxy *aProgress)
7298{
7299 LogFlowThisFuncEnter();
7300
7301 AssertReturn(aControl, E_FAIL);
7302 AssertReturn(aProgress, E_FAIL);
7303
7304 AutoCaller autoCaller(this);
7305 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7306
7307 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7308
7309 if (!mData->mRegistered)
7310 return setError(E_UNEXPECTED,
7311 tr("The machine '%s' is not registered"),
7312 mUserData->s.strName.c_str());
7313
7314 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7315
7316 if ( mData->mSession.mState == SessionState_Locked
7317 || mData->mSession.mState == SessionState_Spawning
7318 || mData->mSession.mState == SessionState_Unlocking)
7319 return setError(VBOX_E_INVALID_OBJECT_STATE,
7320 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7321 mUserData->s.strName.c_str());
7322
7323 /* may not be busy */
7324 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7325
7326 /* get the path to the executable */
7327 char szPath[RTPATH_MAX];
7328 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7329 size_t sz = strlen(szPath);
7330 szPath[sz++] = RTPATH_DELIMITER;
7331 szPath[sz] = 0;
7332 char *cmd = szPath + sz;
7333 sz = RTPATH_MAX - sz;
7334
7335 int vrc = VINF_SUCCESS;
7336 RTPROCESS pid = NIL_RTPROCESS;
7337
7338 RTENV env = RTENV_DEFAULT;
7339
7340 if (!strEnvironment.isEmpty())
7341 {
7342 char *newEnvStr = NULL;
7343
7344 do
7345 {
7346 /* clone the current environment */
7347 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7348 AssertRCBreakStmt(vrc2, vrc = vrc2);
7349
7350 newEnvStr = RTStrDup(strEnvironment.c_str());
7351 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7352
7353 /* put new variables to the environment
7354 * (ignore empty variable names here since RTEnv API
7355 * intentionally doesn't do that) */
7356 char *var = newEnvStr;
7357 for (char *p = newEnvStr; *p; ++p)
7358 {
7359 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7360 {
7361 *p = '\0';
7362 if (*var)
7363 {
7364 char *val = strchr(var, '=');
7365 if (val)
7366 {
7367 *val++ = '\0';
7368 vrc2 = RTEnvSetEx(env, var, val);
7369 }
7370 else
7371 vrc2 = RTEnvUnsetEx(env, var);
7372 if (RT_FAILURE(vrc2))
7373 break;
7374 }
7375 var = p + 1;
7376 }
7377 }
7378 if (RT_SUCCESS(vrc2) && *var)
7379 vrc2 = RTEnvPutEx(env, var);
7380
7381 AssertRCBreakStmt(vrc2, vrc = vrc2);
7382 }
7383 while (0);
7384
7385 if (newEnvStr != NULL)
7386 RTStrFree(newEnvStr);
7387 }
7388
7389 /* Qt is default */
7390#ifdef VBOX_WITH_QTGUI
7391 if (strFrontend == "gui" || strFrontend == "GUI/Qt" || strFrontend == "")
7392 {
7393# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7394 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7395# else
7396 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7397# endif
7398 Assert(sz >= sizeof(VirtualBox_exe));
7399 strcpy(cmd, VirtualBox_exe);
7400
7401 Utf8Str idStr = mData->mUuid.toString();
7402 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7403 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7404 }
7405#else /* !VBOX_WITH_QTGUI */
7406 if (0)
7407 ;
7408#endif /* VBOX_WITH_QTGUI */
7409
7410 else
7411
7412#ifdef VBOX_WITH_VBOXSDL
7413 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7414 {
7415 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7416 Assert(sz >= sizeof(VBoxSDL_exe));
7417 strcpy(cmd, VBoxSDL_exe);
7418
7419 Utf8Str idStr = mData->mUuid.toString();
7420 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7421 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7422 }
7423#else /* !VBOX_WITH_VBOXSDL */
7424 if (0)
7425 ;
7426#endif /* !VBOX_WITH_VBOXSDL */
7427
7428 else
7429
7430#ifdef VBOX_WITH_HEADLESS
7431 if ( strFrontend == "headless"
7432 || strFrontend == "capture"
7433 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7434 )
7435 {
7436 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7437 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7438 * and a VM works even if the server has not been installed.
7439 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7440 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7441 * differently in 4.0 and 3.x.
7442 */
7443 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7444 Assert(sz >= sizeof(VBoxHeadless_exe));
7445 strcpy(cmd, VBoxHeadless_exe);
7446
7447 Utf8Str idStr = mData->mUuid.toString();
7448 /* Leave space for "--capture" arg. */
7449 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7450 "--startvm", idStr.c_str(),
7451 "--vrde", "config",
7452 0, /* For "--capture". */
7453 0 };
7454 if (strFrontend == "capture")
7455 {
7456 unsigned pos = RT_ELEMENTS(args) - 2;
7457 args[pos] = "--capture";
7458 }
7459 vrc = RTProcCreate(szPath, args, env,
7460#ifdef RT_OS_WINDOWS
7461 RTPROC_FLAGS_NO_WINDOW
7462#else
7463 0
7464#endif
7465 , &pid);
7466 }
7467#else /* !VBOX_WITH_HEADLESS */
7468 if (0)
7469 ;
7470#endif /* !VBOX_WITH_HEADLESS */
7471 else
7472 {
7473 RTEnvDestroy(env);
7474 return setError(E_INVALIDARG,
7475 tr("Invalid frontend name: '%s'"),
7476 strFrontend.c_str());
7477 }
7478
7479 RTEnvDestroy(env);
7480
7481 if (RT_FAILURE(vrc))
7482 return setError(VBOX_E_IPRT_ERROR,
7483 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7484 mUserData->s.strName.c_str(), vrc);
7485
7486 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7487
7488 /*
7489 * Note that we don't release the lock here before calling the client,
7490 * because it doesn't need to call us back if called with a NULL argument.
7491 * Releasing the lock here is dangerous because we didn't prepare the
7492 * launch data yet, but the client we've just started may happen to be
7493 * too fast and call openSession() that will fail (because of PID, etc.),
7494 * so that the Machine will never get out of the Spawning session state.
7495 */
7496
7497 /* inform the session that it will be a remote one */
7498 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7499 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
7500 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7501
7502 if (FAILED(rc))
7503 {
7504 /* restore the session state */
7505 mData->mSession.mState = SessionState_Unlocked;
7506 /* The failure may occur w/o any error info (from RPC), so provide one */
7507 return setError(VBOX_E_VM_ERROR,
7508 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7509 }
7510
7511 /* attach launch data to the machine */
7512 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7513 mData->mSession.mRemoteControls.push_back(aControl);
7514 mData->mSession.mProgress = aProgress;
7515 mData->mSession.mPID = pid;
7516 mData->mSession.mState = SessionState_Spawning;
7517 mData->mSession.mType = strFrontend;
7518
7519 LogFlowThisFuncLeave();
7520 return S_OK;
7521}
7522
7523/**
7524 * Returns @c true if the given machine has an open direct session and returns
7525 * the session machine instance and additional session data (on some platforms)
7526 * if so.
7527 *
7528 * Note that when the method returns @c false, the arguments remain unchanged.
7529 *
7530 * @param aMachine Session machine object.
7531 * @param aControl Direct session control object (optional).
7532 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7533 *
7534 * @note locks this object for reading.
7535 */
7536#if defined(RT_OS_WINDOWS)
7537bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7538 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7539 HANDLE *aIPCSem /*= NULL*/,
7540 bool aAllowClosing /*= false*/)
7541#elif defined(RT_OS_OS2)
7542bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7543 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7544 HMTX *aIPCSem /*= NULL*/,
7545 bool aAllowClosing /*= false*/)
7546#else
7547bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7548 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7549 bool aAllowClosing /*= false*/)
7550#endif
7551{
7552 AutoLimitedCaller autoCaller(this);
7553 AssertComRCReturn(autoCaller.rc(), false);
7554
7555 /* just return false for inaccessible machines */
7556 if (autoCaller.state() != Ready)
7557 return false;
7558
7559 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7560
7561 if ( mData->mSession.mState == SessionState_Locked
7562 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7563 )
7564 {
7565 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7566
7567 aMachine = mData->mSession.mMachine;
7568
7569 if (aControl != NULL)
7570 *aControl = mData->mSession.mDirectControl;
7571
7572#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7573 /* Additional session data */
7574 if (aIPCSem != NULL)
7575 *aIPCSem = aMachine->mIPCSem;
7576#endif
7577 return true;
7578 }
7579
7580 return false;
7581}
7582
7583/**
7584 * Returns @c true if the given machine has an spawning direct session and
7585 * returns and additional session data (on some platforms) if so.
7586 *
7587 * Note that when the method returns @c false, the arguments remain unchanged.
7588 *
7589 * @param aPID PID of the spawned direct session process.
7590 *
7591 * @note locks this object for reading.
7592 */
7593#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7594bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7595#else
7596bool Machine::isSessionSpawning()
7597#endif
7598{
7599 AutoLimitedCaller autoCaller(this);
7600 AssertComRCReturn(autoCaller.rc(), false);
7601
7602 /* just return false for inaccessible machines */
7603 if (autoCaller.state() != Ready)
7604 return false;
7605
7606 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7607
7608 if (mData->mSession.mState == SessionState_Spawning)
7609 {
7610#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7611 /* Additional session data */
7612 if (aPID != NULL)
7613 {
7614 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
7615 *aPID = mData->mSession.mPID;
7616 }
7617#endif
7618 return true;
7619 }
7620
7621 return false;
7622}
7623
7624/**
7625 * Called from the client watcher thread to check for unexpected client process
7626 * death during Session_Spawning state (e.g. before it successfully opened a
7627 * direct session).
7628 *
7629 * On Win32 and on OS/2, this method is called only when we've got the
7630 * direct client's process termination notification, so it always returns @c
7631 * true.
7632 *
7633 * On other platforms, this method returns @c true if the client process is
7634 * terminated and @c false if it's still alive.
7635 *
7636 * @note Locks this object for writing.
7637 */
7638bool Machine::checkForSpawnFailure()
7639{
7640 AutoCaller autoCaller(this);
7641 if (!autoCaller.isOk())
7642 {
7643 /* nothing to do */
7644 LogFlowThisFunc(("Already uninitialized!\n"));
7645 return true;
7646 }
7647
7648 /* VirtualBox::addProcessToReap() needs a write lock */
7649 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
7650
7651 if (mData->mSession.mState != SessionState_Spawning)
7652 {
7653 /* nothing to do */
7654 LogFlowThisFunc(("Not spawning any more!\n"));
7655 return true;
7656 }
7657
7658 HRESULT rc = S_OK;
7659
7660#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7661
7662 /* the process was already unexpectedly terminated, we just need to set an
7663 * error and finalize session spawning */
7664 rc = setError(E_FAIL,
7665 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
7666 getName().c_str());
7667#else
7668
7669 /* PID not yet initialized, skip check. */
7670 if (mData->mSession.mPID == NIL_RTPROCESS)
7671 return false;
7672
7673 RTPROCSTATUS status;
7674 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
7675 &status);
7676
7677 if (vrc != VERR_PROCESS_RUNNING)
7678 {
7679 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7680 rc = setError(E_FAIL,
7681 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7682 getName().c_str(), status.iStatus);
7683 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7684 rc = setError(E_FAIL,
7685 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7686 getName().c_str(), status.iStatus);
7687 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7688 rc = setError(E_FAIL,
7689 tr("The virtual machine '%s' has terminated abnormally"),
7690 getName().c_str(), status.iStatus);
7691 else
7692 rc = setError(E_FAIL,
7693 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7694 getName().c_str(), rc);
7695 }
7696
7697#endif
7698
7699 if (FAILED(rc))
7700 {
7701 /* Close the remote session, remove the remote control from the list
7702 * and reset session state to Closed (@note keep the code in sync with
7703 * the relevant part in checkForSpawnFailure()). */
7704
7705 Assert(mData->mSession.mRemoteControls.size() == 1);
7706 if (mData->mSession.mRemoteControls.size() == 1)
7707 {
7708 ErrorInfoKeeper eik;
7709 mData->mSession.mRemoteControls.front()->Uninitialize();
7710 }
7711
7712 mData->mSession.mRemoteControls.clear();
7713 mData->mSession.mState = SessionState_Unlocked;
7714
7715 /* finalize the progress after setting the state */
7716 if (!mData->mSession.mProgress.isNull())
7717 {
7718 mData->mSession.mProgress->notifyComplete(rc);
7719 mData->mSession.mProgress.setNull();
7720 }
7721
7722 mParent->addProcessToReap(mData->mSession.mPID);
7723 mData->mSession.mPID = NIL_RTPROCESS;
7724
7725 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7726 return true;
7727 }
7728
7729 return false;
7730}
7731
7732/**
7733 * Checks whether the machine can be registered. If so, commits and saves
7734 * all settings.
7735 *
7736 * @note Must be called from mParent's write lock. Locks this object and
7737 * children for writing.
7738 */
7739HRESULT Machine::prepareRegister()
7740{
7741 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7742
7743 AutoLimitedCaller autoCaller(this);
7744 AssertComRCReturnRC(autoCaller.rc());
7745
7746 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7747
7748 /* wait for state dependents to drop to zero */
7749 ensureNoStateDependencies();
7750
7751 if (!mData->mAccessible)
7752 return setError(VBOX_E_INVALID_OBJECT_STATE,
7753 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7754 mUserData->s.strName.c_str(),
7755 mData->mUuid.toString().c_str());
7756
7757 AssertReturn(autoCaller.state() == Ready, E_FAIL);
7758
7759 if (mData->mRegistered)
7760 return setError(VBOX_E_INVALID_OBJECT_STATE,
7761 tr("The machine '%s' with UUID {%s} is already registered"),
7762 mUserData->s.strName.c_str(),
7763 mData->mUuid.toString().c_str());
7764
7765 HRESULT rc = S_OK;
7766
7767 // Ensure the settings are saved. If we are going to be registered and
7768 // no config file exists yet, create it by calling saveSettings() too.
7769 if ( (mData->flModifications)
7770 || (!mData->pMachineConfigFile->fileExists())
7771 )
7772 {
7773 rc = saveSettings(NULL);
7774 // no need to check whether VirtualBox.xml needs saving too since
7775 // we can't have a machine XML file rename pending
7776 if (FAILED(rc)) return rc;
7777 }
7778
7779 /* more config checking goes here */
7780
7781 if (SUCCEEDED(rc))
7782 {
7783 /* we may have had implicit modifications we want to fix on success */
7784 commit();
7785
7786 mData->mRegistered = true;
7787 }
7788 else
7789 {
7790 /* we may have had implicit modifications we want to cancel on failure*/
7791 rollback(false /* aNotify */);
7792 }
7793
7794 return rc;
7795}
7796
7797/**
7798 * Increases the number of objects dependent on the machine state or on the
7799 * registered state. Guarantees that these two states will not change at least
7800 * until #releaseStateDependency() is called.
7801 *
7802 * Depending on the @a aDepType value, additional state checks may be made.
7803 * These checks will set extended error info on failure. See
7804 * #checkStateDependency() for more info.
7805 *
7806 * If this method returns a failure, the dependency is not added and the caller
7807 * is not allowed to rely on any particular machine state or registration state
7808 * value and may return the failed result code to the upper level.
7809 *
7810 * @param aDepType Dependency type to add.
7811 * @param aState Current machine state (NULL if not interested).
7812 * @param aRegistered Current registered state (NULL if not interested).
7813 *
7814 * @note Locks this object for writing.
7815 */
7816HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7817 MachineState_T *aState /* = NULL */,
7818 BOOL *aRegistered /* = NULL */)
7819{
7820 AutoCaller autoCaller(this);
7821 AssertComRCReturnRC(autoCaller.rc());
7822
7823 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7824
7825 HRESULT rc = checkStateDependency(aDepType);
7826 if (FAILED(rc)) return rc;
7827
7828 {
7829 if (mData->mMachineStateChangePending != 0)
7830 {
7831 /* ensureNoStateDependencies() is waiting for state dependencies to
7832 * drop to zero so don't add more. It may make sense to wait a bit
7833 * and retry before reporting an error (since the pending state
7834 * transition should be really quick) but let's just assert for
7835 * now to see if it ever happens on practice. */
7836
7837 AssertFailed();
7838
7839 return setError(E_ACCESSDENIED,
7840 tr("Machine state change is in progress. Please retry the operation later."));
7841 }
7842
7843 ++mData->mMachineStateDeps;
7844 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7845 }
7846
7847 if (aState)
7848 *aState = mData->mMachineState;
7849 if (aRegistered)
7850 *aRegistered = mData->mRegistered;
7851
7852 return S_OK;
7853}
7854
7855/**
7856 * Decreases the number of objects dependent on the machine state.
7857 * Must always complete the #addStateDependency() call after the state
7858 * dependency is no more necessary.
7859 */
7860void Machine::releaseStateDependency()
7861{
7862 AutoCaller autoCaller(this);
7863 AssertComRCReturnVoid(autoCaller.rc());
7864
7865 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7866
7867 /* releaseStateDependency() w/o addStateDependency()? */
7868 AssertReturnVoid(mData->mMachineStateDeps != 0);
7869 -- mData->mMachineStateDeps;
7870
7871 if (mData->mMachineStateDeps == 0)
7872 {
7873 /* inform ensureNoStateDependencies() that there are no more deps */
7874 if (mData->mMachineStateChangePending != 0)
7875 {
7876 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7877 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7878 }
7879 }
7880}
7881
7882// protected methods
7883/////////////////////////////////////////////////////////////////////////////
7884
7885/**
7886 * Performs machine state checks based on the @a aDepType value. If a check
7887 * fails, this method will set extended error info, otherwise it will return
7888 * S_OK. It is supposed, that on failure, the caller will immediately return
7889 * the return value of this method to the upper level.
7890 *
7891 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7892 *
7893 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7894 * current state of this machine object allows to change settings of the
7895 * machine (i.e. the machine is not registered, or registered but not running
7896 * and not saved). It is useful to call this method from Machine setters
7897 * before performing any change.
7898 *
7899 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7900 * as for MutableStateDep except that if the machine is saved, S_OK is also
7901 * returned. This is useful in setters which allow changing machine
7902 * properties when it is in the saved state.
7903 *
7904 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7905 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7906 * Aborted).
7907 *
7908 * @param aDepType Dependency type to check.
7909 *
7910 * @note Non Machine based classes should use #addStateDependency() and
7911 * #releaseStateDependency() methods or the smart AutoStateDependency
7912 * template.
7913 *
7914 * @note This method must be called from under this object's read or write
7915 * lock.
7916 */
7917HRESULT Machine::checkStateDependency(StateDependency aDepType)
7918{
7919 switch (aDepType)
7920 {
7921 case AnyStateDep:
7922 {
7923 break;
7924 }
7925 case MutableStateDep:
7926 {
7927 if ( mData->mRegistered
7928 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7929 || ( mData->mMachineState != MachineState_Paused
7930 && mData->mMachineState != MachineState_Running
7931 && mData->mMachineState != MachineState_Aborted
7932 && mData->mMachineState != MachineState_Teleported
7933 && mData->mMachineState != MachineState_PoweredOff
7934 )
7935 )
7936 )
7937 return setError(VBOX_E_INVALID_VM_STATE,
7938 tr("The machine is not mutable (state is %s)"),
7939 Global::stringifyMachineState(mData->mMachineState));
7940 break;
7941 }
7942 case MutableOrSavedStateDep:
7943 {
7944 if ( mData->mRegistered
7945 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7946 || ( mData->mMachineState != MachineState_Paused
7947 && mData->mMachineState != MachineState_Running
7948 && mData->mMachineState != MachineState_Aborted
7949 && mData->mMachineState != MachineState_Teleported
7950 && mData->mMachineState != MachineState_Saved
7951 && mData->mMachineState != MachineState_PoweredOff
7952 )
7953 )
7954 )
7955 return setError(VBOX_E_INVALID_VM_STATE,
7956 tr("The machine is not mutable (state is %s)"),
7957 Global::stringifyMachineState(mData->mMachineState));
7958 break;
7959 }
7960 case OfflineStateDep:
7961 {
7962 if ( mData->mRegistered
7963 && ( !isSessionMachine()
7964 || ( mData->mMachineState != MachineState_PoweredOff
7965 && mData->mMachineState != MachineState_Saved
7966 && mData->mMachineState != MachineState_Aborted
7967 && mData->mMachineState != MachineState_Teleported
7968 )
7969 )
7970 )
7971 return setError(VBOX_E_INVALID_VM_STATE,
7972 tr("The machine is not offline (state is %s)"),
7973 Global::stringifyMachineState(mData->mMachineState));
7974 break;
7975 }
7976 }
7977
7978 return S_OK;
7979}
7980
7981/**
7982 * Helper to initialize all associated child objects and allocate data
7983 * structures.
7984 *
7985 * This method must be called as a part of the object's initialization procedure
7986 * (usually done in the #init() method).
7987 *
7988 * @note Must be called only from #init() or from #registeredInit().
7989 */
7990HRESULT Machine::initDataAndChildObjects()
7991{
7992 AutoCaller autoCaller(this);
7993 AssertComRCReturnRC(autoCaller.rc());
7994 AssertComRCReturn(autoCaller.state() == InInit ||
7995 autoCaller.state() == Limited, E_FAIL);
7996
7997 AssertReturn(!mData->mAccessible, E_FAIL);
7998
7999 /* allocate data structures */
8000 mSSData.allocate();
8001 mUserData.allocate();
8002 mHWData.allocate();
8003 mMediaData.allocate();
8004 mStorageControllers.allocate();
8005
8006 /* initialize mOSTypeId */
8007 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
8008
8009 /* create associated BIOS settings object */
8010 unconst(mBIOSSettings).createObject();
8011 mBIOSSettings->init(this);
8012
8013 /* create an associated VRDE object (default is disabled) */
8014 unconst(mVRDEServer).createObject();
8015 mVRDEServer->init(this);
8016
8017 /* create associated serial port objects */
8018 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8019 {
8020 unconst(mSerialPorts[slot]).createObject();
8021 mSerialPorts[slot]->init(this, slot);
8022 }
8023
8024 /* create associated parallel port objects */
8025 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8026 {
8027 unconst(mParallelPorts[slot]).createObject();
8028 mParallelPorts[slot]->init(this, slot);
8029 }
8030
8031 /* create the audio adapter object (always present, default is disabled) */
8032 unconst(mAudioAdapter).createObject();
8033 mAudioAdapter->init(this);
8034
8035 /* create the USB controller object (always present, default is disabled) */
8036 unconst(mUSBController).createObject();
8037 mUSBController->init(this);
8038
8039 /* create associated network adapter objects */
8040 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8041 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8042 {
8043 unconst(mNetworkAdapters[slot]).createObject();
8044 mNetworkAdapters[slot]->init(this, slot);
8045 }
8046
8047 /* create the bandwidth control */
8048 unconst(mBandwidthControl).createObject();
8049 mBandwidthControl->init(this);
8050
8051 return S_OK;
8052}
8053
8054/**
8055 * Helper to uninitialize all associated child objects and to free all data
8056 * structures.
8057 *
8058 * This method must be called as a part of the object's uninitialization
8059 * procedure (usually done in the #uninit() method).
8060 *
8061 * @note Must be called only from #uninit() or from #registeredInit().
8062 */
8063void Machine::uninitDataAndChildObjects()
8064{
8065 AutoCaller autoCaller(this);
8066 AssertComRCReturnVoid(autoCaller.rc());
8067 AssertComRCReturnVoid( autoCaller.state() == InUninit
8068 || autoCaller.state() == Limited);
8069
8070 /* tell all our other child objects we've been uninitialized */
8071 if (mBandwidthControl)
8072 {
8073 mBandwidthControl->uninit();
8074 unconst(mBandwidthControl).setNull();
8075 }
8076
8077 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8078 {
8079 if (mNetworkAdapters[slot])
8080 {
8081 mNetworkAdapters[slot]->uninit();
8082 unconst(mNetworkAdapters[slot]).setNull();
8083 }
8084 }
8085
8086 if (mUSBController)
8087 {
8088 mUSBController->uninit();
8089 unconst(mUSBController).setNull();
8090 }
8091
8092 if (mAudioAdapter)
8093 {
8094 mAudioAdapter->uninit();
8095 unconst(mAudioAdapter).setNull();
8096 }
8097
8098 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8099 {
8100 if (mParallelPorts[slot])
8101 {
8102 mParallelPorts[slot]->uninit();
8103 unconst(mParallelPorts[slot]).setNull();
8104 }
8105 }
8106
8107 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8108 {
8109 if (mSerialPorts[slot])
8110 {
8111 mSerialPorts[slot]->uninit();
8112 unconst(mSerialPorts[slot]).setNull();
8113 }
8114 }
8115
8116 if (mVRDEServer)
8117 {
8118 mVRDEServer->uninit();
8119 unconst(mVRDEServer).setNull();
8120 }
8121
8122 if (mBIOSSettings)
8123 {
8124 mBIOSSettings->uninit();
8125 unconst(mBIOSSettings).setNull();
8126 }
8127
8128 /* Deassociate media (only when a real Machine or a SnapshotMachine
8129 * instance is uninitialized; SessionMachine instances refer to real
8130 * Machine media). This is necessary for a clean re-initialization of
8131 * the VM after successfully re-checking the accessibility state. Note
8132 * that in case of normal Machine or SnapshotMachine uninitialization (as
8133 * a result of unregistering or deleting the snapshot), outdated media
8134 * attachments will already be uninitialized and deleted, so this
8135 * code will not affect them. */
8136 if ( !!mMediaData
8137 && (!isSessionMachine())
8138 )
8139 {
8140 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8141 it != mMediaData->mAttachments.end();
8142 ++it)
8143 {
8144 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8145 if (pMedium.isNull())
8146 continue;
8147 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8148 AssertComRC(rc);
8149 }
8150 }
8151
8152 if (!isSessionMachine() && !isSnapshotMachine())
8153 {
8154 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8155 if (mData->mFirstSnapshot)
8156 {
8157 // snapshots tree is protected by machine write lock; strictly
8158 // this isn't necessary here since we're deleting the entire
8159 // machine, but otherwise we assert in Snapshot::uninit()
8160 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8161 mData->mFirstSnapshot->uninit();
8162 mData->mFirstSnapshot.setNull();
8163 }
8164
8165 mData->mCurrentSnapshot.setNull();
8166 }
8167
8168 /* free data structures (the essential mData structure is not freed here
8169 * since it may be still in use) */
8170 mMediaData.free();
8171 mStorageControllers.free();
8172 mHWData.free();
8173 mUserData.free();
8174 mSSData.free();
8175}
8176
8177/**
8178 * Returns a pointer to the Machine object for this machine that acts like a
8179 * parent for complex machine data objects such as shared folders, etc.
8180 *
8181 * For primary Machine objects and for SnapshotMachine objects, returns this
8182 * object's pointer itself. For SessionMachine objects, returns the peer
8183 * (primary) machine pointer.
8184 */
8185Machine* Machine::getMachine()
8186{
8187 if (isSessionMachine())
8188 return (Machine*)mPeer;
8189 return this;
8190}
8191
8192/**
8193 * Makes sure that there are no machine state dependents. If necessary, waits
8194 * for the number of dependents to drop to zero.
8195 *
8196 * Make sure this method is called from under this object's write lock to
8197 * guarantee that no new dependents may be added when this method returns
8198 * control to the caller.
8199 *
8200 * @note Locks this object for writing. The lock will be released while waiting
8201 * (if necessary).
8202 *
8203 * @warning To be used only in methods that change the machine state!
8204 */
8205void Machine::ensureNoStateDependencies()
8206{
8207 AssertReturnVoid(isWriteLockOnCurrentThread());
8208
8209 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8210
8211 /* Wait for all state dependents if necessary */
8212 if (mData->mMachineStateDeps != 0)
8213 {
8214 /* lazy semaphore creation */
8215 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8216 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8217
8218 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8219 mData->mMachineStateDeps));
8220
8221 ++mData->mMachineStateChangePending;
8222
8223 /* reset the semaphore before waiting, the last dependent will signal
8224 * it */
8225 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8226
8227 alock.release();
8228
8229 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8230
8231 alock.acquire();
8232
8233 -- mData->mMachineStateChangePending;
8234 }
8235}
8236
8237/**
8238 * Changes the machine state and informs callbacks.
8239 *
8240 * This method is not intended to fail so it either returns S_OK or asserts (and
8241 * returns a failure).
8242 *
8243 * @note Locks this object for writing.
8244 */
8245HRESULT Machine::setMachineState(MachineState_T aMachineState)
8246{
8247 LogFlowThisFuncEnter();
8248 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8249
8250 AutoCaller autoCaller(this);
8251 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8252
8253 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8254
8255 /* wait for state dependents to drop to zero */
8256 ensureNoStateDependencies();
8257
8258 if (mData->mMachineState != aMachineState)
8259 {
8260 mData->mMachineState = aMachineState;
8261
8262 RTTimeNow(&mData->mLastStateChange);
8263
8264 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8265 }
8266
8267 LogFlowThisFuncLeave();
8268 return S_OK;
8269}
8270
8271/**
8272 * Searches for a shared folder with the given logical name
8273 * in the collection of shared folders.
8274 *
8275 * @param aName logical name of the shared folder
8276 * @param aSharedFolder where to return the found object
8277 * @param aSetError whether to set the error info if the folder is
8278 * not found
8279 * @return
8280 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8281 *
8282 * @note
8283 * must be called from under the object's lock!
8284 */
8285HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8286 ComObjPtr<SharedFolder> &aSharedFolder,
8287 bool aSetError /* = false */)
8288{
8289 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8290 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8291 it != mHWData->mSharedFolders.end();
8292 ++it)
8293 {
8294 SharedFolder *pSF = *it;
8295 AutoCaller autoCaller(pSF);
8296 if (pSF->getName() == aName)
8297 {
8298 aSharedFolder = pSF;
8299 rc = S_OK;
8300 break;
8301 }
8302 }
8303
8304 if (aSetError && FAILED(rc))
8305 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8306
8307 return rc;
8308}
8309
8310/**
8311 * Initializes all machine instance data from the given settings structures
8312 * from XML. The exception is the machine UUID which needs special handling
8313 * depending on the caller's use case, so the caller needs to set that herself.
8314 *
8315 * This gets called in several contexts during machine initialization:
8316 *
8317 * -- When machine XML exists on disk already and needs to be loaded into memory,
8318 * for example, from registeredInit() to load all registered machines on
8319 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8320 * attached to the machine should be part of some media registry already.
8321 *
8322 * -- During OVF import, when a machine config has been constructed from an
8323 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8324 * ensure that the media listed as attachments in the config (which have
8325 * been imported from the OVF) receive the correct registry ID.
8326 *
8327 * -- During VM cloning.
8328 *
8329 * @param config Machine settings from XML.
8330 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8331 * @return
8332 */
8333HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8334 const Guid *puuidRegistry)
8335{
8336 // copy name, description, OS type, teleporter, UTC etc.
8337 mUserData->s = config.machineUserData;
8338
8339 // look up the object by Id to check it is valid
8340 ComPtr<IGuestOSType> guestOSType;
8341 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8342 guestOSType.asOutParam());
8343 if (FAILED(rc)) return rc;
8344
8345 // stateFile (optional)
8346 if (config.strStateFile.isEmpty())
8347 mSSData->strStateFilePath.setNull();
8348 else
8349 {
8350 Utf8Str stateFilePathFull(config.strStateFile);
8351 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8352 if (RT_FAILURE(vrc))
8353 return setError(E_FAIL,
8354 tr("Invalid saved state file path '%s' (%Rrc)"),
8355 config.strStateFile.c_str(),
8356 vrc);
8357 mSSData->strStateFilePath = stateFilePathFull;
8358 }
8359
8360 // snapshot folder needs special processing so set it again
8361 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8362 if (FAILED(rc)) return rc;
8363
8364 /* Copy the extra data items (Not in any case config is already the same as
8365 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8366 * make sure the extra data map is copied). */
8367 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8368
8369 /* currentStateModified (optional, default is true) */
8370 mData->mCurrentStateModified = config.fCurrentStateModified;
8371
8372 mData->mLastStateChange = config.timeLastStateChange;
8373
8374 /*
8375 * note: all mUserData members must be assigned prior this point because
8376 * we need to commit changes in order to let mUserData be shared by all
8377 * snapshot machine instances.
8378 */
8379 mUserData.commitCopy();
8380
8381 // machine registry, if present (must be loaded before snapshots)
8382 if (config.canHaveOwnMediaRegistry())
8383 {
8384 // determine machine folder
8385 Utf8Str strMachineFolder = getSettingsFileFull();
8386 strMachineFolder.stripFilename();
8387 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8388 config.mediaRegistry,
8389 strMachineFolder);
8390 if (FAILED(rc)) return rc;
8391 }
8392
8393 /* Snapshot node (optional) */
8394 size_t cRootSnapshots;
8395 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8396 {
8397 // there must be only one root snapshot
8398 Assert(cRootSnapshots == 1);
8399
8400 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8401
8402 rc = loadSnapshot(snap,
8403 config.uuidCurrentSnapshot,
8404 NULL); // no parent == first snapshot
8405 if (FAILED(rc)) return rc;
8406 }
8407
8408 // hardware data
8409 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8410 if (FAILED(rc)) return rc;
8411
8412 // load storage controllers
8413 rc = loadStorageControllers(config.storageMachine,
8414 puuidRegistry,
8415 NULL /* puuidSnapshot */);
8416 if (FAILED(rc)) return rc;
8417
8418 /*
8419 * NOTE: the assignment below must be the last thing to do,
8420 * otherwise it will be not possible to change the settings
8421 * somewhere in the code above because all setters will be
8422 * blocked by checkStateDependency(MutableStateDep).
8423 */
8424
8425 /* set the machine state to Aborted or Saved when appropriate */
8426 if (config.fAborted)
8427 {
8428 mSSData->strStateFilePath.setNull();
8429
8430 /* no need to use setMachineState() during init() */
8431 mData->mMachineState = MachineState_Aborted;
8432 }
8433 else if (!mSSData->strStateFilePath.isEmpty())
8434 {
8435 /* no need to use setMachineState() during init() */
8436 mData->mMachineState = MachineState_Saved;
8437 }
8438
8439 // after loading settings, we are no longer different from the XML on disk
8440 mData->flModifications = 0;
8441
8442 return S_OK;
8443}
8444
8445/**
8446 * Recursively loads all snapshots starting from the given.
8447 *
8448 * @param aNode <Snapshot> node.
8449 * @param aCurSnapshotId Current snapshot ID from the settings file.
8450 * @param aParentSnapshot Parent snapshot.
8451 */
8452HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8453 const Guid &aCurSnapshotId,
8454 Snapshot *aParentSnapshot)
8455{
8456 AssertReturn(!isSnapshotMachine(), E_FAIL);
8457 AssertReturn(!isSessionMachine(), E_FAIL);
8458
8459 HRESULT rc = S_OK;
8460
8461 Utf8Str strStateFile;
8462 if (!data.strStateFile.isEmpty())
8463 {
8464 /* optional */
8465 strStateFile = data.strStateFile;
8466 int vrc = calculateFullPath(strStateFile, strStateFile);
8467 if (RT_FAILURE(vrc))
8468 return setError(E_FAIL,
8469 tr("Invalid saved state file path '%s' (%Rrc)"),
8470 strStateFile.c_str(),
8471 vrc);
8472 }
8473
8474 /* create a snapshot machine object */
8475 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8476 pSnapshotMachine.createObject();
8477 rc = pSnapshotMachine->initFromSettings(this,
8478 data.hardware,
8479 &data.debugging,
8480 &data.autostart,
8481 data.storage,
8482 data.uuid.ref(),
8483 strStateFile);
8484 if (FAILED(rc)) return rc;
8485
8486 /* create a snapshot object */
8487 ComObjPtr<Snapshot> pSnapshot;
8488 pSnapshot.createObject();
8489 /* initialize the snapshot */
8490 rc = pSnapshot->init(mParent, // VirtualBox object
8491 data.uuid,
8492 data.strName,
8493 data.strDescription,
8494 data.timestamp,
8495 pSnapshotMachine,
8496 aParentSnapshot);
8497 if (FAILED(rc)) return rc;
8498
8499 /* memorize the first snapshot if necessary */
8500 if (!mData->mFirstSnapshot)
8501 mData->mFirstSnapshot = pSnapshot;
8502
8503 /* memorize the current snapshot when appropriate */
8504 if ( !mData->mCurrentSnapshot
8505 && pSnapshot->getId() == aCurSnapshotId
8506 )
8507 mData->mCurrentSnapshot = pSnapshot;
8508
8509 // now create the children
8510 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8511 it != data.llChildSnapshots.end();
8512 ++it)
8513 {
8514 const settings::Snapshot &childData = *it;
8515 // recurse
8516 rc = loadSnapshot(childData,
8517 aCurSnapshotId,
8518 pSnapshot); // parent = the one we created above
8519 if (FAILED(rc)) return rc;
8520 }
8521
8522 return rc;
8523}
8524
8525/**
8526 * Loads settings into mHWData.
8527 *
8528 * @param data Reference to the hardware settings.
8529 * @param pDbg Pointer to the debugging settings.
8530 * @param pAutostart Pointer to the autostart settings.
8531 */
8532HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8533 const settings::Autostart *pAutostart)
8534{
8535 AssertReturn(!isSessionMachine(), E_FAIL);
8536
8537 HRESULT rc = S_OK;
8538
8539 try
8540 {
8541 /* The hardware version attribute (optional). */
8542 mHWData->mHWVersion = data.strVersion;
8543 mHWData->mHardwareUUID = data.uuid;
8544
8545 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8546 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8547 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8548 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8549 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8550 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8551 mHWData->mPAEEnabled = data.fPAE;
8552 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8553
8554 mHWData->mCPUCount = data.cCPUs;
8555 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8556 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8557
8558 // cpu
8559 if (mHWData->mCPUHotPlugEnabled)
8560 {
8561 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8562 it != data.llCpus.end();
8563 ++it)
8564 {
8565 const settings::Cpu &cpu = *it;
8566
8567 mHWData->mCPUAttached[cpu.ulId] = true;
8568 }
8569 }
8570
8571 // cpuid leafs
8572 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8573 it != data.llCpuIdLeafs.end();
8574 ++it)
8575 {
8576 const settings::CpuIdLeaf &leaf = *it;
8577
8578 switch (leaf.ulId)
8579 {
8580 case 0x0:
8581 case 0x1:
8582 case 0x2:
8583 case 0x3:
8584 case 0x4:
8585 case 0x5:
8586 case 0x6:
8587 case 0x7:
8588 case 0x8:
8589 case 0x9:
8590 case 0xA:
8591 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8592 break;
8593
8594 case 0x80000000:
8595 case 0x80000001:
8596 case 0x80000002:
8597 case 0x80000003:
8598 case 0x80000004:
8599 case 0x80000005:
8600 case 0x80000006:
8601 case 0x80000007:
8602 case 0x80000008:
8603 case 0x80000009:
8604 case 0x8000000A:
8605 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8606 break;
8607
8608 default:
8609 /* just ignore */
8610 break;
8611 }
8612 }
8613
8614 mHWData->mMemorySize = data.ulMemorySizeMB;
8615 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8616
8617 // boot order
8618 for (size_t i = 0;
8619 i < RT_ELEMENTS(mHWData->mBootOrder);
8620 i++)
8621 {
8622 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8623 if (it == data.mapBootOrder.end())
8624 mHWData->mBootOrder[i] = DeviceType_Null;
8625 else
8626 mHWData->mBootOrder[i] = it->second;
8627 }
8628
8629 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8630 mHWData->mMonitorCount = data.cMonitors;
8631 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8632 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8633 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8634 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8635 mHWData->mVideoCaptureEnabled = false; /* @todo r=klaus restore to data.fVideoCaptureEnabled */
8636 mHWData->mVideoCaptureFile = data.strVideoCaptureFile;
8637 mHWData->mFirmwareType = data.firmwareType;
8638 mHWData->mPointingHIDType = data.pointingHIDType;
8639 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8640 mHWData->mChipsetType = data.chipsetType;
8641 mHWData->mEmulatedUSBWebcamEnabled = data.fEmulatedUSBWebcam;
8642 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8643 mHWData->mHPETEnabled = data.fHPETEnabled;
8644
8645 /* VRDEServer */
8646 rc = mVRDEServer->loadSettings(data.vrdeSettings);
8647 if (FAILED(rc)) return rc;
8648
8649 /* BIOS */
8650 rc = mBIOSSettings->loadSettings(data.biosSettings);
8651 if (FAILED(rc)) return rc;
8652
8653 // Bandwidth control (must come before network adapters)
8654 rc = mBandwidthControl->loadSettings(data.ioSettings);
8655 if (FAILED(rc)) return rc;
8656
8657 /* USB Controller */
8658 rc = mUSBController->loadSettings(data.usbController);
8659 if (FAILED(rc)) return rc;
8660
8661 // network adapters
8662 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8663 uint32_t oldCount = mNetworkAdapters.size();
8664 if (newCount > oldCount)
8665 {
8666 mNetworkAdapters.resize(newCount);
8667 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
8668 {
8669 unconst(mNetworkAdapters[slot]).createObject();
8670 mNetworkAdapters[slot]->init(this, slot);
8671 }
8672 }
8673 else if (newCount < oldCount)
8674 mNetworkAdapters.resize(newCount);
8675 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8676 it != data.llNetworkAdapters.end();
8677 ++it)
8678 {
8679 const settings::NetworkAdapter &nic = *it;
8680
8681 /* slot unicity is guaranteed by XML Schema */
8682 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8683 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
8684 if (FAILED(rc)) return rc;
8685 }
8686
8687 // serial ports
8688 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8689 it != data.llSerialPorts.end();
8690 ++it)
8691 {
8692 const settings::SerialPort &s = *it;
8693
8694 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8695 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
8696 if (FAILED(rc)) return rc;
8697 }
8698
8699 // parallel ports (optional)
8700 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8701 it != data.llParallelPorts.end();
8702 ++it)
8703 {
8704 const settings::ParallelPort &p = *it;
8705
8706 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8707 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
8708 if (FAILED(rc)) return rc;
8709 }
8710
8711 /* AudioAdapter */
8712 rc = mAudioAdapter->loadSettings(data.audioAdapter);
8713 if (FAILED(rc)) return rc;
8714
8715 /* Shared folders */
8716 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8717 it != data.llSharedFolders.end();
8718 ++it)
8719 {
8720 const settings::SharedFolder &sf = *it;
8721
8722 ComObjPtr<SharedFolder> sharedFolder;
8723 /* Check for double entries. Not allowed! */
8724 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8725 if (SUCCEEDED(rc))
8726 return setError(VBOX_E_OBJECT_IN_USE,
8727 tr("Shared folder named '%s' already exists"),
8728 sf.strName.c_str());
8729
8730 /* Create the new shared folder. Don't break on error. This will be
8731 * reported when the machine starts. */
8732 sharedFolder.createObject();
8733 rc = sharedFolder->init(getMachine(),
8734 sf.strName,
8735 sf.strHostPath,
8736 RT_BOOL(sf.fWritable),
8737 RT_BOOL(sf.fAutoMount),
8738 false /* fFailOnError */);
8739 if (FAILED(rc)) return rc;
8740 mHWData->mSharedFolders.push_back(sharedFolder);
8741 }
8742
8743 // Clipboard
8744 mHWData->mClipboardMode = data.clipboardMode;
8745
8746 // drag'n'drop
8747 mHWData->mDragAndDropMode = data.dragAndDropMode;
8748
8749 // guest settings
8750 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8751
8752 // IO settings
8753 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8754 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8755
8756 // Host PCI devices
8757 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8758 it != data.pciAttachments.end();
8759 ++it)
8760 {
8761 const settings::HostPCIDeviceAttachment &hpda = *it;
8762 ComObjPtr<PCIDeviceAttachment> pda;
8763
8764 pda.createObject();
8765 pda->loadSettings(this, hpda);
8766 mHWData->mPCIDeviceAssignments.push_back(pda);
8767 }
8768
8769 /*
8770 * (The following isn't really real hardware, but it lives in HWData
8771 * for reasons of convenience.)
8772 */
8773
8774#ifdef VBOX_WITH_GUEST_PROPS
8775 /* Guest properties (optional) */
8776 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8777 it != data.llGuestProperties.end();
8778 ++it)
8779 {
8780 const settings::GuestProperty &prop = *it;
8781 uint32_t fFlags = guestProp::NILFLAG;
8782 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8783 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8784 mHWData->mGuestProperties[prop.strName] = property;
8785 }
8786
8787 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8788#endif /* VBOX_WITH_GUEST_PROPS defined */
8789
8790 rc = loadDebugging(pDbg);
8791 if (FAILED(rc))
8792 return rc;
8793
8794 mHWData->mAutostart = *pAutostart;
8795
8796 /* default frontend */
8797 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8798 }
8799 catch(std::bad_alloc &)
8800 {
8801 return E_OUTOFMEMORY;
8802 }
8803
8804 AssertComRC(rc);
8805 return rc;
8806}
8807
8808/**
8809 * Called from Machine::loadHardware() to load the debugging settings of the
8810 * machine.
8811 *
8812 * @param pDbg Pointer to the settings.
8813 */
8814HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
8815{
8816 mHWData->mDebugging = *pDbg;
8817 /* no more processing currently required, this will probably change. */
8818 return S_OK;
8819}
8820
8821/**
8822 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
8823 *
8824 * @param data
8825 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8826 * @param puuidSnapshot
8827 * @return
8828 */
8829HRESULT Machine::loadStorageControllers(const settings::Storage &data,
8830 const Guid *puuidRegistry,
8831 const Guid *puuidSnapshot)
8832{
8833 AssertReturn(!isSessionMachine(), E_FAIL);
8834
8835 HRESULT rc = S_OK;
8836
8837 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8838 it != data.llStorageControllers.end();
8839 ++it)
8840 {
8841 const settings::StorageController &ctlData = *it;
8842
8843 ComObjPtr<StorageController> pCtl;
8844 /* Try to find one with the name first. */
8845 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8846 if (SUCCEEDED(rc))
8847 return setError(VBOX_E_OBJECT_IN_USE,
8848 tr("Storage controller named '%s' already exists"),
8849 ctlData.strName.c_str());
8850
8851 pCtl.createObject();
8852 rc = pCtl->init(this,
8853 ctlData.strName,
8854 ctlData.storageBus,
8855 ctlData.ulInstance,
8856 ctlData.fBootable);
8857 if (FAILED(rc)) return rc;
8858
8859 mStorageControllers->push_back(pCtl);
8860
8861 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8862 if (FAILED(rc)) return rc;
8863
8864 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8865 if (FAILED(rc)) return rc;
8866
8867 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8868 if (FAILED(rc)) return rc;
8869
8870 /* Set IDE emulation settings (only for AHCI controller). */
8871 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8872 {
8873 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8874 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8875 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8876 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8877 )
8878 return rc;
8879 }
8880
8881 /* Load the attached devices now. */
8882 rc = loadStorageDevices(pCtl,
8883 ctlData,
8884 puuidRegistry,
8885 puuidSnapshot);
8886 if (FAILED(rc)) return rc;
8887 }
8888
8889 return S_OK;
8890}
8891
8892/**
8893 * Called from loadStorageControllers for a controller's devices.
8894 *
8895 * @param aStorageController
8896 * @param data
8897 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8898 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
8899 * @return
8900 */
8901HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
8902 const settings::StorageController &data,
8903 const Guid *puuidRegistry,
8904 const Guid *puuidSnapshot)
8905{
8906 HRESULT rc = S_OK;
8907
8908 /* paranoia: detect duplicate attachments */
8909 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8910 it != data.llAttachedDevices.end();
8911 ++it)
8912 {
8913 const settings::AttachedDevice &ad = *it;
8914
8915 for (settings::AttachedDevicesList::const_iterator it2 = it;
8916 it2 != data.llAttachedDevices.end();
8917 ++it2)
8918 {
8919 if (it == it2)
8920 continue;
8921
8922 const settings::AttachedDevice &ad2 = *it2;
8923
8924 if ( ad.lPort == ad2.lPort
8925 && ad.lDevice == ad2.lDevice)
8926 {
8927 return setError(E_FAIL,
8928 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
8929 aStorageController->getName().c_str(),
8930 ad.lPort,
8931 ad.lDevice,
8932 mUserData->s.strName.c_str());
8933 }
8934 }
8935 }
8936
8937 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8938 it != data.llAttachedDevices.end();
8939 ++it)
8940 {
8941 const settings::AttachedDevice &dev = *it;
8942 ComObjPtr<Medium> medium;
8943
8944 switch (dev.deviceType)
8945 {
8946 case DeviceType_Floppy:
8947 case DeviceType_DVD:
8948 if (dev.strHostDriveSrc.isNotEmpty())
8949 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
8950 else
8951 rc = mParent->findRemoveableMedium(dev.deviceType,
8952 dev.uuid,
8953 false /* fRefresh */,
8954 false /* aSetError */,
8955 medium);
8956 if (rc == VBOX_E_OBJECT_NOT_FOUND)
8957 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
8958 rc = S_OK;
8959 break;
8960
8961 case DeviceType_HardDisk:
8962 {
8963 /* find a hard disk by UUID */
8964 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
8965 if (FAILED(rc))
8966 {
8967 if (isSnapshotMachine())
8968 {
8969 // wrap another error message around the "cannot find hard disk" set by findHardDisk
8970 // so the user knows that the bad disk is in a snapshot somewhere
8971 com::ErrorInfo info;
8972 return setError(E_FAIL,
8973 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
8974 puuidSnapshot->raw(),
8975 info.getText().raw());
8976 }
8977 else
8978 return rc;
8979 }
8980
8981 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
8982
8983 if (medium->getType() == MediumType_Immutable)
8984 {
8985 if (isSnapshotMachine())
8986 return setError(E_FAIL,
8987 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8988 "of the virtual machine '%s' ('%s')"),
8989 medium->getLocationFull().c_str(),
8990 dev.uuid.raw(),
8991 puuidSnapshot->raw(),
8992 mUserData->s.strName.c_str(),
8993 mData->m_strConfigFileFull.c_str());
8994
8995 return setError(E_FAIL,
8996 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8997 medium->getLocationFull().c_str(),
8998 dev.uuid.raw(),
8999 mUserData->s.strName.c_str(),
9000 mData->m_strConfigFileFull.c_str());
9001 }
9002
9003 if (medium->getType() == MediumType_MultiAttach)
9004 {
9005 if (isSnapshotMachine())
9006 return setError(E_FAIL,
9007 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9008 "of the virtual machine '%s' ('%s')"),
9009 medium->getLocationFull().c_str(),
9010 dev.uuid.raw(),
9011 puuidSnapshot->raw(),
9012 mUserData->s.strName.c_str(),
9013 mData->m_strConfigFileFull.c_str());
9014
9015 return setError(E_FAIL,
9016 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9017 medium->getLocationFull().c_str(),
9018 dev.uuid.raw(),
9019 mUserData->s.strName.c_str(),
9020 mData->m_strConfigFileFull.c_str());
9021 }
9022
9023 if ( !isSnapshotMachine()
9024 && medium->getChildren().size() != 0
9025 )
9026 return setError(E_FAIL,
9027 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9028 "because it has %d differencing child hard disks"),
9029 medium->getLocationFull().c_str(),
9030 dev.uuid.raw(),
9031 mUserData->s.strName.c_str(),
9032 mData->m_strConfigFileFull.c_str(),
9033 medium->getChildren().size());
9034
9035 if (findAttachment(mMediaData->mAttachments,
9036 medium))
9037 return setError(E_FAIL,
9038 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9039 medium->getLocationFull().c_str(),
9040 dev.uuid.raw(),
9041 mUserData->s.strName.c_str(),
9042 mData->m_strConfigFileFull.c_str());
9043
9044 break;
9045 }
9046
9047 default:
9048 return setError(E_FAIL,
9049 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9050 medium->getLocationFull().c_str(),
9051 mUserData->s.strName.c_str(),
9052 mData->m_strConfigFileFull.c_str());
9053 }
9054
9055 if (FAILED(rc))
9056 break;
9057
9058 /* Bandwidth groups are loaded at this point. */
9059 ComObjPtr<BandwidthGroup> pBwGroup;
9060
9061 if (!dev.strBwGroup.isEmpty())
9062 {
9063 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9064 if (FAILED(rc))
9065 return setError(E_FAIL,
9066 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9067 medium->getLocationFull().c_str(),
9068 dev.strBwGroup.c_str(),
9069 mUserData->s.strName.c_str(),
9070 mData->m_strConfigFileFull.c_str());
9071 pBwGroup->reference();
9072 }
9073
9074 const Bstr controllerName = aStorageController->getName();
9075 ComObjPtr<MediumAttachment> pAttachment;
9076 pAttachment.createObject();
9077 rc = pAttachment->init(this,
9078 medium,
9079 controllerName,
9080 dev.lPort,
9081 dev.lDevice,
9082 dev.deviceType,
9083 false,
9084 dev.fPassThrough,
9085 dev.fTempEject,
9086 dev.fNonRotational,
9087 dev.fDiscard,
9088 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
9089 if (FAILED(rc)) break;
9090
9091 /* associate the medium with this machine and snapshot */
9092 if (!medium.isNull())
9093 {
9094 AutoCaller medCaller(medium);
9095 if (FAILED(medCaller.rc())) return medCaller.rc();
9096 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9097
9098 if (isSnapshotMachine())
9099 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
9100 else
9101 rc = medium->addBackReference(mData->mUuid);
9102 /* If the medium->addBackReference fails it sets an appropriate
9103 * error message, so no need to do any guesswork here. */
9104
9105 if (puuidRegistry)
9106 // caller wants registry ID to be set on all attached media (OVF import case)
9107 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9108 }
9109
9110 if (FAILED(rc))
9111 break;
9112
9113 /* back up mMediaData to let registeredInit() properly rollback on failure
9114 * (= limited accessibility) */
9115 setModified(IsModified_Storage);
9116 mMediaData.backup();
9117 mMediaData->mAttachments.push_back(pAttachment);
9118 }
9119
9120 return rc;
9121}
9122
9123/**
9124 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9125 *
9126 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9127 * @param aSnapshot where to return the found snapshot
9128 * @param aSetError true to set extended error info on failure
9129 */
9130HRESULT Machine::findSnapshotById(const Guid &aId,
9131 ComObjPtr<Snapshot> &aSnapshot,
9132 bool aSetError /* = false */)
9133{
9134 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9135
9136 if (!mData->mFirstSnapshot)
9137 {
9138 if (aSetError)
9139 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9140 return E_FAIL;
9141 }
9142
9143 if (aId.isZero())
9144 aSnapshot = mData->mFirstSnapshot;
9145 else
9146 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9147
9148 if (!aSnapshot)
9149 {
9150 if (aSetError)
9151 return setError(E_FAIL,
9152 tr("Could not find a snapshot with UUID {%s}"),
9153 aId.toString().c_str());
9154 return E_FAIL;
9155 }
9156
9157 return S_OK;
9158}
9159
9160/**
9161 * Returns the snapshot with the given name or fails of no such snapshot.
9162 *
9163 * @param aName snapshot name to find
9164 * @param aSnapshot where to return the found snapshot
9165 * @param aSetError true to set extended error info on failure
9166 */
9167HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9168 ComObjPtr<Snapshot> &aSnapshot,
9169 bool aSetError /* = false */)
9170{
9171 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9172
9173 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9174
9175 if (!mData->mFirstSnapshot)
9176 {
9177 if (aSetError)
9178 return setError(VBOX_E_OBJECT_NOT_FOUND,
9179 tr("This machine does not have any snapshots"));
9180 return VBOX_E_OBJECT_NOT_FOUND;
9181 }
9182
9183 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9184
9185 if (!aSnapshot)
9186 {
9187 if (aSetError)
9188 return setError(VBOX_E_OBJECT_NOT_FOUND,
9189 tr("Could not find a snapshot named '%s'"), strName.c_str());
9190 return VBOX_E_OBJECT_NOT_FOUND;
9191 }
9192
9193 return S_OK;
9194}
9195
9196/**
9197 * Returns a storage controller object with the given name.
9198 *
9199 * @param aName storage controller name to find
9200 * @param aStorageController where to return the found storage controller
9201 * @param aSetError true to set extended error info on failure
9202 */
9203HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9204 ComObjPtr<StorageController> &aStorageController,
9205 bool aSetError /* = false */)
9206{
9207 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9208
9209 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9210 it != mStorageControllers->end();
9211 ++it)
9212 {
9213 if ((*it)->getName() == aName)
9214 {
9215 aStorageController = (*it);
9216 return S_OK;
9217 }
9218 }
9219
9220 if (aSetError)
9221 return setError(VBOX_E_OBJECT_NOT_FOUND,
9222 tr("Could not find a storage controller named '%s'"),
9223 aName.c_str());
9224 return VBOX_E_OBJECT_NOT_FOUND;
9225}
9226
9227HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9228 MediaData::AttachmentList &atts)
9229{
9230 AutoCaller autoCaller(this);
9231 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9232
9233 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9234
9235 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9236 it != mMediaData->mAttachments.end();
9237 ++it)
9238 {
9239 const ComObjPtr<MediumAttachment> &pAtt = *it;
9240
9241 // should never happen, but deal with NULL pointers in the list.
9242 AssertStmt(!pAtt.isNull(), continue);
9243
9244 // getControllerName() needs caller+read lock
9245 AutoCaller autoAttCaller(pAtt);
9246 if (FAILED(autoAttCaller.rc()))
9247 {
9248 atts.clear();
9249 return autoAttCaller.rc();
9250 }
9251 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9252
9253 if (pAtt->getControllerName() == aName)
9254 atts.push_back(pAtt);
9255 }
9256
9257 return S_OK;
9258}
9259
9260/**
9261 * Helper for #saveSettings. Cares about renaming the settings directory and
9262 * file if the machine name was changed and about creating a new settings file
9263 * if this is a new machine.
9264 *
9265 * @note Must be never called directly but only from #saveSettings().
9266 */
9267HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9268{
9269 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9270
9271 HRESULT rc = S_OK;
9272
9273 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9274
9275 /// @todo need to handle primary group change, too
9276
9277 /* attempt to rename the settings file if machine name is changed */
9278 if ( mUserData->s.fNameSync
9279 && mUserData.isBackedUp()
9280 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9281 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9282 )
9283 {
9284 bool dirRenamed = false;
9285 bool fileRenamed = false;
9286
9287 Utf8Str configFile, newConfigFile;
9288 Utf8Str configFilePrev, newConfigFilePrev;
9289 Utf8Str configDir, newConfigDir;
9290
9291 do
9292 {
9293 int vrc = VINF_SUCCESS;
9294
9295 Utf8Str name = mUserData.backedUpData()->s.strName;
9296 Utf8Str newName = mUserData->s.strName;
9297 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9298 if (group == "/")
9299 group.setNull();
9300 Utf8Str newGroup = mUserData->s.llGroups.front();
9301 if (newGroup == "/")
9302 newGroup.setNull();
9303
9304 configFile = mData->m_strConfigFileFull;
9305
9306 /* first, rename the directory if it matches the group and machine name */
9307 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9308 group.c_str(), RTPATH_DELIMITER, name.c_str());
9309 /** @todo hack, make somehow use of ComposeMachineFilename */
9310 if (mUserData->s.fDirectoryIncludesUUID)
9311 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9312 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9313 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9314 /** @todo hack, make somehow use of ComposeMachineFilename */
9315 if (mUserData->s.fDirectoryIncludesUUID)
9316 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9317 configDir = configFile;
9318 configDir.stripFilename();
9319 newConfigDir = configDir;
9320 if ( configDir.length() >= groupPlusName.length()
9321 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9322 {
9323 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9324 Utf8Str newConfigBaseDir(newConfigDir);
9325 newConfigDir.append(newGroupPlusName);
9326 /* consistency: use \ if appropriate on the platform */
9327 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9328 /* new dir and old dir cannot be equal here because of 'if'
9329 * above and because name != newName */
9330 Assert(configDir != newConfigDir);
9331 if (!fSettingsFileIsNew)
9332 {
9333 /* perform real rename only if the machine is not new */
9334 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9335 if ( vrc == VERR_FILE_NOT_FOUND
9336 || vrc == VERR_PATH_NOT_FOUND)
9337 {
9338 /* create the parent directory, then retry renaming */
9339 Utf8Str parent(newConfigDir);
9340 parent.stripFilename();
9341 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9342 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9343 }
9344 if (RT_FAILURE(vrc))
9345 {
9346 rc = setError(E_FAIL,
9347 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9348 configDir.c_str(),
9349 newConfigDir.c_str(),
9350 vrc);
9351 break;
9352 }
9353 /* delete subdirectories which are no longer needed */
9354 Utf8Str dir(configDir);
9355 dir.stripFilename();
9356 while (dir != newConfigBaseDir && dir != ".")
9357 {
9358 vrc = RTDirRemove(dir.c_str());
9359 if (RT_FAILURE(vrc))
9360 break;
9361 dir.stripFilename();
9362 }
9363 dirRenamed = true;
9364 }
9365 }
9366
9367 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9368 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9369
9370 /* then try to rename the settings file itself */
9371 if (newConfigFile != configFile)
9372 {
9373 /* get the path to old settings file in renamed directory */
9374 configFile = Utf8StrFmt("%s%c%s",
9375 newConfigDir.c_str(),
9376 RTPATH_DELIMITER,
9377 RTPathFilename(configFile.c_str()));
9378 if (!fSettingsFileIsNew)
9379 {
9380 /* perform real rename only if the machine is not new */
9381 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9382 if (RT_FAILURE(vrc))
9383 {
9384 rc = setError(E_FAIL,
9385 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9386 configFile.c_str(),
9387 newConfigFile.c_str(),
9388 vrc);
9389 break;
9390 }
9391 fileRenamed = true;
9392 configFilePrev = configFile;
9393 configFilePrev += "-prev";
9394 newConfigFilePrev = newConfigFile;
9395 newConfigFilePrev += "-prev";
9396 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9397 }
9398 }
9399
9400 // update m_strConfigFileFull amd mConfigFile
9401 mData->m_strConfigFileFull = newConfigFile;
9402 // compute the relative path too
9403 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9404
9405 // store the old and new so that VirtualBox::saveSettings() can update
9406 // the media registry
9407 if ( mData->mRegistered
9408 && configDir != newConfigDir)
9409 {
9410 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9411
9412 if (pfNeedsGlobalSaveSettings)
9413 *pfNeedsGlobalSaveSettings = true;
9414 }
9415
9416 // in the saved state file path, replace the old directory with the new directory
9417 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9418 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9419
9420 // and do the same thing for the saved state file paths of all the online snapshots
9421 if (mData->mFirstSnapshot)
9422 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9423 newConfigDir.c_str());
9424 }
9425 while (0);
9426
9427 if (FAILED(rc))
9428 {
9429 /* silently try to rename everything back */
9430 if (fileRenamed)
9431 {
9432 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9433 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9434 }
9435 if (dirRenamed)
9436 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9437 }
9438
9439 if (FAILED(rc)) return rc;
9440 }
9441
9442 if (fSettingsFileIsNew)
9443 {
9444 /* create a virgin config file */
9445 int vrc = VINF_SUCCESS;
9446
9447 /* ensure the settings directory exists */
9448 Utf8Str path(mData->m_strConfigFileFull);
9449 path.stripFilename();
9450 if (!RTDirExists(path.c_str()))
9451 {
9452 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9453 if (RT_FAILURE(vrc))
9454 {
9455 return setError(E_FAIL,
9456 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9457 path.c_str(),
9458 vrc);
9459 }
9460 }
9461
9462 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9463 path = Utf8Str(mData->m_strConfigFileFull);
9464 RTFILE f = NIL_RTFILE;
9465 vrc = RTFileOpen(&f, path.c_str(),
9466 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9467 if (RT_FAILURE(vrc))
9468 return setError(E_FAIL,
9469 tr("Could not create the settings file '%s' (%Rrc)"),
9470 path.c_str(),
9471 vrc);
9472 RTFileClose(f);
9473 }
9474
9475 return rc;
9476}
9477
9478/**
9479 * Saves and commits machine data, user data and hardware data.
9480 *
9481 * Note that on failure, the data remains uncommitted.
9482 *
9483 * @a aFlags may combine the following flags:
9484 *
9485 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9486 * Used when saving settings after an operation that makes them 100%
9487 * correspond to the settings from the current snapshot.
9488 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9489 * #isReallyModified() returns false. This is necessary for cases when we
9490 * change machine data directly, not through the backup()/commit() mechanism.
9491 * - SaveS_Force: settings will be saved without doing a deep compare of the
9492 * settings structures. This is used when this is called because snapshots
9493 * have changed to avoid the overhead of the deep compare.
9494 *
9495 * @note Must be called from under this object's write lock. Locks children for
9496 * writing.
9497 *
9498 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9499 * initialized to false and that will be set to true by this function if
9500 * the caller must invoke VirtualBox::saveSettings() because the global
9501 * settings have changed. This will happen if a machine rename has been
9502 * saved and the global machine and media registries will therefore need
9503 * updating.
9504 */
9505HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9506 int aFlags /*= 0*/)
9507{
9508 LogFlowThisFuncEnter();
9509
9510 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9511
9512 /* make sure child objects are unable to modify the settings while we are
9513 * saving them */
9514 ensureNoStateDependencies();
9515
9516 AssertReturn(!isSnapshotMachine(),
9517 E_FAIL);
9518
9519 HRESULT rc = S_OK;
9520 bool fNeedsWrite = false;
9521
9522 /* First, prepare to save settings. It will care about renaming the
9523 * settings directory and file if the machine name was changed and about
9524 * creating a new settings file if this is a new machine. */
9525 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9526 if (FAILED(rc)) return rc;
9527
9528 // keep a pointer to the current settings structures
9529 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9530 settings::MachineConfigFile *pNewConfig = NULL;
9531
9532 try
9533 {
9534 // make a fresh one to have everyone write stuff into
9535 pNewConfig = new settings::MachineConfigFile(NULL);
9536 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9537
9538 // now go and copy all the settings data from COM to the settings structures
9539 // (this calles saveSettings() on all the COM objects in the machine)
9540 copyMachineDataToSettings(*pNewConfig);
9541
9542 if (aFlags & SaveS_ResetCurStateModified)
9543 {
9544 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9545 mData->mCurrentStateModified = FALSE;
9546 fNeedsWrite = true; // always, no need to compare
9547 }
9548 else if (aFlags & SaveS_Force)
9549 {
9550 fNeedsWrite = true; // always, no need to compare
9551 }
9552 else
9553 {
9554 if (!mData->mCurrentStateModified)
9555 {
9556 // do a deep compare of the settings that we just saved with the settings
9557 // previously stored in the config file; this invokes MachineConfigFile::operator==
9558 // which does a deep compare of all the settings, which is expensive but less expensive
9559 // than writing out XML in vain
9560 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9561
9562 // could still be modified if any settings changed
9563 mData->mCurrentStateModified = fAnySettingsChanged;
9564
9565 fNeedsWrite = fAnySettingsChanged;
9566 }
9567 else
9568 fNeedsWrite = true;
9569 }
9570
9571 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9572
9573 if (fNeedsWrite)
9574 // now spit it all out!
9575 pNewConfig->write(mData->m_strConfigFileFull);
9576
9577 mData->pMachineConfigFile = pNewConfig;
9578 delete pOldConfig;
9579 commit();
9580
9581 // after saving settings, we are no longer different from the XML on disk
9582 mData->flModifications = 0;
9583 }
9584 catch (HRESULT err)
9585 {
9586 // we assume that error info is set by the thrower
9587 rc = err;
9588
9589 // restore old config
9590 delete pNewConfig;
9591 mData->pMachineConfigFile = pOldConfig;
9592 }
9593 catch (...)
9594 {
9595 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9596 }
9597
9598 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9599 {
9600 /* Fire the data change event, even on failure (since we've already
9601 * committed all data). This is done only for SessionMachines because
9602 * mutable Machine instances are always not registered (i.e. private
9603 * to the client process that creates them) and thus don't need to
9604 * inform callbacks. */
9605 if (isSessionMachine())
9606 mParent->onMachineDataChange(mData->mUuid);
9607 }
9608
9609 LogFlowThisFunc(("rc=%08X\n", rc));
9610 LogFlowThisFuncLeave();
9611 return rc;
9612}
9613
9614/**
9615 * Implementation for saving the machine settings into the given
9616 * settings::MachineConfigFile instance. This copies machine extradata
9617 * from the previous machine config file in the instance data, if any.
9618 *
9619 * This gets called from two locations:
9620 *
9621 * -- Machine::saveSettings(), during the regular XML writing;
9622 *
9623 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9624 * exported to OVF and we write the VirtualBox proprietary XML
9625 * into a <vbox:Machine> tag.
9626 *
9627 * This routine fills all the fields in there, including snapshots, *except*
9628 * for the following:
9629 *
9630 * -- fCurrentStateModified. There is some special logic associated with that.
9631 *
9632 * The caller can then call MachineConfigFile::write() or do something else
9633 * with it.
9634 *
9635 * Caller must hold the machine lock!
9636 *
9637 * This throws XML errors and HRESULT, so the caller must have a catch block!
9638 */
9639void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
9640{
9641 // deep copy extradata
9642 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9643
9644 config.uuid = mData->mUuid;
9645
9646 // copy name, description, OS type, teleport, UTC etc.
9647 config.machineUserData = mUserData->s;
9648
9649 if ( mData->mMachineState == MachineState_Saved
9650 || mData->mMachineState == MachineState_Restoring
9651 // when deleting a snapshot we may or may not have a saved state in the current state,
9652 // so let's not assert here please
9653 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9654 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9655 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9656 && (!mSSData->strStateFilePath.isEmpty())
9657 )
9658 )
9659 {
9660 Assert(!mSSData->strStateFilePath.isEmpty());
9661 /* try to make the file name relative to the settings file dir */
9662 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9663 }
9664 else
9665 {
9666 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9667 config.strStateFile.setNull();
9668 }
9669
9670 if (mData->mCurrentSnapshot)
9671 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
9672 else
9673 config.uuidCurrentSnapshot.clear();
9674
9675 config.timeLastStateChange = mData->mLastStateChange;
9676 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9677 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9678
9679 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9680 if (FAILED(rc)) throw rc;
9681
9682 rc = saveStorageControllers(config.storageMachine);
9683 if (FAILED(rc)) throw rc;
9684
9685 // save machine's media registry if this is VirtualBox 4.0 or later
9686 if (config.canHaveOwnMediaRegistry())
9687 {
9688 // determine machine folder
9689 Utf8Str strMachineFolder = getSettingsFileFull();
9690 strMachineFolder.stripFilename();
9691 mParent->saveMediaRegistry(config.mediaRegistry,
9692 getId(), // only media with registry ID == machine UUID
9693 strMachineFolder);
9694 // this throws HRESULT
9695 }
9696
9697 // save snapshots
9698 rc = saveAllSnapshots(config);
9699 if (FAILED(rc)) throw rc;
9700}
9701
9702/**
9703 * Saves all snapshots of the machine into the given machine config file. Called
9704 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9705 * @param config
9706 * @return
9707 */
9708HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
9709{
9710 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9711
9712 HRESULT rc = S_OK;
9713
9714 try
9715 {
9716 config.llFirstSnapshot.clear();
9717
9718 if (mData->mFirstSnapshot)
9719 {
9720 settings::Snapshot snapNew;
9721 config.llFirstSnapshot.push_back(snapNew);
9722
9723 // get reference to the fresh copy of the snapshot on the list and
9724 // work on that copy directly to avoid excessive copying later
9725 settings::Snapshot &snap = config.llFirstSnapshot.front();
9726
9727 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
9728 if (FAILED(rc)) throw rc;
9729 }
9730
9731// if (mType == IsSessionMachine)
9732// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9733
9734 }
9735 catch (HRESULT err)
9736 {
9737 /* we assume that error info is set by the thrower */
9738 rc = err;
9739 }
9740 catch (...)
9741 {
9742 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9743 }
9744
9745 return rc;
9746}
9747
9748/**
9749 * Saves the VM hardware configuration. It is assumed that the
9750 * given node is empty.
9751 *
9752 * @param data Reference to the settings object for the hardware config.
9753 * @param pDbg Pointer to the settings object for the debugging config
9754 * which happens to live in mHWData.
9755 * @param pAutostart Pointer to the settings object for the autostart config
9756 * which happens to live in mHWData.
9757 */
9758HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9759 settings::Autostart *pAutostart)
9760{
9761 HRESULT rc = S_OK;
9762
9763 try
9764 {
9765 /* The hardware version attribute (optional).
9766 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9767 if ( mHWData->mHWVersion == "1"
9768 && mSSData->strStateFilePath.isEmpty()
9769 )
9770 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. */
9771
9772 data.strVersion = mHWData->mHWVersion;
9773 data.uuid = mHWData->mHardwareUUID;
9774
9775 // CPU
9776 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9777 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
9778 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9779 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9780 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9781 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9782 data.fPAE = !!mHWData->mPAEEnabled;
9783 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9784
9785 /* Standard and Extended CPUID leafs. */
9786 data.llCpuIdLeafs.clear();
9787 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
9788 {
9789 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9790 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9791 }
9792 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
9793 {
9794 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9795 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9796 }
9797
9798 data.cCPUs = mHWData->mCPUCount;
9799 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9800 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9801
9802 data.llCpus.clear();
9803 if (data.fCpuHotPlug)
9804 {
9805 for (unsigned idx = 0; idx < data.cCPUs; idx++)
9806 {
9807 if (mHWData->mCPUAttached[idx])
9808 {
9809 settings::Cpu cpu;
9810 cpu.ulId = idx;
9811 data.llCpus.push_back(cpu);
9812 }
9813 }
9814 }
9815
9816 // memory
9817 data.ulMemorySizeMB = mHWData->mMemorySize;
9818 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9819
9820 // firmware
9821 data.firmwareType = mHWData->mFirmwareType;
9822
9823 // HID
9824 data.pointingHIDType = mHWData->mPointingHIDType;
9825 data.keyboardHIDType = mHWData->mKeyboardHIDType;
9826
9827 // chipset
9828 data.chipsetType = mHWData->mChipsetType;
9829
9830 data.fEmulatedUSBWebcam = !!mHWData->mEmulatedUSBWebcamEnabled;
9831 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9832
9833 // HPET
9834 data.fHPETEnabled = !!mHWData->mHPETEnabled;
9835
9836 // boot order
9837 data.mapBootOrder.clear();
9838 for (size_t i = 0;
9839 i < RT_ELEMENTS(mHWData->mBootOrder);
9840 ++i)
9841 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9842
9843 // display
9844 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9845 data.cMonitors = mHWData->mMonitorCount;
9846 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9847 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9848 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
9849 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
9850 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
9851 data.strVideoCaptureFile = mHWData->mVideoCaptureFile;
9852
9853 /* VRDEServer settings (optional) */
9854 rc = mVRDEServer->saveSettings(data.vrdeSettings);
9855 if (FAILED(rc)) throw rc;
9856
9857 /* BIOS (required) */
9858 rc = mBIOSSettings->saveSettings(data.biosSettings);
9859 if (FAILED(rc)) throw rc;
9860
9861 /* USB Controller (required) */
9862 rc = mUSBController->saveSettings(data.usbController);
9863 if (FAILED(rc)) throw rc;
9864
9865 /* Network adapters (required) */
9866 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
9867 data.llNetworkAdapters.clear();
9868 /* Write out only the nominal number of network adapters for this
9869 * chipset type. Since Machine::commit() hasn't been called there
9870 * may be extra NIC settings in the vector. */
9871 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
9872 {
9873 settings::NetworkAdapter nic;
9874 nic.ulSlot = slot;
9875 /* paranoia check... must not be NULL, but must not crash either. */
9876 if (mNetworkAdapters[slot])
9877 {
9878 rc = mNetworkAdapters[slot]->saveSettings(nic);
9879 if (FAILED(rc)) throw rc;
9880
9881 data.llNetworkAdapters.push_back(nic);
9882 }
9883 }
9884
9885 /* Serial ports */
9886 data.llSerialPorts.clear();
9887 for (ULONG slot = 0;
9888 slot < RT_ELEMENTS(mSerialPorts);
9889 ++slot)
9890 {
9891 settings::SerialPort s;
9892 s.ulSlot = slot;
9893 rc = mSerialPorts[slot]->saveSettings(s);
9894 if (FAILED(rc)) return rc;
9895
9896 data.llSerialPorts.push_back(s);
9897 }
9898
9899 /* Parallel ports */
9900 data.llParallelPorts.clear();
9901 for (ULONG slot = 0;
9902 slot < RT_ELEMENTS(mParallelPorts);
9903 ++slot)
9904 {
9905 settings::ParallelPort p;
9906 p.ulSlot = slot;
9907 rc = mParallelPorts[slot]->saveSettings(p);
9908 if (FAILED(rc)) return rc;
9909
9910 data.llParallelPorts.push_back(p);
9911 }
9912
9913 /* Audio adapter */
9914 rc = mAudioAdapter->saveSettings(data.audioAdapter);
9915 if (FAILED(rc)) return rc;
9916
9917 /* Shared folders */
9918 data.llSharedFolders.clear();
9919 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
9920 it != mHWData->mSharedFolders.end();
9921 ++it)
9922 {
9923 SharedFolder *pSF = *it;
9924 AutoCaller sfCaller(pSF);
9925 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
9926 settings::SharedFolder sf;
9927 sf.strName = pSF->getName();
9928 sf.strHostPath = pSF->getHostPath();
9929 sf.fWritable = !!pSF->isWritable();
9930 sf.fAutoMount = !!pSF->isAutoMounted();
9931
9932 data.llSharedFolders.push_back(sf);
9933 }
9934
9935 // clipboard
9936 data.clipboardMode = mHWData->mClipboardMode;
9937
9938 // drag'n'drop
9939 data.dragAndDropMode = mHWData->mDragAndDropMode;
9940
9941 /* Guest */
9942 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
9943
9944 // IO settings
9945 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
9946 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
9947
9948 /* BandwidthControl (required) */
9949 rc = mBandwidthControl->saveSettings(data.ioSettings);
9950 if (FAILED(rc)) throw rc;
9951
9952 /* Host PCI devices */
9953 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
9954 it != mHWData->mPCIDeviceAssignments.end();
9955 ++it)
9956 {
9957 ComObjPtr<PCIDeviceAttachment> pda = *it;
9958 settings::HostPCIDeviceAttachment hpda;
9959
9960 rc = pda->saveSettings(hpda);
9961 if (FAILED(rc)) throw rc;
9962
9963 data.pciAttachments.push_back(hpda);
9964 }
9965
9966
9967 // guest properties
9968 data.llGuestProperties.clear();
9969#ifdef VBOX_WITH_GUEST_PROPS
9970 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
9971 it != mHWData->mGuestProperties.end();
9972 ++it)
9973 {
9974 HWData::GuestProperty property = it->second;
9975
9976 /* Remove transient guest properties at shutdown unless we
9977 * are saving state */
9978 if ( ( mData->mMachineState == MachineState_PoweredOff
9979 || mData->mMachineState == MachineState_Aborted
9980 || mData->mMachineState == MachineState_Teleported)
9981 && ( property.mFlags & guestProp::TRANSIENT
9982 || property.mFlags & guestProp::TRANSRESET))
9983 continue;
9984 settings::GuestProperty prop;
9985 prop.strName = it->first;
9986 prop.strValue = property.strValue;
9987 prop.timestamp = property.mTimestamp;
9988 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
9989 guestProp::writeFlags(property.mFlags, szFlags);
9990 prop.strFlags = szFlags;
9991
9992 data.llGuestProperties.push_back(prop);
9993 }
9994
9995 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
9996 /* I presume this doesn't require a backup(). */
9997 mData->mGuestPropertiesModified = FALSE;
9998#endif /* VBOX_WITH_GUEST_PROPS defined */
9999
10000 *pDbg = mHWData->mDebugging;
10001 *pAutostart = mHWData->mAutostart;
10002
10003 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10004 }
10005 catch(std::bad_alloc &)
10006 {
10007 return E_OUTOFMEMORY;
10008 }
10009
10010 AssertComRC(rc);
10011 return rc;
10012}
10013
10014/**
10015 * Saves the storage controller configuration.
10016 *
10017 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10018 */
10019HRESULT Machine::saveStorageControllers(settings::Storage &data)
10020{
10021 data.llStorageControllers.clear();
10022
10023 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10024 it != mStorageControllers->end();
10025 ++it)
10026 {
10027 HRESULT rc;
10028 ComObjPtr<StorageController> pCtl = *it;
10029
10030 settings::StorageController ctl;
10031 ctl.strName = pCtl->getName();
10032 ctl.controllerType = pCtl->getControllerType();
10033 ctl.storageBus = pCtl->getStorageBus();
10034 ctl.ulInstance = pCtl->getInstance();
10035 ctl.fBootable = pCtl->getBootable();
10036
10037 /* Save the port count. */
10038 ULONG portCount;
10039 rc = pCtl->COMGETTER(PortCount)(&portCount);
10040 ComAssertComRCRet(rc, rc);
10041 ctl.ulPortCount = portCount;
10042
10043 /* Save fUseHostIOCache */
10044 BOOL fUseHostIOCache;
10045 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10046 ComAssertComRCRet(rc, rc);
10047 ctl.fUseHostIOCache = !!fUseHostIOCache;
10048
10049 /* Save IDE emulation settings. */
10050 if (ctl.controllerType == StorageControllerType_IntelAhci)
10051 {
10052 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10053 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10054 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10055 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10056 )
10057 ComAssertComRCRet(rc, rc);
10058 }
10059
10060 /* save the devices now. */
10061 rc = saveStorageDevices(pCtl, ctl);
10062 ComAssertComRCRet(rc, rc);
10063
10064 data.llStorageControllers.push_back(ctl);
10065 }
10066
10067 return S_OK;
10068}
10069
10070/**
10071 * Saves the hard disk configuration.
10072 */
10073HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10074 settings::StorageController &data)
10075{
10076 MediaData::AttachmentList atts;
10077
10078 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
10079 if (FAILED(rc)) return rc;
10080
10081 data.llAttachedDevices.clear();
10082 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10083 it != atts.end();
10084 ++it)
10085 {
10086 settings::AttachedDevice dev;
10087
10088 MediumAttachment *pAttach = *it;
10089 Medium *pMedium = pAttach->getMedium();
10090
10091 dev.deviceType = pAttach->getType();
10092 dev.lPort = pAttach->getPort();
10093 dev.lDevice = pAttach->getDevice();
10094 if (pMedium)
10095 {
10096 if (pMedium->isHostDrive())
10097 dev.strHostDriveSrc = pMedium->getLocationFull();
10098 else
10099 dev.uuid = pMedium->getId();
10100 dev.fPassThrough = pAttach->getPassthrough();
10101 dev.fTempEject = pAttach->getTempEject();
10102 dev.fNonRotational = pAttach->getNonRotational();
10103 dev.fDiscard = pAttach->getDiscard();
10104 }
10105
10106 dev.strBwGroup = pAttach->getBandwidthGroup();
10107
10108 data.llAttachedDevices.push_back(dev);
10109 }
10110
10111 return S_OK;
10112}
10113
10114/**
10115 * Saves machine state settings as defined by aFlags
10116 * (SaveSTS_* values).
10117 *
10118 * @param aFlags Combination of SaveSTS_* flags.
10119 *
10120 * @note Locks objects for writing.
10121 */
10122HRESULT Machine::saveStateSettings(int aFlags)
10123{
10124 if (aFlags == 0)
10125 return S_OK;
10126
10127 AutoCaller autoCaller(this);
10128 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10129
10130 /* This object's write lock is also necessary to serialize file access
10131 * (prevent concurrent reads and writes) */
10132 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10133
10134 HRESULT rc = S_OK;
10135
10136 Assert(mData->pMachineConfigFile);
10137
10138 try
10139 {
10140 if (aFlags & SaveSTS_CurStateModified)
10141 mData->pMachineConfigFile->fCurrentStateModified = true;
10142
10143 if (aFlags & SaveSTS_StateFilePath)
10144 {
10145 if (!mSSData->strStateFilePath.isEmpty())
10146 /* try to make the file name relative to the settings file dir */
10147 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10148 else
10149 mData->pMachineConfigFile->strStateFile.setNull();
10150 }
10151
10152 if (aFlags & SaveSTS_StateTimeStamp)
10153 {
10154 Assert( mData->mMachineState != MachineState_Aborted
10155 || mSSData->strStateFilePath.isEmpty());
10156
10157 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10158
10159 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10160//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10161 }
10162
10163 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10164 }
10165 catch (...)
10166 {
10167 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10168 }
10169
10170 return rc;
10171}
10172
10173/**
10174 * Ensures that the given medium is added to a media registry. If this machine
10175 * was created with 4.0 or later, then the machine registry is used. Otherwise
10176 * the global VirtualBox media registry is used.
10177 *
10178 * Caller must NOT hold machine lock, media tree or any medium locks!
10179 *
10180 * @param pMedium
10181 */
10182void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10183{
10184 /* Paranoia checks: do not hold machine or media tree locks. */
10185 AssertReturnVoid(!isWriteLockOnCurrentThread());
10186 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10187
10188 ComObjPtr<Medium> pBase;
10189 {
10190 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10191 pBase = pMedium->getBase();
10192 }
10193
10194 /* Paranoia checks: do not hold medium locks. */
10195 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10196 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10197
10198 // decide which medium registry to use now that the medium is attached:
10199 Guid uuid;
10200 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10201 // machine XML is VirtualBox 4.0 or higher:
10202 uuid = getId(); // machine UUID
10203 else
10204 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10205
10206 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10207 mParent->markRegistryModified(uuid);
10208
10209 /* For more complex hard disk structures it can happen that the base
10210 * medium isn't yet associated with any medium registry. Do that now. */
10211 if (pMedium != pBase)
10212 {
10213 if (pBase->addRegistry(uuid, true /* fRecurse */))
10214 mParent->markRegistryModified(uuid);
10215 }
10216}
10217
10218/**
10219 * Creates differencing hard disks for all normal hard disks attached to this
10220 * machine and a new set of attachments to refer to created disks.
10221 *
10222 * Used when taking a snapshot or when deleting the current state. Gets called
10223 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10224 *
10225 * This method assumes that mMediaData contains the original hard disk attachments
10226 * it needs to create diffs for. On success, these attachments will be replaced
10227 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10228 * called to delete created diffs which will also rollback mMediaData and restore
10229 * whatever was backed up before calling this method.
10230 *
10231 * Attachments with non-normal hard disks are left as is.
10232 *
10233 * If @a aOnline is @c false then the original hard disks that require implicit
10234 * diffs will be locked for reading. Otherwise it is assumed that they are
10235 * already locked for writing (when the VM was started). Note that in the latter
10236 * case it is responsibility of the caller to lock the newly created diffs for
10237 * writing if this method succeeds.
10238 *
10239 * @param aProgress Progress object to run (must contain at least as
10240 * many operations left as the number of hard disks
10241 * attached).
10242 * @param aOnline Whether the VM was online prior to this operation.
10243 *
10244 * @note The progress object is not marked as completed, neither on success nor
10245 * on failure. This is a responsibility of the caller.
10246 *
10247 * @note Locks this object and the media tree for writing.
10248 */
10249HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10250 ULONG aWeight,
10251 bool aOnline)
10252{
10253 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10254
10255 AutoCaller autoCaller(this);
10256 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10257
10258 AutoMultiWriteLock2 alock(this->lockHandle(),
10259 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10260
10261 /* must be in a protective state because we release the lock below */
10262 AssertReturn( mData->mMachineState == MachineState_Saving
10263 || mData->mMachineState == MachineState_LiveSnapshotting
10264 || mData->mMachineState == MachineState_RestoringSnapshot
10265 || mData->mMachineState == MachineState_DeletingSnapshot
10266 , E_FAIL);
10267
10268 HRESULT rc = S_OK;
10269
10270 // use appropriate locked media map (online or offline)
10271 MediumLockListMap lockedMediaOffline;
10272 MediumLockListMap *lockedMediaMap;
10273 if (aOnline)
10274 lockedMediaMap = &mData->mSession.mLockedMedia;
10275 else
10276 lockedMediaMap = &lockedMediaOffline;
10277
10278 try
10279 {
10280 if (!aOnline)
10281 {
10282 /* lock all attached hard disks early to detect "in use"
10283 * situations before creating actual diffs */
10284 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10285 it != mMediaData->mAttachments.end();
10286 ++it)
10287 {
10288 MediumAttachment* pAtt = *it;
10289 if (pAtt->getType() == DeviceType_HardDisk)
10290 {
10291 Medium* pMedium = pAtt->getMedium();
10292 Assert(pMedium);
10293
10294 MediumLockList *pMediumLockList(new MediumLockList());
10295 alock.release();
10296 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10297 false /* fMediumLockWrite */,
10298 NULL,
10299 *pMediumLockList);
10300 alock.acquire();
10301 if (FAILED(rc))
10302 {
10303 delete pMediumLockList;
10304 throw rc;
10305 }
10306 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10307 if (FAILED(rc))
10308 {
10309 throw setError(rc,
10310 tr("Collecting locking information for all attached media failed"));
10311 }
10312 }
10313 }
10314
10315 /* Now lock all media. If this fails, nothing is locked. */
10316 alock.release();
10317 rc = lockedMediaMap->Lock();
10318 alock.acquire();
10319 if (FAILED(rc))
10320 {
10321 throw setError(rc,
10322 tr("Locking of attached media failed"));
10323 }
10324 }
10325
10326 /* remember the current list (note that we don't use backup() since
10327 * mMediaData may be already backed up) */
10328 MediaData::AttachmentList atts = mMediaData->mAttachments;
10329
10330 /* start from scratch */
10331 mMediaData->mAttachments.clear();
10332
10333 /* go through remembered attachments and create diffs for normal hard
10334 * disks and attach them */
10335 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10336 it != atts.end();
10337 ++it)
10338 {
10339 MediumAttachment* pAtt = *it;
10340
10341 DeviceType_T devType = pAtt->getType();
10342 Medium* pMedium = pAtt->getMedium();
10343
10344 if ( devType != DeviceType_HardDisk
10345 || pMedium == NULL
10346 || pMedium->getType() != MediumType_Normal)
10347 {
10348 /* copy the attachment as is */
10349
10350 /** @todo the progress object created in Console::TakeSnaphot
10351 * only expects operations for hard disks. Later other
10352 * device types need to show up in the progress as well. */
10353 if (devType == DeviceType_HardDisk)
10354 {
10355 if (pMedium == NULL)
10356 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10357 aWeight); // weight
10358 else
10359 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10360 pMedium->getBase()->getName().c_str()).raw(),
10361 aWeight); // weight
10362 }
10363
10364 mMediaData->mAttachments.push_back(pAtt);
10365 continue;
10366 }
10367
10368 /* need a diff */
10369 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10370 pMedium->getBase()->getName().c_str()).raw(),
10371 aWeight); // weight
10372
10373 Utf8Str strFullSnapshotFolder;
10374 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10375
10376 ComObjPtr<Medium> diff;
10377 diff.createObject();
10378 // store the diff in the same registry as the parent
10379 // (this cannot fail here because we can't create implicit diffs for
10380 // unregistered images)
10381 Guid uuidRegistryParent;
10382 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10383 Assert(fInRegistry); NOREF(fInRegistry);
10384 rc = diff->init(mParent,
10385 pMedium->getPreferredDiffFormat(),
10386 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10387 uuidRegistryParent);
10388 if (FAILED(rc)) throw rc;
10389
10390 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10391 * the push_back? Looks like we're going to release medium with the
10392 * wrong kind of lock (general issue with if we fail anywhere at all)
10393 * and an orphaned VDI in the snapshots folder. */
10394
10395 /* update the appropriate lock list */
10396 MediumLockList *pMediumLockList;
10397 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10398 AssertComRCThrowRC(rc);
10399 if (aOnline)
10400 {
10401 alock.release();
10402 /* The currently attached medium will be read-only, change
10403 * the lock type to read. */
10404 rc = pMediumLockList->Update(pMedium, false);
10405 alock.acquire();
10406 AssertComRCThrowRC(rc);
10407 }
10408
10409 /* release the locks before the potentially lengthy operation */
10410 alock.release();
10411 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10412 pMediumLockList,
10413 NULL /* aProgress */,
10414 true /* aWait */);
10415 alock.acquire();
10416 if (FAILED(rc)) throw rc;
10417
10418 rc = lockedMediaMap->Unlock();
10419 AssertComRCThrowRC(rc);
10420 alock.release();
10421 rc = pMediumLockList->Append(diff, true);
10422 alock.acquire();
10423 AssertComRCThrowRC(rc);
10424 alock.release();
10425 rc = lockedMediaMap->Lock();
10426 alock.acquire();
10427 AssertComRCThrowRC(rc);
10428
10429 rc = diff->addBackReference(mData->mUuid);
10430 AssertComRCThrowRC(rc);
10431
10432 /* add a new attachment */
10433 ComObjPtr<MediumAttachment> attachment;
10434 attachment.createObject();
10435 rc = attachment->init(this,
10436 diff,
10437 pAtt->getControllerName(),
10438 pAtt->getPort(),
10439 pAtt->getDevice(),
10440 DeviceType_HardDisk,
10441 true /* aImplicit */,
10442 false /* aPassthrough */,
10443 false /* aTempEject */,
10444 pAtt->getNonRotational(),
10445 pAtt->getDiscard(),
10446 pAtt->getBandwidthGroup());
10447 if (FAILED(rc)) throw rc;
10448
10449 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10450 AssertComRCThrowRC(rc);
10451 mMediaData->mAttachments.push_back(attachment);
10452 }
10453 }
10454 catch (HRESULT aRC) { rc = aRC; }
10455
10456 /* unlock all hard disks we locked when there is no VM */
10457 if (!aOnline)
10458 {
10459 ErrorInfoKeeper eik;
10460
10461 HRESULT rc1 = lockedMediaMap->Clear();
10462 AssertComRC(rc1);
10463 }
10464
10465 return rc;
10466}
10467
10468/**
10469 * Deletes implicit differencing hard disks created either by
10470 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10471 *
10472 * Note that to delete hard disks created by #AttachDevice() this method is
10473 * called from #fixupMedia() when the changes are rolled back.
10474 *
10475 * @note Locks this object and the media tree for writing.
10476 */
10477HRESULT Machine::deleteImplicitDiffs(bool aOnline)
10478{
10479 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10480
10481 AutoCaller autoCaller(this);
10482 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10483
10484 AutoMultiWriteLock2 alock(this->lockHandle(),
10485 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10486
10487 /* We absolutely must have backed up state. */
10488 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10489
10490 /* Check if there are any implicitly created diff images. */
10491 bool fImplicitDiffs = false;
10492 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10493 it != mMediaData->mAttachments.end();
10494 ++it)
10495 {
10496 const ComObjPtr<MediumAttachment> &pAtt = *it;
10497 if (pAtt->isImplicit())
10498 {
10499 fImplicitDiffs = true;
10500 break;
10501 }
10502 }
10503 /* If there is nothing to do, leave early. This saves lots of image locking
10504 * effort. It also avoids a MachineStateChanged event without real reason.
10505 * This is important e.g. when loading a VM config, because there should be
10506 * no events. Otherwise API clients can become thoroughly confused for
10507 * inaccessible VMs (the code for loading VM configs uses this method for
10508 * cleanup if the config makes no sense), as they take such events as an
10509 * indication that the VM is alive, and they would force the VM config to
10510 * be reread, leading to an endless loop. */
10511 if (!fImplicitDiffs)
10512 return S_OK;
10513
10514 HRESULT rc = S_OK;
10515 MachineState_T oldState = mData->mMachineState;
10516
10517 /* will release the lock before the potentially lengthy operation,
10518 * so protect with the special state (unless already protected) */
10519 if ( oldState != MachineState_Saving
10520 && oldState != MachineState_LiveSnapshotting
10521 && oldState != MachineState_RestoringSnapshot
10522 && oldState != MachineState_DeletingSnapshot
10523 && oldState != MachineState_DeletingSnapshotOnline
10524 && oldState != MachineState_DeletingSnapshotPaused
10525 )
10526 setMachineState(MachineState_SettingUp);
10527
10528 // use appropriate locked media map (online or offline)
10529 MediumLockListMap lockedMediaOffline;
10530 MediumLockListMap *lockedMediaMap;
10531 if (aOnline)
10532 lockedMediaMap = &mData->mSession.mLockedMedia;
10533 else
10534 lockedMediaMap = &lockedMediaOffline;
10535
10536 try
10537 {
10538 if (!aOnline)
10539 {
10540 /* lock all attached hard disks early to detect "in use"
10541 * situations before deleting actual diffs */
10542 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10543 it != mMediaData->mAttachments.end();
10544 ++it)
10545 {
10546 MediumAttachment* pAtt = *it;
10547 if (pAtt->getType() == DeviceType_HardDisk)
10548 {
10549 Medium* pMedium = pAtt->getMedium();
10550 Assert(pMedium);
10551
10552 MediumLockList *pMediumLockList(new MediumLockList());
10553 alock.release();
10554 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10555 false /* fMediumLockWrite */,
10556 NULL,
10557 *pMediumLockList);
10558 alock.acquire();
10559
10560 if (FAILED(rc))
10561 {
10562 delete pMediumLockList;
10563 throw rc;
10564 }
10565
10566 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10567 if (FAILED(rc))
10568 throw rc;
10569 }
10570 }
10571
10572 if (FAILED(rc))
10573 throw rc;
10574 } // end of offline
10575
10576 /* Lock lists are now up to date and include implicitly created media */
10577
10578 /* Go through remembered attachments and delete all implicitly created
10579 * diffs and fix up the attachment information */
10580 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10581 MediaData::AttachmentList implicitAtts;
10582 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10583 it != mMediaData->mAttachments.end();
10584 ++it)
10585 {
10586 ComObjPtr<MediumAttachment> pAtt = *it;
10587 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10588 if (pMedium.isNull())
10589 continue;
10590
10591 // Implicit attachments go on the list for deletion and back references are removed.
10592 if (pAtt->isImplicit())
10593 {
10594 /* Deassociate and mark for deletion */
10595 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
10596 rc = pMedium->removeBackReference(mData->mUuid);
10597 if (FAILED(rc))
10598 throw rc;
10599 implicitAtts.push_back(pAtt);
10600 continue;
10601 }
10602
10603 /* Was this medium attached before? */
10604 if (!findAttachment(oldAtts, pMedium))
10605 {
10606 /* no: de-associate */
10607 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
10608 rc = pMedium->removeBackReference(mData->mUuid);
10609 if (FAILED(rc))
10610 throw rc;
10611 continue;
10612 }
10613 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
10614 }
10615
10616 /* If there are implicit attachments to delete, throw away the lock
10617 * map contents (which will unlock all media) since the medium
10618 * attachments will be rolled back. Below we need to completely
10619 * recreate the lock map anyway since it is infinitely complex to
10620 * do this incrementally (would need reconstructing each attachment
10621 * change, which would be extremely hairy). */
10622 if (implicitAtts.size() != 0)
10623 {
10624 ErrorInfoKeeper eik;
10625
10626 HRESULT rc1 = lockedMediaMap->Clear();
10627 AssertComRC(rc1);
10628 }
10629
10630 /* rollback hard disk changes */
10631 mMediaData.rollback();
10632
10633 MultiResult mrc(S_OK);
10634
10635 // Delete unused implicit diffs.
10636 if (implicitAtts.size() != 0)
10637 {
10638 alock.release();
10639
10640 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
10641 it != implicitAtts.end();
10642 ++it)
10643 {
10644 // Remove medium associated with this attachment.
10645 ComObjPtr<MediumAttachment> pAtt = *it;
10646 Assert(pAtt);
10647 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
10648 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10649 Assert(pMedium);
10650
10651 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10652 // continue on delete failure, just collect error messages
10653 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
10654 mrc = rc;
10655 }
10656
10657 alock.acquire();
10658
10659 /* if there is a VM recreate media lock map as mentioned above,
10660 * otherwise it is a waste of time and we leave things unlocked */
10661 if (aOnline)
10662 {
10663 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10664 /* must never be NULL, but better safe than sorry */
10665 if (!pMachine.isNull())
10666 {
10667 alock.release();
10668 rc = mData->mSession.mMachine->lockMedia();
10669 alock.acquire();
10670 if (FAILED(rc))
10671 throw rc;
10672 }
10673 }
10674 }
10675 }
10676 catch (HRESULT aRC) {rc = aRC;}
10677
10678 if (mData->mMachineState == MachineState_SettingUp)
10679 setMachineState(oldState);
10680
10681 /* unlock all hard disks we locked when there is no VM */
10682 if (!aOnline)
10683 {
10684 ErrorInfoKeeper eik;
10685
10686 HRESULT rc1 = lockedMediaMap->Clear();
10687 AssertComRC(rc1);
10688 }
10689
10690 return rc;
10691}
10692
10693
10694/**
10695 * Looks through the given list of media attachments for one with the given parameters
10696 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10697 * can be searched as well if needed.
10698 *
10699 * @param list
10700 * @param aControllerName
10701 * @param aControllerPort
10702 * @param aDevice
10703 * @return
10704 */
10705MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10706 IN_BSTR aControllerName,
10707 LONG aControllerPort,
10708 LONG aDevice)
10709{
10710 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10711 it != ll.end();
10712 ++it)
10713 {
10714 MediumAttachment *pAttach = *it;
10715 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
10716 return pAttach;
10717 }
10718
10719 return NULL;
10720}
10721
10722/**
10723 * Looks through the given list of media attachments for one with the given parameters
10724 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10725 * can be searched as well if needed.
10726 *
10727 * @param list
10728 * @param aControllerName
10729 * @param aControllerPort
10730 * @param aDevice
10731 * @return
10732 */
10733MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10734 ComObjPtr<Medium> pMedium)
10735{
10736 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10737 it != ll.end();
10738 ++it)
10739 {
10740 MediumAttachment *pAttach = *it;
10741 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10742 if (pMediumThis == pMedium)
10743 return pAttach;
10744 }
10745
10746 return NULL;
10747}
10748
10749/**
10750 * Looks through the given list of media attachments for one with the given parameters
10751 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10752 * can be searched as well if needed.
10753 *
10754 * @param list
10755 * @param aControllerName
10756 * @param aControllerPort
10757 * @param aDevice
10758 * @return
10759 */
10760MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10761 Guid &id)
10762{
10763 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10764 it != ll.end();
10765 ++it)
10766 {
10767 MediumAttachment *pAttach = *it;
10768 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10769 if (pMediumThis->getId() == id)
10770 return pAttach;
10771 }
10772
10773 return NULL;
10774}
10775
10776/**
10777 * Main implementation for Machine::DetachDevice. This also gets called
10778 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10779 *
10780 * @param pAttach Medium attachment to detach.
10781 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10782 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
10783 * @return
10784 */
10785HRESULT Machine::detachDevice(MediumAttachment *pAttach,
10786 AutoWriteLock &writeLock,
10787 Snapshot *pSnapshot)
10788{
10789 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
10790 DeviceType_T mediumType = pAttach->getType();
10791
10792 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
10793
10794 if (pAttach->isImplicit())
10795 {
10796 /* attempt to implicitly delete the implicitly created diff */
10797
10798 /// @todo move the implicit flag from MediumAttachment to Medium
10799 /// and forbid any hard disk operation when it is implicit. Or maybe
10800 /// a special media state for it to make it even more simple.
10801
10802 Assert(mMediaData.isBackedUp());
10803
10804 /* will release the lock before the potentially lengthy operation, so
10805 * protect with the special state */
10806 MachineState_T oldState = mData->mMachineState;
10807 setMachineState(MachineState_SettingUp);
10808
10809 writeLock.release();
10810
10811 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
10812 true /*aWait*/);
10813
10814 writeLock.acquire();
10815
10816 setMachineState(oldState);
10817
10818 if (FAILED(rc)) return rc;
10819 }
10820
10821 setModified(IsModified_Storage);
10822 mMediaData.backup();
10823 mMediaData->mAttachments.remove(pAttach);
10824
10825 if (!oldmedium.isNull())
10826 {
10827 // if this is from a snapshot, do not defer detachment to commitMedia()
10828 if (pSnapshot)
10829 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
10830 // else if non-hard disk media, do not defer detachment to commitMedia() either
10831 else if (mediumType != DeviceType_HardDisk)
10832 oldmedium->removeBackReference(mData->mUuid);
10833 }
10834
10835 return S_OK;
10836}
10837
10838/**
10839 * Goes thru all media of the given list and
10840 *
10841 * 1) calls detachDevice() on each of them for this machine and
10842 * 2) adds all Medium objects found in the process to the given list,
10843 * depending on cleanupMode.
10844 *
10845 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10846 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10847 * media to the list.
10848 *
10849 * This gets called from Machine::Unregister, both for the actual Machine and
10850 * the SnapshotMachine objects that might be found in the snapshots.
10851 *
10852 * Requires caller and locking. The machine lock must be passed in because it
10853 * will be passed on to detachDevice which needs it for temporary unlocking.
10854 *
10855 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
10856 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
10857 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
10858 * otherwise no media get added.
10859 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
10860 * @return
10861 */
10862HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
10863 Snapshot *pSnapshot,
10864 CleanupMode_T cleanupMode,
10865 MediaList &llMedia)
10866{
10867 Assert(isWriteLockOnCurrentThread());
10868
10869 HRESULT rc;
10870
10871 // make a temporary list because detachDevice invalidates iterators into
10872 // mMediaData->mAttachments
10873 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
10874
10875 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
10876 it != llAttachments2.end();
10877 ++it)
10878 {
10879 ComObjPtr<MediumAttachment> &pAttach = *it;
10880 ComObjPtr<Medium> pMedium = pAttach->getMedium();
10881
10882 if (!pMedium.isNull())
10883 {
10884 AutoCaller mac(pMedium);
10885 if (FAILED(mac.rc())) return mac.rc();
10886 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
10887 DeviceType_T devType = pMedium->getDeviceType();
10888 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
10889 && devType == DeviceType_HardDisk)
10890 || (cleanupMode == CleanupMode_Full)
10891 )
10892 {
10893 llMedia.push_back(pMedium);
10894 ComObjPtr<Medium> pParent = pMedium->getParent();
10895 /*
10896 * Search for medias which are not attached to any machine, but
10897 * in the chain to an attached disk. Mediums are only consided
10898 * if they are:
10899 * - have only one child
10900 * - no references to any machines
10901 * - are of normal medium type
10902 */
10903 while (!pParent.isNull())
10904 {
10905 AutoCaller mac1(pParent);
10906 if (FAILED(mac1.rc())) return mac1.rc();
10907 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
10908 if (pParent->getChildren().size() == 1)
10909 {
10910 if ( pParent->getMachineBackRefCount() == 0
10911 && pParent->getType() == MediumType_Normal
10912 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
10913 llMedia.push_back(pParent);
10914 }
10915 else
10916 break;
10917 pParent = pParent->getParent();
10918 }
10919 }
10920 }
10921
10922 // real machine: then we need to use the proper method
10923 rc = detachDevice(pAttach, writeLock, pSnapshot);
10924
10925 if (FAILED(rc))
10926 return rc;
10927 }
10928
10929 return S_OK;
10930}
10931
10932/**
10933 * Perform deferred hard disk detachments.
10934 *
10935 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10936 * backed up).
10937 *
10938 * If @a aOnline is @c true then this method will also unlock the old hard disks
10939 * for which the new implicit diffs were created and will lock these new diffs for
10940 * writing.
10941 *
10942 * @param aOnline Whether the VM was online prior to this operation.
10943 *
10944 * @note Locks this object for writing!
10945 */
10946void Machine::commitMedia(bool aOnline /*= false*/)
10947{
10948 AutoCaller autoCaller(this);
10949 AssertComRCReturnVoid(autoCaller.rc());
10950
10951 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10952
10953 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
10954
10955 HRESULT rc = S_OK;
10956
10957 /* no attach/detach operations -- nothing to do */
10958 if (!mMediaData.isBackedUp())
10959 return;
10960
10961 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10962 bool fMediaNeedsLocking = false;
10963
10964 /* enumerate new attachments */
10965 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10966 it != mMediaData->mAttachments.end();
10967 ++it)
10968 {
10969 MediumAttachment *pAttach = *it;
10970
10971 pAttach->commit();
10972
10973 Medium* pMedium = pAttach->getMedium();
10974 bool fImplicit = pAttach->isImplicit();
10975
10976 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
10977 (pMedium) ? pMedium->getName().c_str() : "NULL",
10978 fImplicit));
10979
10980 /** @todo convert all this Machine-based voodoo to MediumAttachment
10981 * based commit logic. */
10982 if (fImplicit)
10983 {
10984 /* convert implicit attachment to normal */
10985 pAttach->setImplicit(false);
10986
10987 if ( aOnline
10988 && pMedium
10989 && pAttach->getType() == DeviceType_HardDisk
10990 )
10991 {
10992 ComObjPtr<Medium> parent = pMedium->getParent();
10993 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
10994
10995 /* update the appropriate lock list */
10996 MediumLockList *pMediumLockList;
10997 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10998 AssertComRC(rc);
10999 if (pMediumLockList)
11000 {
11001 /* unlock if there's a need to change the locking */
11002 if (!fMediaNeedsLocking)
11003 {
11004 rc = mData->mSession.mLockedMedia.Unlock();
11005 AssertComRC(rc);
11006 fMediaNeedsLocking = true;
11007 }
11008 rc = pMediumLockList->Update(parent, false);
11009 AssertComRC(rc);
11010 rc = pMediumLockList->Append(pMedium, true);
11011 AssertComRC(rc);
11012 }
11013 }
11014
11015 continue;
11016 }
11017
11018 if (pMedium)
11019 {
11020 /* was this medium attached before? */
11021 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11022 oldIt != oldAtts.end();
11023 ++oldIt)
11024 {
11025 MediumAttachment *pOldAttach = *oldIt;
11026 if (pOldAttach->getMedium() == pMedium)
11027 {
11028 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
11029
11030 /* yes: remove from old to avoid de-association */
11031 oldAtts.erase(oldIt);
11032 break;
11033 }
11034 }
11035 }
11036 }
11037
11038 /* enumerate remaining old attachments and de-associate from the
11039 * current machine state */
11040 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11041 it != oldAtts.end();
11042 ++it)
11043 {
11044 MediumAttachment *pAttach = *it;
11045 Medium* pMedium = pAttach->getMedium();
11046
11047 /* Detach only hard disks, since DVD/floppy media is detached
11048 * instantly in MountMedium. */
11049 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
11050 {
11051 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
11052
11053 /* now de-associate from the current machine state */
11054 rc = pMedium->removeBackReference(mData->mUuid);
11055 AssertComRC(rc);
11056
11057 if (aOnline)
11058 {
11059 /* unlock since medium is not used anymore */
11060 MediumLockList *pMediumLockList;
11061 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11062 AssertComRC(rc);
11063 if (pMediumLockList)
11064 {
11065 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11066 AssertComRC(rc);
11067 }
11068 }
11069 }
11070 }
11071
11072 /* take media locks again so that the locking state is consistent */
11073 if (fMediaNeedsLocking)
11074 {
11075 Assert(aOnline);
11076 rc = mData->mSession.mLockedMedia.Lock();
11077 AssertComRC(rc);
11078 }
11079
11080 /* commit the hard disk changes */
11081 mMediaData.commit();
11082
11083 if (isSessionMachine())
11084 {
11085 /*
11086 * Update the parent machine to point to the new owner.
11087 * This is necessary because the stored parent will point to the
11088 * session machine otherwise and cause crashes or errors later
11089 * when the session machine gets invalid.
11090 */
11091 /** @todo Change the MediumAttachment class to behave like any other
11092 * class in this regard by creating peer MediumAttachment
11093 * objects for session machines and share the data with the peer
11094 * machine.
11095 */
11096 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11097 it != mMediaData->mAttachments.end();
11098 ++it)
11099 {
11100 (*it)->updateParentMachine(mPeer);
11101 }
11102
11103 /* attach new data to the primary machine and reshare it */
11104 mPeer->mMediaData.attach(mMediaData);
11105 }
11106
11107 return;
11108}
11109
11110/**
11111 * Perform deferred deletion of implicitly created diffs.
11112 *
11113 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11114 * backed up).
11115 *
11116 * @note Locks this object for writing!
11117 */
11118void Machine::rollbackMedia()
11119{
11120 AutoCaller autoCaller(this);
11121 AssertComRCReturnVoid(autoCaller.rc());
11122
11123 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11124 LogFlowThisFunc(("Entering rollbackMedia\n"));
11125
11126 HRESULT rc = S_OK;
11127
11128 /* no attach/detach operations -- nothing to do */
11129 if (!mMediaData.isBackedUp())
11130 return;
11131
11132 /* enumerate new attachments */
11133 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11134 it != mMediaData->mAttachments.end();
11135 ++it)
11136 {
11137 MediumAttachment *pAttach = *it;
11138 /* Fix up the backrefs for DVD/floppy media. */
11139 if (pAttach->getType() != DeviceType_HardDisk)
11140 {
11141 Medium* pMedium = pAttach->getMedium();
11142 if (pMedium)
11143 {
11144 rc = pMedium->removeBackReference(mData->mUuid);
11145 AssertComRC(rc);
11146 }
11147 }
11148
11149 (*it)->rollback();
11150
11151 pAttach = *it;
11152 /* Fix up the backrefs for DVD/floppy media. */
11153 if (pAttach->getType() != DeviceType_HardDisk)
11154 {
11155 Medium* pMedium = pAttach->getMedium();
11156 if (pMedium)
11157 {
11158 rc = pMedium->addBackReference(mData->mUuid);
11159 AssertComRC(rc);
11160 }
11161 }
11162 }
11163
11164 /** @todo convert all this Machine-based voodoo to MediumAttachment
11165 * based rollback logic. */
11166 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11167
11168 return;
11169}
11170
11171/**
11172 * Returns true if the settings file is located in the directory named exactly
11173 * as the machine; this means, among other things, that the machine directory
11174 * should be auto-renamed.
11175 *
11176 * @param aSettingsDir if not NULL, the full machine settings file directory
11177 * name will be assigned there.
11178 *
11179 * @note Doesn't lock anything.
11180 * @note Not thread safe (must be called from this object's lock).
11181 */
11182bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11183{
11184 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11185 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11186 if (aSettingsDir)
11187 *aSettingsDir = strMachineDirName;
11188 strMachineDirName.stripPath(); // vmname
11189 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11190 strConfigFileOnly.stripPath() // vmname.vbox
11191 .stripExt(); // vmname
11192 /** @todo hack, make somehow use of ComposeMachineFilename */
11193 if (mUserData->s.fDirectoryIncludesUUID)
11194 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11195
11196 AssertReturn(!strMachineDirName.isEmpty(), false);
11197 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11198
11199 return strMachineDirName == strConfigFileOnly;
11200}
11201
11202/**
11203 * Discards all changes to machine settings.
11204 *
11205 * @param aNotify Whether to notify the direct session about changes or not.
11206 *
11207 * @note Locks objects for writing!
11208 */
11209void Machine::rollback(bool aNotify)
11210{
11211 AutoCaller autoCaller(this);
11212 AssertComRCReturn(autoCaller.rc(), (void)0);
11213
11214 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11215
11216 if (!mStorageControllers.isNull())
11217 {
11218 if (mStorageControllers.isBackedUp())
11219 {
11220 /* unitialize all new devices (absent in the backed up list). */
11221 StorageControllerList::const_iterator it = mStorageControllers->begin();
11222 StorageControllerList *backedList = mStorageControllers.backedUpData();
11223 while (it != mStorageControllers->end())
11224 {
11225 if ( std::find(backedList->begin(), backedList->end(), *it)
11226 == backedList->end()
11227 )
11228 {
11229 (*it)->uninit();
11230 }
11231 ++it;
11232 }
11233
11234 /* restore the list */
11235 mStorageControllers.rollback();
11236 }
11237
11238 /* rollback any changes to devices after restoring the list */
11239 if (mData->flModifications & IsModified_Storage)
11240 {
11241 StorageControllerList::const_iterator it = mStorageControllers->begin();
11242 while (it != mStorageControllers->end())
11243 {
11244 (*it)->rollback();
11245 ++it;
11246 }
11247 }
11248 }
11249
11250 mUserData.rollback();
11251
11252 mHWData.rollback();
11253
11254 if (mData->flModifications & IsModified_Storage)
11255 rollbackMedia();
11256
11257 if (mBIOSSettings)
11258 mBIOSSettings->rollback();
11259
11260 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11261 mVRDEServer->rollback();
11262
11263 if (mAudioAdapter)
11264 mAudioAdapter->rollback();
11265
11266 if (mUSBController && (mData->flModifications & IsModified_USB))
11267 mUSBController->rollback();
11268
11269 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11270 mBandwidthControl->rollback();
11271
11272 if (!mHWData.isNull())
11273 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11274 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11275 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11276 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11277
11278 if (mData->flModifications & IsModified_NetworkAdapters)
11279 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11280 if ( mNetworkAdapters[slot]
11281 && mNetworkAdapters[slot]->isModified())
11282 {
11283 mNetworkAdapters[slot]->rollback();
11284 networkAdapters[slot] = mNetworkAdapters[slot];
11285 }
11286
11287 if (mData->flModifications & IsModified_SerialPorts)
11288 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11289 if ( mSerialPorts[slot]
11290 && mSerialPorts[slot]->isModified())
11291 {
11292 mSerialPorts[slot]->rollback();
11293 serialPorts[slot] = mSerialPorts[slot];
11294 }
11295
11296 if (mData->flModifications & IsModified_ParallelPorts)
11297 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11298 if ( mParallelPorts[slot]
11299 && mParallelPorts[slot]->isModified())
11300 {
11301 mParallelPorts[slot]->rollback();
11302 parallelPorts[slot] = mParallelPorts[slot];
11303 }
11304
11305 if (aNotify)
11306 {
11307 /* inform the direct session about changes */
11308
11309 ComObjPtr<Machine> that = this;
11310 uint32_t flModifications = mData->flModifications;
11311 alock.release();
11312
11313 if (flModifications & IsModified_SharedFolders)
11314 that->onSharedFolderChange();
11315
11316 if (flModifications & IsModified_VRDEServer)
11317 that->onVRDEServerChange(/* aRestart */ TRUE);
11318 if (flModifications & IsModified_USB)
11319 that->onUSBControllerChange();
11320
11321 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
11322 if (networkAdapters[slot])
11323 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
11324 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
11325 if (serialPorts[slot])
11326 that->onSerialPortChange(serialPorts[slot]);
11327 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
11328 if (parallelPorts[slot])
11329 that->onParallelPortChange(parallelPorts[slot]);
11330
11331 if (flModifications & IsModified_Storage)
11332 that->onStorageControllerChange();
11333
11334#if 0
11335 if (flModifications & IsModified_BandwidthControl)
11336 that->onBandwidthControlChange();
11337#endif
11338 }
11339}
11340
11341/**
11342 * Commits all the changes to machine settings.
11343 *
11344 * Note that this operation is supposed to never fail.
11345 *
11346 * @note Locks this object and children for writing.
11347 */
11348void Machine::commit()
11349{
11350 AutoCaller autoCaller(this);
11351 AssertComRCReturnVoid(autoCaller.rc());
11352
11353 AutoCaller peerCaller(mPeer);
11354 AssertComRCReturnVoid(peerCaller.rc());
11355
11356 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11357
11358 /*
11359 * use safe commit to ensure Snapshot machines (that share mUserData)
11360 * will still refer to a valid memory location
11361 */
11362 mUserData.commitCopy();
11363
11364 mHWData.commit();
11365
11366 if (mMediaData.isBackedUp())
11367 commitMedia();
11368
11369 mBIOSSettings->commit();
11370 mVRDEServer->commit();
11371 mAudioAdapter->commit();
11372 mUSBController->commit();
11373 mBandwidthControl->commit();
11374
11375 /* Since mNetworkAdapters is a list which might have been changed (resized)
11376 * without using the Backupable<> template we need to handle the copying
11377 * of the list entries manually, including the creation of peers for the
11378 * new objects. */
11379 bool commitNetworkAdapters = false;
11380 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11381 if (mPeer)
11382 {
11383 /* commit everything, even the ones which will go away */
11384 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11385 mNetworkAdapters[slot]->commit();
11386 /* copy over the new entries, creating a peer and uninit the original */
11387 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11388 for (size_t slot = 0; slot < newSize; slot++)
11389 {
11390 /* look if this adapter has a peer device */
11391 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
11392 if (!peer)
11393 {
11394 /* no peer means the adapter is a newly created one;
11395 * create a peer owning data this data share it with */
11396 peer.createObject();
11397 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11398 }
11399 mPeer->mNetworkAdapters[slot] = peer;
11400 }
11401 /* uninit any no longer needed network adapters */
11402 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
11403 mNetworkAdapters[slot]->uninit();
11404 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
11405 {
11406 if (mPeer->mNetworkAdapters[slot])
11407 mPeer->mNetworkAdapters[slot]->uninit();
11408 }
11409 /* Keep the original network adapter count until this point, so that
11410 * discarding a chipset type change will not lose settings. */
11411 mNetworkAdapters.resize(newSize);
11412 mPeer->mNetworkAdapters.resize(newSize);
11413 }
11414 else
11415 {
11416 /* we have no peer (our parent is the newly created machine);
11417 * just commit changes to the network adapters */
11418 commitNetworkAdapters = true;
11419 }
11420 if (commitNetworkAdapters)
11421 {
11422 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11423 mNetworkAdapters[slot]->commit();
11424 }
11425
11426 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11427 mSerialPorts[slot]->commit();
11428 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11429 mParallelPorts[slot]->commit();
11430
11431 bool commitStorageControllers = false;
11432
11433 if (mStorageControllers.isBackedUp())
11434 {
11435 mStorageControllers.commit();
11436
11437 if (mPeer)
11438 {
11439 /* Commit all changes to new controllers (this will reshare data with
11440 * peers for those who have peers) */
11441 StorageControllerList *newList = new StorageControllerList();
11442 StorageControllerList::const_iterator it = mStorageControllers->begin();
11443 while (it != mStorageControllers->end())
11444 {
11445 (*it)->commit();
11446
11447 /* look if this controller has a peer device */
11448 ComObjPtr<StorageController> peer = (*it)->getPeer();
11449 if (!peer)
11450 {
11451 /* no peer means the device is a newly created one;
11452 * create a peer owning data this device share it with */
11453 peer.createObject();
11454 peer->init(mPeer, *it, true /* aReshare */);
11455 }
11456 else
11457 {
11458 /* remove peer from the old list */
11459 mPeer->mStorageControllers->remove(peer);
11460 }
11461 /* and add it to the new list */
11462 newList->push_back(peer);
11463
11464 ++it;
11465 }
11466
11467 /* uninit old peer's controllers that are left */
11468 it = mPeer->mStorageControllers->begin();
11469 while (it != mPeer->mStorageControllers->end())
11470 {
11471 (*it)->uninit();
11472 ++it;
11473 }
11474
11475 /* attach new list of controllers to our peer */
11476 mPeer->mStorageControllers.attach(newList);
11477 }
11478 else
11479 {
11480 /* we have no peer (our parent is the newly created machine);
11481 * just commit changes to devices */
11482 commitStorageControllers = true;
11483 }
11484 }
11485 else
11486 {
11487 /* the list of controllers itself is not changed,
11488 * just commit changes to controllers themselves */
11489 commitStorageControllers = true;
11490 }
11491
11492 if (commitStorageControllers)
11493 {
11494 StorageControllerList::const_iterator it = mStorageControllers->begin();
11495 while (it != mStorageControllers->end())
11496 {
11497 (*it)->commit();
11498 ++it;
11499 }
11500 }
11501
11502 if (isSessionMachine())
11503 {
11504 /* attach new data to the primary machine and reshare it */
11505 mPeer->mUserData.attach(mUserData);
11506 mPeer->mHWData.attach(mHWData);
11507 /* mMediaData is reshared by fixupMedia */
11508 // mPeer->mMediaData.attach(mMediaData);
11509 Assert(mPeer->mMediaData.data() == mMediaData.data());
11510 }
11511}
11512
11513/**
11514 * Copies all the hardware data from the given machine.
11515 *
11516 * Currently, only called when the VM is being restored from a snapshot. In
11517 * particular, this implies that the VM is not running during this method's
11518 * call.
11519 *
11520 * @note This method must be called from under this object's lock.
11521 *
11522 * @note This method doesn't call #commit(), so all data remains backed up and
11523 * unsaved.
11524 */
11525void Machine::copyFrom(Machine *aThat)
11526{
11527 AssertReturnVoid(!isSnapshotMachine());
11528 AssertReturnVoid(aThat->isSnapshotMachine());
11529
11530 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11531
11532 mHWData.assignCopy(aThat->mHWData);
11533
11534 // create copies of all shared folders (mHWData after attaching a copy
11535 // contains just references to original objects)
11536 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11537 it != mHWData->mSharedFolders.end();
11538 ++it)
11539 {
11540 ComObjPtr<SharedFolder> folder;
11541 folder.createObject();
11542 HRESULT rc = folder->initCopy(getMachine(), *it);
11543 AssertComRC(rc);
11544 *it = folder;
11545 }
11546
11547 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
11548 mVRDEServer->copyFrom(aThat->mVRDEServer);
11549 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
11550 mUSBController->copyFrom(aThat->mUSBController);
11551 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
11552
11553 /* create private copies of all controllers */
11554 mStorageControllers.backup();
11555 mStorageControllers->clear();
11556 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11557 it != aThat->mStorageControllers->end();
11558 ++it)
11559 {
11560 ComObjPtr<StorageController> ctrl;
11561 ctrl.createObject();
11562 ctrl->initCopy(this, *it);
11563 mStorageControllers->push_back(ctrl);
11564 }
11565
11566 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11567 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11568 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
11569 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11570 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
11571 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11572 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
11573}
11574
11575/**
11576 * Returns whether the given storage controller is hotplug capable.
11577 *
11578 * @returns true if the controller supports hotplugging
11579 * false otherwise.
11580 * @param enmCtrlType The controller type to check for.
11581 */
11582bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11583{
11584 switch (enmCtrlType)
11585 {
11586 case StorageControllerType_IntelAhci:
11587 return true;
11588 case StorageControllerType_LsiLogic:
11589 case StorageControllerType_LsiLogicSas:
11590 case StorageControllerType_BusLogic:
11591 case StorageControllerType_PIIX3:
11592 case StorageControllerType_PIIX4:
11593 case StorageControllerType_ICH6:
11594 case StorageControllerType_I82078:
11595 default:
11596 return false;
11597 }
11598}
11599
11600#ifdef VBOX_WITH_RESOURCE_USAGE_API
11601
11602void Machine::getDiskList(MediaList &list)
11603{
11604 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11605 it != mMediaData->mAttachments.end();
11606 ++it)
11607 {
11608 MediumAttachment* pAttach = *it;
11609 /* just in case */
11610 AssertStmt(pAttach, continue);
11611
11612 AutoCaller localAutoCallerA(pAttach);
11613 if (FAILED(localAutoCallerA.rc())) continue;
11614
11615 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11616
11617 if (pAttach->getType() == DeviceType_HardDisk)
11618 list.push_back(pAttach->getMedium());
11619 }
11620}
11621
11622void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11623{
11624 AssertReturnVoid(isWriteLockOnCurrentThread());
11625 AssertPtrReturnVoid(aCollector);
11626
11627 pm::CollectorHAL *hal = aCollector->getHAL();
11628 /* Create sub metrics */
11629 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11630 "Percentage of processor time spent in user mode by the VM process.");
11631 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11632 "Percentage of processor time spent in kernel mode by the VM process.");
11633 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11634 "Size of resident portion of VM process in memory.");
11635 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11636 "Actual size of all VM disks combined.");
11637 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11638 "Network receive rate.");
11639 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11640 "Network transmit rate.");
11641 /* Create and register base metrics */
11642 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11643 cpuLoadUser, cpuLoadKernel);
11644 aCollector->registerBaseMetric(cpuLoad);
11645 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11646 ramUsageUsed);
11647 aCollector->registerBaseMetric(ramUsage);
11648 MediaList disks;
11649 getDiskList(disks);
11650 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11651 diskUsageUsed);
11652 aCollector->registerBaseMetric(diskUsage);
11653
11654 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11655 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11656 new pm::AggregateAvg()));
11657 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11658 new pm::AggregateMin()));
11659 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11660 new pm::AggregateMax()));
11661 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11662 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11663 new pm::AggregateAvg()));
11664 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11665 new pm::AggregateMin()));
11666 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11667 new pm::AggregateMax()));
11668
11669 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11670 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11671 new pm::AggregateAvg()));
11672 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11673 new pm::AggregateMin()));
11674 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11675 new pm::AggregateMax()));
11676
11677 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11678 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11679 new pm::AggregateAvg()));
11680 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11681 new pm::AggregateMin()));
11682 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11683 new pm::AggregateMax()));
11684
11685
11686 /* Guest metrics collector */
11687 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11688 aCollector->registerGuest(mCollectorGuest);
11689 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11690 this, __PRETTY_FUNCTION__, mCollectorGuest));
11691
11692 /* Create sub metrics */
11693 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11694 "Percentage of processor time spent in user mode as seen by the guest.");
11695 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11696 "Percentage of processor time spent in kernel mode as seen by the guest.");
11697 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11698 "Percentage of processor time spent idling as seen by the guest.");
11699
11700 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11701 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11702 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11703 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11704 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11705 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11706
11707 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11708
11709 /* Create and register base metrics */
11710 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
11711 machineNetRx, machineNetTx);
11712 aCollector->registerBaseMetric(machineNetRate);
11713
11714 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11715 guestLoadUser, guestLoadKernel, guestLoadIdle);
11716 aCollector->registerBaseMetric(guestCpuLoad);
11717
11718 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11719 guestMemTotal, guestMemFree,
11720 guestMemBalloon, guestMemShared,
11721 guestMemCache, guestPagedTotal);
11722 aCollector->registerBaseMetric(guestCpuMem);
11723
11724 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
11725 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
11726 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
11727 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
11728
11729 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
11730 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
11731 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
11732 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
11733
11734 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11735 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11736 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11737 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11738
11739 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11740 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11741 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11742 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11743
11744 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11745 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11746 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11747 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11748
11749 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11750 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11751 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11752 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11753
11754 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11755 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11756 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11757 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11758
11759 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11760 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11761 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11762 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11763
11764 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11765 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11766 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11767 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11768
11769 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11770 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11771 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11772 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11773
11774 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11775 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11776 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11777 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11778}
11779
11780void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11781{
11782 AssertReturnVoid(isWriteLockOnCurrentThread());
11783
11784 if (aCollector)
11785 {
11786 aCollector->unregisterMetricsFor(aMachine);
11787 aCollector->unregisterBaseMetricsFor(aMachine);
11788 }
11789}
11790
11791#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11792
11793
11794////////////////////////////////////////////////////////////////////////////////
11795
11796DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11797
11798HRESULT SessionMachine::FinalConstruct()
11799{
11800 LogFlowThisFunc(("\n"));
11801
11802#if defined(RT_OS_WINDOWS)
11803 mIPCSem = NULL;
11804#elif defined(RT_OS_OS2)
11805 mIPCSem = NULLHANDLE;
11806#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11807 mIPCSem = -1;
11808#else
11809# error "Port me!"
11810#endif
11811
11812 return BaseFinalConstruct();
11813}
11814
11815void SessionMachine::FinalRelease()
11816{
11817 LogFlowThisFunc(("\n"));
11818
11819 uninit(Uninit::Unexpected);
11820
11821 BaseFinalRelease();
11822}
11823
11824/**
11825 * @note Must be called only by Machine::openSession() from its own write lock.
11826 */
11827HRESULT SessionMachine::init(Machine *aMachine)
11828{
11829 LogFlowThisFuncEnter();
11830 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
11831
11832 AssertReturn(aMachine, E_INVALIDARG);
11833
11834 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
11835
11836 /* Enclose the state transition NotReady->InInit->Ready */
11837 AutoInitSpan autoInitSpan(this);
11838 AssertReturn(autoInitSpan.isOk(), E_FAIL);
11839
11840 /* create the interprocess semaphore */
11841#if defined(RT_OS_WINDOWS)
11842 mIPCSemName = aMachine->mData->m_strConfigFileFull;
11843 for (size_t i = 0; i < mIPCSemName.length(); i++)
11844 if (mIPCSemName.raw()[i] == '\\')
11845 mIPCSemName.raw()[i] = '/';
11846 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
11847 ComAssertMsgRet(mIPCSem,
11848 ("Cannot create IPC mutex '%ls', err=%d",
11849 mIPCSemName.raw(), ::GetLastError()),
11850 E_FAIL);
11851#elif defined(RT_OS_OS2)
11852 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
11853 aMachine->mData->mUuid.raw());
11854 mIPCSemName = ipcSem;
11855 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
11856 ComAssertMsgRet(arc == NO_ERROR,
11857 ("Cannot create IPC mutex '%s', arc=%ld",
11858 ipcSem.c_str(), arc),
11859 E_FAIL);
11860#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11861# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11862# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
11863 /** @todo Check that this still works correctly. */
11864 AssertCompileSize(key_t, 8);
11865# else
11866 AssertCompileSize(key_t, 4);
11867# endif
11868 key_t key;
11869 mIPCSem = -1;
11870 mIPCKey = "0";
11871 for (uint32_t i = 0; i < 1 << 24; i++)
11872 {
11873 key = ((uint32_t)'V' << 24) | i;
11874 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
11875 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
11876 {
11877 mIPCSem = sem;
11878 if (sem >= 0)
11879 mIPCKey = BstrFmt("%u", key);
11880 break;
11881 }
11882 }
11883# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11884 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
11885 char *pszSemName = NULL;
11886 RTStrUtf8ToCurrentCP(&pszSemName, semName);
11887 key_t key = ::ftok(pszSemName, 'V');
11888 RTStrFree(pszSemName);
11889
11890 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
11891# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11892
11893 int errnoSave = errno;
11894 if (mIPCSem < 0 && errnoSave == ENOSYS)
11895 {
11896 setError(E_FAIL,
11897 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
11898 "support for SysV IPC. Check the host kernel configuration for "
11899 "CONFIG_SYSVIPC=y"));
11900 return E_FAIL;
11901 }
11902 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
11903 * the IPC semaphores */
11904 if (mIPCSem < 0 && errnoSave == ENOSPC)
11905 {
11906#ifdef RT_OS_LINUX
11907 setError(E_FAIL,
11908 tr("Cannot create IPC semaphore because the system limit for the "
11909 "maximum number of semaphore sets (SEMMNI), or the system wide "
11910 "maximum number of semaphores (SEMMNS) would be exceeded. The "
11911 "current set of SysV IPC semaphores can be determined from "
11912 "the file /proc/sysvipc/sem"));
11913#else
11914 setError(E_FAIL,
11915 tr("Cannot create IPC semaphore because the system-imposed limit "
11916 "on the maximum number of allowed semaphores or semaphore "
11917 "identifiers system-wide would be exceeded"));
11918#endif
11919 return E_FAIL;
11920 }
11921 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
11922 E_FAIL);
11923 /* set the initial value to 1 */
11924 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
11925 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
11926 E_FAIL);
11927#else
11928# error "Port me!"
11929#endif
11930
11931 /* memorize the peer Machine */
11932 unconst(mPeer) = aMachine;
11933 /* share the parent pointer */
11934 unconst(mParent) = aMachine->mParent;
11935
11936 /* take the pointers to data to share */
11937 mData.share(aMachine->mData);
11938 mSSData.share(aMachine->mSSData);
11939
11940 mUserData.share(aMachine->mUserData);
11941 mHWData.share(aMachine->mHWData);
11942 mMediaData.share(aMachine->mMediaData);
11943
11944 mStorageControllers.allocate();
11945 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
11946 it != aMachine->mStorageControllers->end();
11947 ++it)
11948 {
11949 ComObjPtr<StorageController> ctl;
11950 ctl.createObject();
11951 ctl->init(this, *it);
11952 mStorageControllers->push_back(ctl);
11953 }
11954
11955 unconst(mBIOSSettings).createObject();
11956 mBIOSSettings->init(this, aMachine->mBIOSSettings);
11957 /* create another VRDEServer object that will be mutable */
11958 unconst(mVRDEServer).createObject();
11959 mVRDEServer->init(this, aMachine->mVRDEServer);
11960 /* create another audio adapter object that will be mutable */
11961 unconst(mAudioAdapter).createObject();
11962 mAudioAdapter->init(this, aMachine->mAudioAdapter);
11963 /* create a list of serial ports that will be mutable */
11964 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11965 {
11966 unconst(mSerialPorts[slot]).createObject();
11967 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
11968 }
11969 /* create a list of parallel ports that will be mutable */
11970 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11971 {
11972 unconst(mParallelPorts[slot]).createObject();
11973 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
11974 }
11975 /* create another USB controller object that will be mutable */
11976 unconst(mUSBController).createObject();
11977 mUSBController->init(this, aMachine->mUSBController);
11978
11979 /* create a list of network adapters that will be mutable */
11980 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
11981 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11982 {
11983 unconst(mNetworkAdapters[slot]).createObject();
11984 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
11985 }
11986
11987 /* create another bandwidth control object that will be mutable */
11988 unconst(mBandwidthControl).createObject();
11989 mBandwidthControl->init(this, aMachine->mBandwidthControl);
11990
11991 /* default is to delete saved state on Saved -> PoweredOff transition */
11992 mRemoveSavedState = true;
11993
11994 /* Confirm a successful initialization when it's the case */
11995 autoInitSpan.setSucceeded();
11996
11997 LogFlowThisFuncLeave();
11998 return S_OK;
11999}
12000
12001/**
12002 * Uninitializes this session object. If the reason is other than
12003 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
12004 *
12005 * @param aReason uninitialization reason
12006 *
12007 * @note Locks mParent + this object for writing.
12008 */
12009void SessionMachine::uninit(Uninit::Reason aReason)
12010{
12011 LogFlowThisFuncEnter();
12012 LogFlowThisFunc(("reason=%d\n", aReason));
12013
12014 /*
12015 * Strongly reference ourselves to prevent this object deletion after
12016 * mData->mSession.mMachine.setNull() below (which can release the last
12017 * reference and call the destructor). Important: this must be done before
12018 * accessing any members (and before AutoUninitSpan that does it as well).
12019 * This self reference will be released as the very last step on return.
12020 */
12021 ComObjPtr<SessionMachine> selfRef = this;
12022
12023 /* Enclose the state transition Ready->InUninit->NotReady */
12024 AutoUninitSpan autoUninitSpan(this);
12025 if (autoUninitSpan.uninitDone())
12026 {
12027 LogFlowThisFunc(("Already uninitialized\n"));
12028 LogFlowThisFuncLeave();
12029 return;
12030 }
12031
12032 if (autoUninitSpan.initFailed())
12033 {
12034 /* We've been called by init() because it's failed. It's not really
12035 * necessary (nor it's safe) to perform the regular uninit sequence
12036 * below, the following is enough.
12037 */
12038 LogFlowThisFunc(("Initialization failed.\n"));
12039#if defined(RT_OS_WINDOWS)
12040 if (mIPCSem)
12041 ::CloseHandle(mIPCSem);
12042 mIPCSem = NULL;
12043#elif defined(RT_OS_OS2)
12044 if (mIPCSem != NULLHANDLE)
12045 ::DosCloseMutexSem(mIPCSem);
12046 mIPCSem = NULLHANDLE;
12047#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12048 if (mIPCSem >= 0)
12049 ::semctl(mIPCSem, 0, IPC_RMID);
12050 mIPCSem = -1;
12051# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12052 mIPCKey = "0";
12053# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12054#else
12055# error "Port me!"
12056#endif
12057 uninitDataAndChildObjects();
12058 mData.free();
12059 unconst(mParent) = NULL;
12060 unconst(mPeer) = NULL;
12061 LogFlowThisFuncLeave();
12062 return;
12063 }
12064
12065 MachineState_T lastState;
12066 {
12067 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12068 lastState = mData->mMachineState;
12069 }
12070 NOREF(lastState);
12071
12072#ifdef VBOX_WITH_USB
12073 // release all captured USB devices, but do this before requesting the locks below
12074 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12075 {
12076 /* Console::captureUSBDevices() is called in the VM process only after
12077 * setting the machine state to Starting or Restoring.
12078 * Console::detachAllUSBDevices() will be called upon successful
12079 * termination. So, we need to release USB devices only if there was
12080 * an abnormal termination of a running VM.
12081 *
12082 * This is identical to SessionMachine::DetachAllUSBDevices except
12083 * for the aAbnormal argument. */
12084 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12085 AssertComRC(rc);
12086 NOREF(rc);
12087
12088 USBProxyService *service = mParent->host()->usbProxyService();
12089 if (service)
12090 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12091 }
12092#endif /* VBOX_WITH_USB */
12093
12094 // we need to lock this object in uninit() because the lock is shared
12095 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12096 // and others need mParent lock, and USB needs host lock.
12097 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12098
12099#if 0
12100 // Trigger async cleanup tasks, avoid doing things here which are not
12101 // vital to be done immediately and maybe need more locks. This calls
12102 // Machine::unregisterMetrics().
12103 mParent->onMachineUninit(mPeer);
12104#else
12105 /*
12106 * It is safe to call Machine::unregisterMetrics() here because
12107 * PerformanceCollector::samplerCallback no longer accesses guest methods
12108 * holding the lock.
12109 */
12110 unregisterMetrics(mParent->performanceCollector(), mPeer);
12111#endif
12112 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12113 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12114 this, __PRETTY_FUNCTION__, mCollectorGuest));
12115 if (mCollectorGuest)
12116 {
12117 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
12118 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12119 mCollectorGuest = NULL;
12120 }
12121
12122 if (aReason == Uninit::Abnormal)
12123 {
12124 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12125 Global::IsOnlineOrTransient(lastState)));
12126
12127 /* reset the state to Aborted */
12128 if (mData->mMachineState != MachineState_Aborted)
12129 setMachineState(MachineState_Aborted);
12130 }
12131
12132 // any machine settings modified?
12133 if (mData->flModifications)
12134 {
12135 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12136 rollback(false /* aNotify */);
12137 }
12138
12139 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12140 || !mConsoleTaskData.mSnapshot);
12141 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12142 {
12143 LogWarningThisFunc(("canceling failed save state request!\n"));
12144 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12145 }
12146 else if (!mConsoleTaskData.mSnapshot.isNull())
12147 {
12148 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12149
12150 /* delete all differencing hard disks created (this will also attach
12151 * their parents back by rolling back mMediaData) */
12152 rollbackMedia();
12153
12154 // delete the saved state file (it might have been already created)
12155 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12156 // think it's still in use
12157 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12158 mConsoleTaskData.mSnapshot->uninit();
12159 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12160 }
12161
12162 if (!mData->mSession.mType.isEmpty())
12163 {
12164 /* mType is not null when this machine's process has been started by
12165 * Machine::LaunchVMProcess(), therefore it is our child. We
12166 * need to queue the PID to reap the process (and avoid zombies on
12167 * Linux). */
12168 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12169 mParent->addProcessToReap(mData->mSession.mPID);
12170 }
12171
12172 mData->mSession.mPID = NIL_RTPROCESS;
12173
12174 if (aReason == Uninit::Unexpected)
12175 {
12176 /* Uninitialization didn't come from #checkForDeath(), so tell the
12177 * client watcher thread to update the set of machines that have open
12178 * sessions. */
12179 mParent->updateClientWatcher();
12180 }
12181
12182 /* uninitialize all remote controls */
12183 if (mData->mSession.mRemoteControls.size())
12184 {
12185 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12186 mData->mSession.mRemoteControls.size()));
12187
12188 Data::Session::RemoteControlList::iterator it =
12189 mData->mSession.mRemoteControls.begin();
12190 while (it != mData->mSession.mRemoteControls.end())
12191 {
12192 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12193 HRESULT rc = (*it)->Uninitialize();
12194 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12195 if (FAILED(rc))
12196 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12197 ++it;
12198 }
12199 mData->mSession.mRemoteControls.clear();
12200 }
12201
12202 /*
12203 * An expected uninitialization can come only from #checkForDeath().
12204 * Otherwise it means that something's gone really wrong (for example,
12205 * the Session implementation has released the VirtualBox reference
12206 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12207 * etc). However, it's also possible, that the client releases the IPC
12208 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12209 * but the VirtualBox release event comes first to the server process.
12210 * This case is practically possible, so we should not assert on an
12211 * unexpected uninit, just log a warning.
12212 */
12213
12214 if ((aReason == Uninit::Unexpected))
12215 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12216
12217 if (aReason != Uninit::Normal)
12218 {
12219 mData->mSession.mDirectControl.setNull();
12220 }
12221 else
12222 {
12223 /* this must be null here (see #OnSessionEnd()) */
12224 Assert(mData->mSession.mDirectControl.isNull());
12225 Assert(mData->mSession.mState == SessionState_Unlocking);
12226 Assert(!mData->mSession.mProgress.isNull());
12227 }
12228 if (mData->mSession.mProgress)
12229 {
12230 if (aReason == Uninit::Normal)
12231 mData->mSession.mProgress->notifyComplete(S_OK);
12232 else
12233 mData->mSession.mProgress->notifyComplete(E_FAIL,
12234 COM_IIDOF(ISession),
12235 getComponentName(),
12236 tr("The VM session was aborted"));
12237 mData->mSession.mProgress.setNull();
12238 }
12239
12240 /* remove the association between the peer machine and this session machine */
12241 Assert( (SessionMachine*)mData->mSession.mMachine == this
12242 || aReason == Uninit::Unexpected);
12243
12244 /* reset the rest of session data */
12245 mData->mSession.mMachine.setNull();
12246 mData->mSession.mState = SessionState_Unlocked;
12247 mData->mSession.mType.setNull();
12248
12249 /* close the interprocess semaphore before leaving the exclusive lock */
12250#if defined(RT_OS_WINDOWS)
12251 if (mIPCSem)
12252 ::CloseHandle(mIPCSem);
12253 mIPCSem = NULL;
12254#elif defined(RT_OS_OS2)
12255 if (mIPCSem != NULLHANDLE)
12256 ::DosCloseMutexSem(mIPCSem);
12257 mIPCSem = NULLHANDLE;
12258#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12259 if (mIPCSem >= 0)
12260 ::semctl(mIPCSem, 0, IPC_RMID);
12261 mIPCSem = -1;
12262# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12263 mIPCKey = "0";
12264# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12265#else
12266# error "Port me!"
12267#endif
12268
12269 /* fire an event */
12270 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12271
12272 uninitDataAndChildObjects();
12273
12274 /* free the essential data structure last */
12275 mData.free();
12276
12277 /* release the exclusive lock before setting the below two to NULL */
12278 multilock.release();
12279
12280 unconst(mParent) = NULL;
12281 unconst(mPeer) = NULL;
12282
12283 LogFlowThisFuncLeave();
12284}
12285
12286// util::Lockable interface
12287////////////////////////////////////////////////////////////////////////////////
12288
12289/**
12290 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12291 * with the primary Machine instance (mPeer).
12292 */
12293RWLockHandle *SessionMachine::lockHandle() const
12294{
12295 AssertReturn(mPeer != NULL, NULL);
12296 return mPeer->lockHandle();
12297}
12298
12299// IInternalMachineControl methods
12300////////////////////////////////////////////////////////////////////////////////
12301
12302/**
12303 * Passes collected guest statistics to performance collector object
12304 */
12305STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12306 ULONG aCpuKernel, ULONG aCpuIdle,
12307 ULONG aMemTotal, ULONG aMemFree,
12308 ULONG aMemBalloon, ULONG aMemShared,
12309 ULONG aMemCache, ULONG aPageTotal,
12310 ULONG aAllocVMM, ULONG aFreeVMM,
12311 ULONG aBalloonedVMM, ULONG aSharedVMM,
12312 ULONG aVmNetRx, ULONG aVmNetTx)
12313{
12314 if (mCollectorGuest)
12315 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12316 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12317 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12318 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12319
12320 return S_OK;
12321}
12322
12323/**
12324 * @note Locks this object for writing.
12325 */
12326STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12327{
12328 AutoCaller autoCaller(this);
12329 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12330
12331 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12332
12333 mRemoveSavedState = aRemove;
12334
12335 return S_OK;
12336}
12337
12338/**
12339 * @note Locks the same as #setMachineState() does.
12340 */
12341STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12342{
12343 return setMachineState(aMachineState);
12344}
12345
12346/**
12347 * @note Locks this object for reading.
12348 */
12349STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
12350{
12351 AutoCaller autoCaller(this);
12352 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12353
12354 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12355
12356#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
12357 mIPCSemName.cloneTo(aId);
12358 return S_OK;
12359#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12360# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12361 mIPCKey.cloneTo(aId);
12362# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12363 mData->m_strConfigFileFull.cloneTo(aId);
12364# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12365 return S_OK;
12366#else
12367# error "Port me!"
12368#endif
12369}
12370
12371/**
12372 * @note Locks this object for writing.
12373 */
12374STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12375{
12376 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12377 AutoCaller autoCaller(this);
12378 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12379
12380 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12381
12382 if (mData->mSession.mState != SessionState_Locked)
12383 return VBOX_E_INVALID_OBJECT_STATE;
12384
12385 if (!mData->mSession.mProgress.isNull())
12386 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12387
12388 LogFlowThisFunc(("returns S_OK.\n"));
12389 return S_OK;
12390}
12391
12392/**
12393 * @note Locks this object for writing.
12394 */
12395STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12396{
12397 AutoCaller autoCaller(this);
12398 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12399
12400 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12401
12402 if (mData->mSession.mState != SessionState_Locked)
12403 return VBOX_E_INVALID_OBJECT_STATE;
12404
12405 /* Finalize the LaunchVMProcess progress object. */
12406 if (mData->mSession.mProgress)
12407 {
12408 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12409 mData->mSession.mProgress.setNull();
12410 }
12411
12412 if (SUCCEEDED((HRESULT)iResult))
12413 {
12414#ifdef VBOX_WITH_RESOURCE_USAGE_API
12415 /* The VM has been powered up successfully, so it makes sense
12416 * now to offer the performance metrics for a running machine
12417 * object. Doing it earlier wouldn't be safe. */
12418 registerMetrics(mParent->performanceCollector(), mPeer,
12419 mData->mSession.mPID);
12420#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12421 }
12422
12423 return S_OK;
12424}
12425
12426/**
12427 * @note Locks this object for writing.
12428 */
12429STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12430{
12431 LogFlowThisFuncEnter();
12432
12433 CheckComArgOutPointerValid(aProgress);
12434
12435 AutoCaller autoCaller(this);
12436 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12437
12438 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12439
12440 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12441 E_FAIL);
12442
12443 /* create a progress object to track operation completion */
12444 ComObjPtr<Progress> pProgress;
12445 pProgress.createObject();
12446 pProgress->init(getVirtualBox(),
12447 static_cast<IMachine *>(this) /* aInitiator */,
12448 Bstr(tr("Stopping the virtual machine")).raw(),
12449 FALSE /* aCancelable */);
12450
12451 /* fill in the console task data */
12452 mConsoleTaskData.mLastState = mData->mMachineState;
12453 mConsoleTaskData.mProgress = pProgress;
12454
12455 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12456 setMachineState(MachineState_Stopping);
12457
12458 pProgress.queryInterfaceTo(aProgress);
12459
12460 return S_OK;
12461}
12462
12463/**
12464 * @note Locks this object for writing.
12465 */
12466STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12467{
12468 LogFlowThisFuncEnter();
12469
12470 AutoCaller autoCaller(this);
12471 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12472
12473 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12474
12475 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12476 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12477 && mConsoleTaskData.mLastState != MachineState_Null,
12478 E_FAIL);
12479
12480 /*
12481 * On failure, set the state to the state we had when BeginPoweringDown()
12482 * was called (this is expected by Console::PowerDown() and the associated
12483 * task). On success the VM process already changed the state to
12484 * MachineState_PoweredOff, so no need to do anything.
12485 */
12486 if (FAILED(iResult))
12487 setMachineState(mConsoleTaskData.mLastState);
12488
12489 /* notify the progress object about operation completion */
12490 Assert(mConsoleTaskData.mProgress);
12491 if (SUCCEEDED(iResult))
12492 mConsoleTaskData.mProgress->notifyComplete(S_OK);
12493 else
12494 {
12495 Utf8Str strErrMsg(aErrMsg);
12496 if (strErrMsg.length())
12497 mConsoleTaskData.mProgress->notifyComplete(iResult,
12498 COM_IIDOF(ISession),
12499 getComponentName(),
12500 strErrMsg.c_str());
12501 else
12502 mConsoleTaskData.mProgress->notifyComplete(iResult);
12503 }
12504
12505 /* clear out the temporary saved state data */
12506 mConsoleTaskData.mLastState = MachineState_Null;
12507 mConsoleTaskData.mProgress.setNull();
12508
12509 LogFlowThisFuncLeave();
12510 return S_OK;
12511}
12512
12513
12514/**
12515 * Goes through the USB filters of the given machine to see if the given
12516 * device matches any filter or not.
12517 *
12518 * @note Locks the same as USBController::hasMatchingFilter() does.
12519 */
12520STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12521 BOOL *aMatched,
12522 ULONG *aMaskedIfs)
12523{
12524 LogFlowThisFunc(("\n"));
12525
12526 CheckComArgNotNull(aUSBDevice);
12527 CheckComArgOutPointerValid(aMatched);
12528
12529 AutoCaller autoCaller(this);
12530 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12531
12532#ifdef VBOX_WITH_USB
12533 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
12534#else
12535 NOREF(aUSBDevice);
12536 NOREF(aMaskedIfs);
12537 *aMatched = FALSE;
12538#endif
12539
12540 return S_OK;
12541}
12542
12543/**
12544 * @note Locks the same as Host::captureUSBDevice() does.
12545 */
12546STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12547{
12548 LogFlowThisFunc(("\n"));
12549
12550 AutoCaller autoCaller(this);
12551 AssertComRCReturnRC(autoCaller.rc());
12552
12553#ifdef VBOX_WITH_USB
12554 /* if captureDeviceForVM() fails, it must have set extended error info */
12555 clearError();
12556 MultiResult rc = mParent->host()->checkUSBProxyService();
12557 if (FAILED(rc)) return rc;
12558
12559 USBProxyService *service = mParent->host()->usbProxyService();
12560 AssertReturn(service, E_FAIL);
12561 return service->captureDeviceForVM(this, Guid(aId).ref());
12562#else
12563 NOREF(aId);
12564 return E_NOTIMPL;
12565#endif
12566}
12567
12568/**
12569 * @note Locks the same as Host::detachUSBDevice() does.
12570 */
12571STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12572{
12573 LogFlowThisFunc(("\n"));
12574
12575 AutoCaller autoCaller(this);
12576 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12577
12578#ifdef VBOX_WITH_USB
12579 USBProxyService *service = mParent->host()->usbProxyService();
12580 AssertReturn(service, E_FAIL);
12581 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12582#else
12583 NOREF(aId);
12584 NOREF(aDone);
12585 return E_NOTIMPL;
12586#endif
12587}
12588
12589/**
12590 * Inserts all machine filters to the USB proxy service and then calls
12591 * Host::autoCaptureUSBDevices().
12592 *
12593 * Called by Console from the VM process upon VM startup.
12594 *
12595 * @note Locks what called methods lock.
12596 */
12597STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12598{
12599 LogFlowThisFunc(("\n"));
12600
12601 AutoCaller autoCaller(this);
12602 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12603
12604#ifdef VBOX_WITH_USB
12605 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
12606 AssertComRC(rc);
12607 NOREF(rc);
12608
12609 USBProxyService *service = mParent->host()->usbProxyService();
12610 AssertReturn(service, E_FAIL);
12611 return service->autoCaptureDevicesForVM(this);
12612#else
12613 return S_OK;
12614#endif
12615}
12616
12617/**
12618 * Removes all machine filters from the USB proxy service and then calls
12619 * Host::detachAllUSBDevices().
12620 *
12621 * Called by Console from the VM process upon normal VM termination or by
12622 * SessionMachine::uninit() upon abnormal VM termination (from under the
12623 * Machine/SessionMachine lock).
12624 *
12625 * @note Locks what called methods lock.
12626 */
12627STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12628{
12629 LogFlowThisFunc(("\n"));
12630
12631 AutoCaller autoCaller(this);
12632 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12633
12634#ifdef VBOX_WITH_USB
12635 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12636 AssertComRC(rc);
12637 NOREF(rc);
12638
12639 USBProxyService *service = mParent->host()->usbProxyService();
12640 AssertReturn(service, E_FAIL);
12641 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12642#else
12643 NOREF(aDone);
12644 return S_OK;
12645#endif
12646}
12647
12648/**
12649 * @note Locks this object for writing.
12650 */
12651STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12652 IProgress **aProgress)
12653{
12654 LogFlowThisFuncEnter();
12655
12656 AssertReturn(aSession, E_INVALIDARG);
12657 AssertReturn(aProgress, E_INVALIDARG);
12658
12659 AutoCaller autoCaller(this);
12660
12661 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
12662 /*
12663 * We don't assert below because it might happen that a non-direct session
12664 * informs us it is closed right after we've been uninitialized -- it's ok.
12665 */
12666 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12667
12668 /* get IInternalSessionControl interface */
12669 ComPtr<IInternalSessionControl> control(aSession);
12670
12671 ComAssertRet(!control.isNull(), E_INVALIDARG);
12672
12673 /* Creating a Progress object requires the VirtualBox lock, and
12674 * thus locking it here is required by the lock order rules. */
12675 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12676
12677 if (control == mData->mSession.mDirectControl)
12678 {
12679 ComAssertRet(aProgress, E_POINTER);
12680
12681 /* The direct session is being normally closed by the client process
12682 * ----------------------------------------------------------------- */
12683
12684 /* go to the closing state (essential for all open*Session() calls and
12685 * for #checkForDeath()) */
12686 Assert(mData->mSession.mState == SessionState_Locked);
12687 mData->mSession.mState = SessionState_Unlocking;
12688
12689 /* set direct control to NULL to release the remote instance */
12690 mData->mSession.mDirectControl.setNull();
12691 LogFlowThisFunc(("Direct control is set to NULL\n"));
12692
12693 if (mData->mSession.mProgress)
12694 {
12695 /* finalize the progress, someone might wait if a frontend
12696 * closes the session before powering on the VM. */
12697 mData->mSession.mProgress->notifyComplete(E_FAIL,
12698 COM_IIDOF(ISession),
12699 getComponentName(),
12700 tr("The VM session was closed before any attempt to power it on"));
12701 mData->mSession.mProgress.setNull();
12702 }
12703
12704 /* Create the progress object the client will use to wait until
12705 * #checkForDeath() is called to uninitialize this session object after
12706 * it releases the IPC semaphore.
12707 * Note! Because we're "reusing" mProgress here, this must be a proxy
12708 * object just like for LaunchVMProcess. */
12709 Assert(mData->mSession.mProgress.isNull());
12710 ComObjPtr<ProgressProxy> progress;
12711 progress.createObject();
12712 ComPtr<IUnknown> pPeer(mPeer);
12713 progress->init(mParent, pPeer,
12714 Bstr(tr("Closing session")).raw(),
12715 FALSE /* aCancelable */);
12716 progress.queryInterfaceTo(aProgress);
12717 mData->mSession.mProgress = progress;
12718 }
12719 else
12720 {
12721 /* the remote session is being normally closed */
12722 Data::Session::RemoteControlList::iterator it =
12723 mData->mSession.mRemoteControls.begin();
12724 while (it != mData->mSession.mRemoteControls.end())
12725 {
12726 if (control == *it)
12727 break;
12728 ++it;
12729 }
12730 BOOL found = it != mData->mSession.mRemoteControls.end();
12731 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12732 E_INVALIDARG);
12733 // This MUST be erase(it), not remove(*it) as the latter triggers a
12734 // very nasty use after free due to the place where the value "lives".
12735 mData->mSession.mRemoteControls.erase(it);
12736 }
12737
12738 /* signal the client watcher thread, because the client is going away */
12739 mParent->updateClientWatcher();
12740
12741 LogFlowThisFuncLeave();
12742 return S_OK;
12743}
12744
12745/**
12746 * @note Locks this object for writing.
12747 */
12748STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12749{
12750 LogFlowThisFuncEnter();
12751
12752 CheckComArgOutPointerValid(aProgress);
12753 CheckComArgOutPointerValid(aStateFilePath);
12754
12755 AutoCaller autoCaller(this);
12756 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12757
12758 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12759
12760 AssertReturn( mData->mMachineState == MachineState_Paused
12761 && mConsoleTaskData.mLastState == MachineState_Null
12762 && mConsoleTaskData.strStateFilePath.isEmpty(),
12763 E_FAIL);
12764
12765 /* create a progress object to track operation completion */
12766 ComObjPtr<Progress> pProgress;
12767 pProgress.createObject();
12768 pProgress->init(getVirtualBox(),
12769 static_cast<IMachine *>(this) /* aInitiator */,
12770 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12771 FALSE /* aCancelable */);
12772
12773 Utf8Str strStateFilePath;
12774 /* stateFilePath is null when the machine is not running */
12775 if (mData->mMachineState == MachineState_Paused)
12776 composeSavedStateFilename(strStateFilePath);
12777
12778 /* fill in the console task data */
12779 mConsoleTaskData.mLastState = mData->mMachineState;
12780 mConsoleTaskData.strStateFilePath = strStateFilePath;
12781 mConsoleTaskData.mProgress = pProgress;
12782
12783 /* set the state to Saving (this is expected by Console::SaveState()) */
12784 setMachineState(MachineState_Saving);
12785
12786 strStateFilePath.cloneTo(aStateFilePath);
12787 pProgress.queryInterfaceTo(aProgress);
12788
12789 return S_OK;
12790}
12791
12792/**
12793 * @note Locks mParent + this object for writing.
12794 */
12795STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12796{
12797 LogFlowThisFunc(("\n"));
12798
12799 AutoCaller autoCaller(this);
12800 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12801
12802 /* endSavingState() need mParent lock */
12803 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12804
12805 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12806 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12807 && mConsoleTaskData.mLastState != MachineState_Null
12808 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12809 E_FAIL);
12810
12811 /*
12812 * On failure, set the state to the state we had when BeginSavingState()
12813 * was called (this is expected by Console::SaveState() and the associated
12814 * task). On success the VM process already changed the state to
12815 * MachineState_Saved, so no need to do anything.
12816 */
12817 if (FAILED(iResult))
12818 setMachineState(mConsoleTaskData.mLastState);
12819
12820 return endSavingState(iResult, aErrMsg);
12821}
12822
12823/**
12824 * @note Locks this object for writing.
12825 */
12826STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12827{
12828 LogFlowThisFunc(("\n"));
12829
12830 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12831
12832 AutoCaller autoCaller(this);
12833 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12834
12835 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12836
12837 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12838 || mData->mMachineState == MachineState_Teleported
12839 || mData->mMachineState == MachineState_Aborted
12840 , E_FAIL); /** @todo setError. */
12841
12842 Utf8Str stateFilePathFull = aSavedStateFile;
12843 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
12844 if (RT_FAILURE(vrc))
12845 return setError(VBOX_E_FILE_ERROR,
12846 tr("Invalid saved state file path '%ls' (%Rrc)"),
12847 aSavedStateFile,
12848 vrc);
12849
12850 mSSData->strStateFilePath = stateFilePathFull;
12851
12852 /* The below setMachineState() will detect the state transition and will
12853 * update the settings file */
12854
12855 return setMachineState(MachineState_Saved);
12856}
12857
12858STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
12859 ComSafeArrayOut(BSTR, aValues),
12860 ComSafeArrayOut(LONG64, aTimestamps),
12861 ComSafeArrayOut(BSTR, aFlags))
12862{
12863 LogFlowThisFunc(("\n"));
12864
12865#ifdef VBOX_WITH_GUEST_PROPS
12866 using namespace guestProp;
12867
12868 AutoCaller autoCaller(this);
12869 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12870
12871 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12872
12873 CheckComArgOutSafeArrayPointerValid(aNames);
12874 CheckComArgOutSafeArrayPointerValid(aValues);
12875 CheckComArgOutSafeArrayPointerValid(aTimestamps);
12876 CheckComArgOutSafeArrayPointerValid(aFlags);
12877
12878 size_t cEntries = mHWData->mGuestProperties.size();
12879 com::SafeArray<BSTR> names(cEntries);
12880 com::SafeArray<BSTR> values(cEntries);
12881 com::SafeArray<LONG64> timestamps(cEntries);
12882 com::SafeArray<BSTR> flags(cEntries);
12883 unsigned i = 0;
12884 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
12885 it != mHWData->mGuestProperties.end();
12886 ++it)
12887 {
12888 char szFlags[MAX_FLAGS_LEN + 1];
12889 it->first.cloneTo(&names[i]);
12890 it->second.strValue.cloneTo(&values[i]);
12891 timestamps[i] = it->second.mTimestamp;
12892 /* If it is NULL, keep it NULL. */
12893 if (it->second.mFlags)
12894 {
12895 writeFlags(it->second.mFlags, szFlags);
12896 Bstr(szFlags).cloneTo(&flags[i]);
12897 }
12898 else
12899 flags[i] = NULL;
12900 ++i;
12901 }
12902 names.detachTo(ComSafeArrayOutArg(aNames));
12903 values.detachTo(ComSafeArrayOutArg(aValues));
12904 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
12905 flags.detachTo(ComSafeArrayOutArg(aFlags));
12906 return S_OK;
12907#else
12908 ReturnComNotImplemented();
12909#endif
12910}
12911
12912STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
12913 IN_BSTR aValue,
12914 LONG64 aTimestamp,
12915 IN_BSTR aFlags)
12916{
12917 LogFlowThisFunc(("\n"));
12918
12919#ifdef VBOX_WITH_GUEST_PROPS
12920 using namespace guestProp;
12921
12922 CheckComArgStrNotEmptyOrNull(aName);
12923 CheckComArgNotNull(aValue);
12924 CheckComArgNotNull(aFlags);
12925
12926 try
12927 {
12928 /*
12929 * Convert input up front.
12930 */
12931 Utf8Str utf8Name(aName);
12932 uint32_t fFlags = NILFLAG;
12933 if (aFlags)
12934 {
12935 Utf8Str utf8Flags(aFlags);
12936 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
12937 AssertRCReturn(vrc, E_INVALIDARG);
12938 }
12939
12940 /*
12941 * Now grab the object lock, validate the state and do the update.
12942 */
12943 AutoCaller autoCaller(this);
12944 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12945
12946 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12947
12948 switch (mData->mMachineState)
12949 {
12950 case MachineState_Paused:
12951 case MachineState_Running:
12952 case MachineState_Teleporting:
12953 case MachineState_TeleportingPausedVM:
12954 case MachineState_LiveSnapshotting:
12955 case MachineState_DeletingSnapshotOnline:
12956 case MachineState_DeletingSnapshotPaused:
12957 case MachineState_Saving:
12958 case MachineState_Stopping:
12959 break;
12960
12961 default:
12962 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
12963 VBOX_E_INVALID_VM_STATE);
12964 }
12965
12966 setModified(IsModified_MachineData);
12967 mHWData.backup();
12968
12969 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
12970 if (it != mHWData->mGuestProperties.end())
12971 {
12972 if (RT_VALID_PTR(aValue) && *(aValue) != '\0')
12973 {
12974 it->second.strValue = aValue;
12975 it->second.mFlags = fFlags;
12976 it->second.mTimestamp = aTimestamp;
12977 }
12978 else
12979 mHWData->mGuestProperties.erase(it);
12980
12981 mData->mGuestPropertiesModified = TRUE;
12982 }
12983
12984 /*
12985 * Send a callback notification if appropriate
12986 */
12987 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
12988 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
12989 RTSTR_MAX,
12990 utf8Name.c_str(),
12991 RTSTR_MAX, NULL)
12992 )
12993 {
12994 alock.release();
12995
12996 mParent->onGuestPropertyChange(mData->mUuid,
12997 aName,
12998 aValue,
12999 aFlags);
13000 }
13001 }
13002 catch (...)
13003 {
13004 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13005 }
13006 return S_OK;
13007#else
13008 ReturnComNotImplemented();
13009#endif
13010}
13011
13012STDMETHODIMP SessionMachine::LockMedia()
13013{
13014 AutoCaller autoCaller(this);
13015 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13016
13017 AutoMultiWriteLock2 alock(this->lockHandle(),
13018 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13019
13020 AssertReturn( mData->mMachineState == MachineState_Starting
13021 || mData->mMachineState == MachineState_Restoring
13022 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13023
13024 clearError();
13025 alock.release();
13026 return lockMedia();
13027}
13028
13029STDMETHODIMP SessionMachine::UnlockMedia()
13030{
13031 unlockMedia();
13032 return S_OK;
13033}
13034
13035STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13036 IMediumAttachment **aNewAttachment)
13037{
13038 CheckComArgNotNull(aAttachment);
13039 CheckComArgOutPointerValid(aNewAttachment);
13040
13041 AutoCaller autoCaller(this);
13042 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13043
13044 // request the host lock first, since might be calling Host methods for getting host drives;
13045 // next, protect the media tree all the while we're in here, as well as our member variables
13046 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13047 this->lockHandle(),
13048 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13049
13050 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13051
13052 Bstr ctrlName;
13053 LONG lPort;
13054 LONG lDevice;
13055 bool fTempEject;
13056 {
13057 AutoCaller autoAttachCaller(this);
13058 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13059
13060 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13061
13062 /* Need to query the details first, as the IMediumAttachment reference
13063 * might be to the original settings, which we are going to change. */
13064 ctrlName = pAttach->getControllerName();
13065 lPort = pAttach->getPort();
13066 lDevice = pAttach->getDevice();
13067 fTempEject = pAttach->getTempEject();
13068 }
13069
13070 if (!fTempEject)
13071 {
13072 /* Remember previously mounted medium. The medium before taking the
13073 * backup is not necessarily the same thing. */
13074 ComObjPtr<Medium> oldmedium;
13075 oldmedium = pAttach->getMedium();
13076
13077 setModified(IsModified_Storage);
13078 mMediaData.backup();
13079
13080 // The backup operation makes the pAttach reference point to the
13081 // old settings. Re-get the correct reference.
13082 pAttach = findAttachment(mMediaData->mAttachments,
13083 ctrlName.raw(),
13084 lPort,
13085 lDevice);
13086
13087 {
13088 AutoCaller autoAttachCaller(this);
13089 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13090
13091 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13092 if (!oldmedium.isNull())
13093 oldmedium->removeBackReference(mData->mUuid);
13094
13095 pAttach->updateMedium(NULL);
13096 pAttach->updateEjected();
13097 }
13098
13099 setModified(IsModified_Storage);
13100 }
13101 else
13102 {
13103 {
13104 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13105 pAttach->updateEjected();
13106 }
13107 }
13108
13109 pAttach.queryInterfaceTo(aNewAttachment);
13110
13111 return S_OK;
13112}
13113
13114// public methods only for internal purposes
13115/////////////////////////////////////////////////////////////////////////////
13116
13117/**
13118 * Called from the client watcher thread to check for expected or unexpected
13119 * death of the client process that has a direct session to this machine.
13120 *
13121 * On Win32 and on OS/2, this method is called only when we've got the
13122 * mutex (i.e. the client has either died or terminated normally) so it always
13123 * returns @c true (the client is terminated, the session machine is
13124 * uninitialized).
13125 *
13126 * On other platforms, the method returns @c true if the client process has
13127 * terminated normally or abnormally and the session machine was uninitialized,
13128 * and @c false if the client process is still alive.
13129 *
13130 * @note Locks this object for writing.
13131 */
13132bool SessionMachine::checkForDeath()
13133{
13134 Uninit::Reason reason;
13135 bool terminated = false;
13136
13137 /* Enclose autoCaller with a block because calling uninit() from under it
13138 * will deadlock. */
13139 {
13140 AutoCaller autoCaller(this);
13141 if (!autoCaller.isOk())
13142 {
13143 /* return true if not ready, to cause the client watcher to exclude
13144 * the corresponding session from watching */
13145 LogFlowThisFunc(("Already uninitialized!\n"));
13146 return true;
13147 }
13148
13149 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13150
13151 /* Determine the reason of death: if the session state is Closing here,
13152 * everything is fine. Otherwise it means that the client did not call
13153 * OnSessionEnd() before it released the IPC semaphore. This may happen
13154 * either because the client process has abnormally terminated, or
13155 * because it simply forgot to call ISession::Close() before exiting. We
13156 * threat the latter also as an abnormal termination (see
13157 * Session::uninit() for details). */
13158 reason = mData->mSession.mState == SessionState_Unlocking ?
13159 Uninit::Normal :
13160 Uninit::Abnormal;
13161
13162#if defined(RT_OS_WINDOWS)
13163
13164 AssertMsg(mIPCSem, ("semaphore must be created"));
13165
13166 /* release the IPC mutex */
13167 ::ReleaseMutex(mIPCSem);
13168
13169 terminated = true;
13170
13171#elif defined(RT_OS_OS2)
13172
13173 AssertMsg(mIPCSem, ("semaphore must be created"));
13174
13175 /* release the IPC mutex */
13176 ::DosReleaseMutexSem(mIPCSem);
13177
13178 terminated = true;
13179
13180#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
13181
13182 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
13183
13184 int val = ::semctl(mIPCSem, 0, GETVAL);
13185 if (val > 0)
13186 {
13187 /* the semaphore is signaled, meaning the session is terminated */
13188 terminated = true;
13189 }
13190
13191#else
13192# error "Port me!"
13193#endif
13194
13195 } /* AutoCaller block */
13196
13197 if (terminated)
13198 uninit(reason);
13199
13200 return terminated;
13201}
13202
13203/**
13204 * @note Locks this object for reading.
13205 */
13206HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13207{
13208 LogFlowThisFunc(("\n"));
13209
13210 AutoCaller autoCaller(this);
13211 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13212
13213 ComPtr<IInternalSessionControl> directControl;
13214 {
13215 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13216 directControl = mData->mSession.mDirectControl;
13217 }
13218
13219 /* ignore notifications sent after #OnSessionEnd() is called */
13220 if (!directControl)
13221 return S_OK;
13222
13223 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13224}
13225
13226/**
13227 * @note Locks this object for reading.
13228 */
13229HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13230 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
13231{
13232 LogFlowThisFunc(("\n"));
13233
13234 AutoCaller autoCaller(this);
13235 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13236
13237 ComPtr<IInternalSessionControl> directControl;
13238 {
13239 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13240 directControl = mData->mSession.mDirectControl;
13241 }
13242
13243 /* ignore notifications sent after #OnSessionEnd() is called */
13244 if (!directControl)
13245 return S_OK;
13246 /*
13247 * instead acting like callback we ask IVirtualBox deliver corresponding event
13248 */
13249
13250 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13251 return S_OK;
13252}
13253
13254/**
13255 * @note Locks this object for reading.
13256 */
13257HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
13258{
13259 LogFlowThisFunc(("\n"));
13260
13261 AutoCaller autoCaller(this);
13262 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13263
13264 ComPtr<IInternalSessionControl> directControl;
13265 {
13266 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13267 directControl = mData->mSession.mDirectControl;
13268 }
13269
13270 /* ignore notifications sent after #OnSessionEnd() is called */
13271 if (!directControl)
13272 return S_OK;
13273
13274 return directControl->OnSerialPortChange(serialPort);
13275}
13276
13277/**
13278 * @note Locks this object for reading.
13279 */
13280HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
13281{
13282 LogFlowThisFunc(("\n"));
13283
13284 AutoCaller autoCaller(this);
13285 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13286
13287 ComPtr<IInternalSessionControl> directControl;
13288 {
13289 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13290 directControl = mData->mSession.mDirectControl;
13291 }
13292
13293 /* ignore notifications sent after #OnSessionEnd() is called */
13294 if (!directControl)
13295 return S_OK;
13296
13297 return directControl->OnParallelPortChange(parallelPort);
13298}
13299
13300/**
13301 * @note Locks this object for reading.
13302 */
13303HRESULT SessionMachine::onStorageControllerChange()
13304{
13305 LogFlowThisFunc(("\n"));
13306
13307 AutoCaller autoCaller(this);
13308 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13309
13310 ComPtr<IInternalSessionControl> directControl;
13311 {
13312 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13313 directControl = mData->mSession.mDirectControl;
13314 }
13315
13316 /* ignore notifications sent after #OnSessionEnd() is called */
13317 if (!directControl)
13318 return S_OK;
13319
13320 return directControl->OnStorageControllerChange();
13321}
13322
13323/**
13324 * @note Locks this object for reading.
13325 */
13326HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13327{
13328 LogFlowThisFunc(("\n"));
13329
13330 AutoCaller autoCaller(this);
13331 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13332
13333 ComPtr<IInternalSessionControl> directControl;
13334 {
13335 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13336 directControl = mData->mSession.mDirectControl;
13337 }
13338
13339 /* ignore notifications sent after #OnSessionEnd() is called */
13340 if (!directControl)
13341 return S_OK;
13342
13343 return directControl->OnMediumChange(aAttachment, aForce);
13344}
13345
13346/**
13347 * @note Locks this object for reading.
13348 */
13349HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
13350{
13351 LogFlowThisFunc(("\n"));
13352
13353 AutoCaller autoCaller(this);
13354 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13355
13356 ComPtr<IInternalSessionControl> directControl;
13357 {
13358 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13359 directControl = mData->mSession.mDirectControl;
13360 }
13361
13362 /* ignore notifications sent after #OnSessionEnd() is called */
13363 if (!directControl)
13364 return S_OK;
13365
13366 return directControl->OnCPUChange(aCPU, aRemove);
13367}
13368
13369HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
13370{
13371 LogFlowThisFunc(("\n"));
13372
13373 AutoCaller autoCaller(this);
13374 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13375
13376 ComPtr<IInternalSessionControl> directControl;
13377 {
13378 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13379 directControl = mData->mSession.mDirectControl;
13380 }
13381
13382 /* ignore notifications sent after #OnSessionEnd() is called */
13383 if (!directControl)
13384 return S_OK;
13385
13386 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13387}
13388
13389/**
13390 * @note Locks this object for reading.
13391 */
13392HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
13393{
13394 LogFlowThisFunc(("\n"));
13395
13396 AutoCaller autoCaller(this);
13397 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13398
13399 ComPtr<IInternalSessionControl> directControl;
13400 {
13401 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13402 directControl = mData->mSession.mDirectControl;
13403 }
13404
13405 /* ignore notifications sent after #OnSessionEnd() is called */
13406 if (!directControl)
13407 return S_OK;
13408
13409 return directControl->OnVRDEServerChange(aRestart);
13410}
13411
13412/**
13413 * @note Locks this object for reading.
13414 */
13415HRESULT SessionMachine::onUSBControllerChange()
13416{
13417 LogFlowThisFunc(("\n"));
13418
13419 AutoCaller autoCaller(this);
13420 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13421
13422 ComPtr<IInternalSessionControl> directControl;
13423 {
13424 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13425 directControl = mData->mSession.mDirectControl;
13426 }
13427
13428 /* ignore notifications sent after #OnSessionEnd() is called */
13429 if (!directControl)
13430 return S_OK;
13431
13432 return directControl->OnUSBControllerChange();
13433}
13434
13435/**
13436 * @note Locks this object for reading.
13437 */
13438HRESULT SessionMachine::onSharedFolderChange()
13439{
13440 LogFlowThisFunc(("\n"));
13441
13442 AutoCaller autoCaller(this);
13443 AssertComRCReturnRC(autoCaller.rc());
13444
13445 ComPtr<IInternalSessionControl> directControl;
13446 {
13447 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13448 directControl = mData->mSession.mDirectControl;
13449 }
13450
13451 /* ignore notifications sent after #OnSessionEnd() is called */
13452 if (!directControl)
13453 return S_OK;
13454
13455 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13456}
13457
13458/**
13459 * @note Locks this object for reading.
13460 */
13461HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
13462{
13463 LogFlowThisFunc(("\n"));
13464
13465 AutoCaller autoCaller(this);
13466 AssertComRCReturnRC(autoCaller.rc());
13467
13468 ComPtr<IInternalSessionControl> directControl;
13469 {
13470 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13471 directControl = mData->mSession.mDirectControl;
13472 }
13473
13474 /* ignore notifications sent after #OnSessionEnd() is called */
13475 if (!directControl)
13476 return S_OK;
13477
13478 return directControl->OnClipboardModeChange(aClipboardMode);
13479}
13480
13481/**
13482 * @note Locks this object for reading.
13483 */
13484HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
13485{
13486 LogFlowThisFunc(("\n"));
13487
13488 AutoCaller autoCaller(this);
13489 AssertComRCReturnRC(autoCaller.rc());
13490
13491 ComPtr<IInternalSessionControl> directControl;
13492 {
13493 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13494 directControl = mData->mSession.mDirectControl;
13495 }
13496
13497 /* ignore notifications sent after #OnSessionEnd() is called */
13498 if (!directControl)
13499 return S_OK;
13500
13501 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
13502}
13503
13504/**
13505 * @note Locks this object for reading.
13506 */
13507HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13508{
13509 LogFlowThisFunc(("\n"));
13510
13511 AutoCaller autoCaller(this);
13512 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13513
13514 ComPtr<IInternalSessionControl> directControl;
13515 {
13516 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13517 directControl = mData->mSession.mDirectControl;
13518 }
13519
13520 /* ignore notifications sent after #OnSessionEnd() is called */
13521 if (!directControl)
13522 return S_OK;
13523
13524 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13525}
13526
13527/**
13528 * @note Locks this object for reading.
13529 */
13530HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13531{
13532 LogFlowThisFunc(("\n"));
13533
13534 AutoCaller autoCaller(this);
13535 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13536
13537 ComPtr<IInternalSessionControl> directControl;
13538 {
13539 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13540 directControl = mData->mSession.mDirectControl;
13541 }
13542
13543 /* ignore notifications sent after #OnSessionEnd() is called */
13544 if (!directControl)
13545 return S_OK;
13546
13547 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13548}
13549
13550/**
13551 * Returns @c true if this machine's USB controller reports it has a matching
13552 * filter for the given USB device and @c false otherwise.
13553 *
13554 * @note locks this object for reading.
13555 */
13556bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13557{
13558 AutoCaller autoCaller(this);
13559 /* silently return if not ready -- this method may be called after the
13560 * direct machine session has been called */
13561 if (!autoCaller.isOk())
13562 return false;
13563
13564#ifdef VBOX_WITH_USB
13565 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13566
13567 switch (mData->mMachineState)
13568 {
13569 case MachineState_Starting:
13570 case MachineState_Restoring:
13571 case MachineState_TeleportingIn:
13572 case MachineState_Paused:
13573 case MachineState_Running:
13574 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13575 * elsewhere... */
13576 alock.release();
13577 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
13578 default: break;
13579 }
13580#else
13581 NOREF(aDevice);
13582 NOREF(aMaskedIfs);
13583#endif
13584 return false;
13585}
13586
13587/**
13588 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13589 */
13590HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
13591 IVirtualBoxErrorInfo *aError,
13592 ULONG aMaskedIfs)
13593{
13594 LogFlowThisFunc(("\n"));
13595
13596 AutoCaller autoCaller(this);
13597
13598 /* This notification may happen after the machine object has been
13599 * uninitialized (the session was closed), so don't assert. */
13600 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13601
13602 ComPtr<IInternalSessionControl> directControl;
13603 {
13604 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13605 directControl = mData->mSession.mDirectControl;
13606 }
13607
13608 /* fail on notifications sent after #OnSessionEnd() is called, it is
13609 * expected by the caller */
13610 if (!directControl)
13611 return E_FAIL;
13612
13613 /* No locks should be held at this point. */
13614 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13615 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13616
13617 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13618}
13619
13620/**
13621 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13622 */
13623HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
13624 IVirtualBoxErrorInfo *aError)
13625{
13626 LogFlowThisFunc(("\n"));
13627
13628 AutoCaller autoCaller(this);
13629
13630 /* This notification may happen after the machine object has been
13631 * uninitialized (the session was closed), so don't assert. */
13632 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13633
13634 ComPtr<IInternalSessionControl> directControl;
13635 {
13636 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13637 directControl = mData->mSession.mDirectControl;
13638 }
13639
13640 /* fail on notifications sent after #OnSessionEnd() is called, it is
13641 * expected by the caller */
13642 if (!directControl)
13643 return E_FAIL;
13644
13645 /* No locks should be held at this point. */
13646 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13647 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13648
13649 return directControl->OnUSBDeviceDetach(aId, aError);
13650}
13651
13652// protected methods
13653/////////////////////////////////////////////////////////////////////////////
13654
13655/**
13656 * Helper method to finalize saving the state.
13657 *
13658 * @note Must be called from under this object's lock.
13659 *
13660 * @param aRc S_OK if the snapshot has been taken successfully
13661 * @param aErrMsg human readable error message for failure
13662 *
13663 * @note Locks mParent + this objects for writing.
13664 */
13665HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13666{
13667 LogFlowThisFuncEnter();
13668
13669 AutoCaller autoCaller(this);
13670 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13671
13672 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13673
13674 HRESULT rc = S_OK;
13675
13676 if (SUCCEEDED(aRc))
13677 {
13678 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13679
13680 /* save all VM settings */
13681 rc = saveSettings(NULL);
13682 // no need to check whether VirtualBox.xml needs saving also since
13683 // we can't have a name change pending at this point
13684 }
13685 else
13686 {
13687 // delete the saved state file (it might have been already created);
13688 // we need not check whether this is shared with a snapshot here because
13689 // we certainly created this saved state file here anew
13690 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13691 }
13692
13693 /* notify the progress object about operation completion */
13694 Assert(mConsoleTaskData.mProgress);
13695 if (SUCCEEDED(aRc))
13696 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13697 else
13698 {
13699 if (aErrMsg.length())
13700 mConsoleTaskData.mProgress->notifyComplete(aRc,
13701 COM_IIDOF(ISession),
13702 getComponentName(),
13703 aErrMsg.c_str());
13704 else
13705 mConsoleTaskData.mProgress->notifyComplete(aRc);
13706 }
13707
13708 /* clear out the temporary saved state data */
13709 mConsoleTaskData.mLastState = MachineState_Null;
13710 mConsoleTaskData.strStateFilePath.setNull();
13711 mConsoleTaskData.mProgress.setNull();
13712
13713 LogFlowThisFuncLeave();
13714 return rc;
13715}
13716
13717/**
13718 * Deletes the given file if it is no longer in use by either the current machine state
13719 * (if the machine is "saved") or any of the machine's snapshots.
13720 *
13721 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13722 * but is different for each SnapshotMachine. When calling this, the order of calling this
13723 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13724 * is therefore critical. I know, it's all rather messy.
13725 *
13726 * @param strStateFile
13727 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
13728 */
13729void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
13730 Snapshot *pSnapshotToIgnore)
13731{
13732 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13733 if ( (strStateFile.isNotEmpty())
13734 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13735 )
13736 // ... and it must also not be shared with other snapshots
13737 if ( !mData->mFirstSnapshot
13738 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13739 // this checks the SnapshotMachine's state file paths
13740 )
13741 RTFileDelete(strStateFile.c_str());
13742}
13743
13744/**
13745 * Locks the attached media.
13746 *
13747 * All attached hard disks are locked for writing and DVD/floppy are locked for
13748 * reading. Parents of attached hard disks (if any) are locked for reading.
13749 *
13750 * This method also performs accessibility check of all media it locks: if some
13751 * media is inaccessible, the method will return a failure and a bunch of
13752 * extended error info objects per each inaccessible medium.
13753 *
13754 * Note that this method is atomic: if it returns a success, all media are
13755 * locked as described above; on failure no media is locked at all (all
13756 * succeeded individual locks will be undone).
13757 *
13758 * The caller is responsible for doing the necessary state sanity checks.
13759 *
13760 * The locks made by this method must be undone by calling #unlockMedia() when
13761 * no more needed.
13762 */
13763HRESULT SessionMachine::lockMedia()
13764{
13765 AutoCaller autoCaller(this);
13766 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13767
13768 AutoMultiWriteLock2 alock(this->lockHandle(),
13769 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13770
13771 /* bail out if trying to lock things with already set up locking */
13772 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13773
13774 MultiResult mrc(S_OK);
13775
13776 /* Collect locking information for all medium objects attached to the VM. */
13777 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13778 it != mMediaData->mAttachments.end();
13779 ++it)
13780 {
13781 MediumAttachment* pAtt = *it;
13782 DeviceType_T devType = pAtt->getType();
13783 Medium *pMedium = pAtt->getMedium();
13784
13785 MediumLockList *pMediumLockList(new MediumLockList());
13786 // There can be attachments without a medium (floppy/dvd), and thus
13787 // it's impossible to create a medium lock list. It still makes sense
13788 // to have the empty medium lock list in the map in case a medium is
13789 // attached later.
13790 if (pMedium != NULL)
13791 {
13792 MediumType_T mediumType = pMedium->getType();
13793 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13794 || mediumType == MediumType_Shareable;
13795 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13796
13797 alock.release();
13798 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13799 !fIsReadOnlyLock /* fMediumLockWrite */,
13800 NULL,
13801 *pMediumLockList);
13802 alock.acquire();
13803 if (FAILED(mrc))
13804 {
13805 delete pMediumLockList;
13806 mData->mSession.mLockedMedia.Clear();
13807 break;
13808 }
13809 }
13810
13811 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13812 if (FAILED(rc))
13813 {
13814 mData->mSession.mLockedMedia.Clear();
13815 mrc = setError(rc,
13816 tr("Collecting locking information for all attached media failed"));
13817 break;
13818 }
13819 }
13820
13821 if (SUCCEEDED(mrc))
13822 {
13823 /* Now lock all media. If this fails, nothing is locked. */
13824 alock.release();
13825 HRESULT rc = mData->mSession.mLockedMedia.Lock();
13826 alock.acquire();
13827 if (FAILED(rc))
13828 {
13829 mrc = setError(rc,
13830 tr("Locking of attached media failed"));
13831 }
13832 }
13833
13834 return mrc;
13835}
13836
13837/**
13838 * Undoes the locks made by by #lockMedia().
13839 */
13840void SessionMachine::unlockMedia()
13841{
13842 AutoCaller autoCaller(this);
13843 AssertComRCReturnVoid(autoCaller.rc());
13844
13845 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13846
13847 /* we may be holding important error info on the current thread;
13848 * preserve it */
13849 ErrorInfoKeeper eik;
13850
13851 HRESULT rc = mData->mSession.mLockedMedia.Clear();
13852 AssertComRC(rc);
13853}
13854
13855/**
13856 * Helper to change the machine state (reimplementation).
13857 *
13858 * @note Locks this object for writing.
13859 * @note This method must not call saveSettings or SaveSettings, otherwise
13860 * it can cause crashes in random places due to unexpectedly committing
13861 * the current settings. The caller is responsible for that. The call
13862 * to saveStateSettings is fine, because this method does not commit.
13863 */
13864HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
13865{
13866 LogFlowThisFuncEnter();
13867 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
13868
13869 AutoCaller autoCaller(this);
13870 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13871
13872 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13873
13874 MachineState_T oldMachineState = mData->mMachineState;
13875
13876 AssertMsgReturn(oldMachineState != aMachineState,
13877 ("oldMachineState=%s, aMachineState=%s\n",
13878 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
13879 E_FAIL);
13880
13881 HRESULT rc = S_OK;
13882
13883 int stsFlags = 0;
13884 bool deleteSavedState = false;
13885
13886 /* detect some state transitions */
13887
13888 if ( ( oldMachineState == MachineState_Saved
13889 && aMachineState == MachineState_Restoring)
13890 || ( ( oldMachineState == MachineState_PoweredOff
13891 || oldMachineState == MachineState_Teleported
13892 || oldMachineState == MachineState_Aborted
13893 )
13894 && ( aMachineState == MachineState_TeleportingIn
13895 || aMachineState == MachineState_Starting
13896 )
13897 )
13898 )
13899 {
13900 /* The EMT thread is about to start */
13901
13902 /* Nothing to do here for now... */
13903
13904 /// @todo NEWMEDIA don't let mDVDDrive and other children
13905 /// change anything when in the Starting/Restoring state
13906 }
13907 else if ( ( oldMachineState == MachineState_Running
13908 || oldMachineState == MachineState_Paused
13909 || oldMachineState == MachineState_Teleporting
13910 || oldMachineState == MachineState_LiveSnapshotting
13911 || oldMachineState == MachineState_Stuck
13912 || oldMachineState == MachineState_Starting
13913 || oldMachineState == MachineState_Stopping
13914 || oldMachineState == MachineState_Saving
13915 || oldMachineState == MachineState_Restoring
13916 || oldMachineState == MachineState_TeleportingPausedVM
13917 || oldMachineState == MachineState_TeleportingIn
13918 )
13919 && ( aMachineState == MachineState_PoweredOff
13920 || aMachineState == MachineState_Saved
13921 || aMachineState == MachineState_Teleported
13922 || aMachineState == MachineState_Aborted
13923 )
13924 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
13925 * snapshot */
13926 && ( mConsoleTaskData.mSnapshot.isNull()
13927 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
13928 )
13929 )
13930 {
13931 /* The EMT thread has just stopped, unlock attached media. Note that as
13932 * opposed to locking that is done from Console, we do unlocking here
13933 * because the VM process may have aborted before having a chance to
13934 * properly unlock all media it locked. */
13935
13936 unlockMedia();
13937 }
13938
13939 if (oldMachineState == MachineState_Restoring)
13940 {
13941 if (aMachineState != MachineState_Saved)
13942 {
13943 /*
13944 * delete the saved state file once the machine has finished
13945 * restoring from it (note that Console sets the state from
13946 * Restoring to Saved if the VM couldn't restore successfully,
13947 * to give the user an ability to fix an error and retry --
13948 * we keep the saved state file in this case)
13949 */
13950 deleteSavedState = true;
13951 }
13952 }
13953 else if ( oldMachineState == MachineState_Saved
13954 && ( aMachineState == MachineState_PoweredOff
13955 || aMachineState == MachineState_Aborted
13956 || aMachineState == MachineState_Teleported
13957 )
13958 )
13959 {
13960 /*
13961 * delete the saved state after Console::ForgetSavedState() is called
13962 * or if the VM process (owning a direct VM session) crashed while the
13963 * VM was Saved
13964 */
13965
13966 /// @todo (dmik)
13967 // Not sure that deleting the saved state file just because of the
13968 // client death before it attempted to restore the VM is a good
13969 // thing. But when it crashes we need to go to the Aborted state
13970 // which cannot have the saved state file associated... The only
13971 // way to fix this is to make the Aborted condition not a VM state
13972 // but a bool flag: i.e., when a crash occurs, set it to true and
13973 // change the state to PoweredOff or Saved depending on the
13974 // saved state presence.
13975
13976 deleteSavedState = true;
13977 mData->mCurrentStateModified = TRUE;
13978 stsFlags |= SaveSTS_CurStateModified;
13979 }
13980
13981 if ( aMachineState == MachineState_Starting
13982 || aMachineState == MachineState_Restoring
13983 || aMachineState == MachineState_TeleportingIn
13984 )
13985 {
13986 /* set the current state modified flag to indicate that the current
13987 * state is no more identical to the state in the
13988 * current snapshot */
13989 if (!mData->mCurrentSnapshot.isNull())
13990 {
13991 mData->mCurrentStateModified = TRUE;
13992 stsFlags |= SaveSTS_CurStateModified;
13993 }
13994 }
13995
13996 if (deleteSavedState)
13997 {
13998 if (mRemoveSavedState)
13999 {
14000 Assert(!mSSData->strStateFilePath.isEmpty());
14001
14002 // it is safe to delete the saved state file if ...
14003 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14004 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14005 // ... none of the snapshots share the saved state file
14006 )
14007 RTFileDelete(mSSData->strStateFilePath.c_str());
14008 }
14009
14010 mSSData->strStateFilePath.setNull();
14011 stsFlags |= SaveSTS_StateFilePath;
14012 }
14013
14014 /* redirect to the underlying peer machine */
14015 mPeer->setMachineState(aMachineState);
14016
14017 if ( aMachineState == MachineState_PoweredOff
14018 || aMachineState == MachineState_Teleported
14019 || aMachineState == MachineState_Aborted
14020 || aMachineState == MachineState_Saved)
14021 {
14022 /* the machine has stopped execution
14023 * (or the saved state file was adopted) */
14024 stsFlags |= SaveSTS_StateTimeStamp;
14025 }
14026
14027 if ( ( oldMachineState == MachineState_PoweredOff
14028 || oldMachineState == MachineState_Aborted
14029 || oldMachineState == MachineState_Teleported
14030 )
14031 && aMachineState == MachineState_Saved)
14032 {
14033 /* the saved state file was adopted */
14034 Assert(!mSSData->strStateFilePath.isEmpty());
14035 stsFlags |= SaveSTS_StateFilePath;
14036 }
14037
14038#ifdef VBOX_WITH_GUEST_PROPS
14039 if ( aMachineState == MachineState_PoweredOff
14040 || aMachineState == MachineState_Aborted
14041 || aMachineState == MachineState_Teleported)
14042 {
14043 /* Make sure any transient guest properties get removed from the
14044 * property store on shutdown. */
14045
14046 HWData::GuestPropertyMap::const_iterator it;
14047 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14048 if (!fNeedsSaving)
14049 for (it = mHWData->mGuestProperties.begin();
14050 it != mHWData->mGuestProperties.end(); ++it)
14051 if ( (it->second.mFlags & guestProp::TRANSIENT)
14052 || (it->second.mFlags & guestProp::TRANSRESET))
14053 {
14054 fNeedsSaving = true;
14055 break;
14056 }
14057 if (fNeedsSaving)
14058 {
14059 mData->mCurrentStateModified = TRUE;
14060 stsFlags |= SaveSTS_CurStateModified;
14061 }
14062 }
14063#endif
14064
14065 rc = saveStateSettings(stsFlags);
14066
14067 if ( ( oldMachineState != MachineState_PoweredOff
14068 && oldMachineState != MachineState_Aborted
14069 && oldMachineState != MachineState_Teleported
14070 )
14071 && ( aMachineState == MachineState_PoweredOff
14072 || aMachineState == MachineState_Aborted
14073 || aMachineState == MachineState_Teleported
14074 )
14075 )
14076 {
14077 /* we've been shut down for any reason */
14078 /* no special action so far */
14079 }
14080
14081 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14082 LogFlowThisFuncLeave();
14083 return rc;
14084}
14085
14086/**
14087 * Sends the current machine state value to the VM process.
14088 *
14089 * @note Locks this object for reading, then calls a client process.
14090 */
14091HRESULT SessionMachine::updateMachineStateOnClient()
14092{
14093 AutoCaller autoCaller(this);
14094 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14095
14096 ComPtr<IInternalSessionControl> directControl;
14097 {
14098 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14099 AssertReturn(!!mData, E_FAIL);
14100 directControl = mData->mSession.mDirectControl;
14101
14102 /* directControl may be already set to NULL here in #OnSessionEnd()
14103 * called too early by the direct session process while there is still
14104 * some operation (like deleting the snapshot) in progress. The client
14105 * process in this case is waiting inside Session::close() for the
14106 * "end session" process object to complete, while #uninit() called by
14107 * #checkForDeath() on the Watcher thread is waiting for the pending
14108 * operation to complete. For now, we accept this inconsistent behavior
14109 * and simply do nothing here. */
14110
14111 if (mData->mSession.mState == SessionState_Unlocking)
14112 return S_OK;
14113
14114 AssertReturn(!directControl.isNull(), E_FAIL);
14115 }
14116
14117 return directControl->UpdateMachineState(mData->mMachineState);
14118}
Note: See TracBrowser for help on using the repository browser.

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