VirtualBox

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

Last change on this file since 41214 was 41214, checked in by vboxsync, 13 years ago

Main: move handleUnexpectedExceptions method to VirtualBoxBase

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