VirtualBox

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

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

Main/VirtualBox+Machine+Snapshot+Medium: fix lock order issues introduced by new style of medium registry updates

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