VirtualBox

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

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

Main/Machine: Add a newly attached medium to the lcoked media map during hotplugging or when the expert storage mgmt mode is enabled

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