VirtualBox

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

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

Main: query the guest screen info from saved state (xTracker 5820)

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