VirtualBox

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

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

Main: Extended IMachine and the settings XML with three tracing related properties.

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