VirtualBox

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

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

Main/Machine+Snapshot: add a comment to make it easier to get rid of those time-consuming commits on startup one day

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

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