VirtualBox

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

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

Main/Machine: fix initializer warning with recent gcc

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

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