VirtualBox

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

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

Main/MachineImpl: coding style (spaces before and after '='); no C-style variable declaration

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 448.5 KB
Line 
1/* $Id: MachineImpl.cpp 42022 2012-07-05 06:43:16Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2012 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
27# include <errno.h>
28# include <sys/types.h>
29# include <sys/stat.h>
30# include <sys/ipc.h>
31# include <sys/sem.h>
32#endif
33
34#include "Logging.h"
35#include "VirtualBoxImpl.h"
36#include "MachineImpl.h"
37#include "ProgressImpl.h"
38#include "ProgressProxyImpl.h"
39#include "MediumAttachmentImpl.h"
40#include "MediumImpl.h"
41#include "MediumLock.h"
42#include "USBControllerImpl.h"
43#include "HostImpl.h"
44#include "SharedFolderImpl.h"
45#include "GuestOSTypeImpl.h"
46#include "VirtualBoxErrorInfoImpl.h"
47#include "GuestImpl.h"
48#include "StorageControllerImpl.h"
49#include "DisplayImpl.h"
50#include "DisplayUtils.h"
51#include "BandwidthControlImpl.h"
52#include "MachineImplCloneVM.h"
53#include "AutostartDb.h"
54
55// generated header
56#include "VBoxEvents.h"
57
58#ifdef VBOX_WITH_USB
59# include "USBProxyService.h"
60#endif
61
62#include "AutoCaller.h"
63#include "HashedPw.h"
64#include "Performance.h"
65
66#include <iprt/asm.h>
67#include <iprt/path.h>
68#include <iprt/dir.h>
69#include <iprt/env.h>
70#include <iprt/lockvalidator.h>
71#include <iprt/process.h>
72#include <iprt/cpp/utils.h>
73#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
74#include <iprt/sha.h>
75#include <iprt/string.h>
76
77#include <VBox/com/array.h>
78#include <VBox/com/list.h>
79
80#include <VBox/err.h>
81#include <VBox/param.h>
82#include <VBox/settings.h>
83#include <VBox/vmm/ssm.h>
84
85#ifdef VBOX_WITH_GUEST_PROPS
86# include <VBox/HostServices/GuestPropertySvc.h>
87# include <VBox/com/array.h>
88#endif
89
90#include "VBox/com/MultiResult.h"
91
92#include <algorithm>
93
94#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
95# define HOSTSUFF_EXE ".exe"
96#else /* !RT_OS_WINDOWS */
97# define HOSTSUFF_EXE ""
98#endif /* !RT_OS_WINDOWS */
99
100// defines / prototypes
101/////////////////////////////////////////////////////////////////////////////
102
103/////////////////////////////////////////////////////////////////////////////
104// Machine::Data structure
105/////////////////////////////////////////////////////////////////////////////
106
107Machine::Data::Data()
108{
109 mRegistered = FALSE;
110 pMachineConfigFile = NULL;
111 /* Contains hints on what has changed when the user is using the VM (config
112 * changes, running the VM, ...). This is used to decide if a config needs
113 * to be written to disk. */
114 flModifications = 0;
115 /* VM modification usually also trigger setting the current state to
116 * "Modified". Although this is not always the case. An e.g. is the VM
117 * initialization phase or when snapshot related data is changed. The
118 * actually behavior is controlled by the following flag. */
119 m_fAllowStateModification = false;
120 mAccessible = FALSE;
121 /* mUuid is initialized in Machine::init() */
122
123 mMachineState = MachineState_PoweredOff;
124 RTTimeNow(&mLastStateChange);
125
126 mMachineStateDeps = 0;
127 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
128 mMachineStateChangePending = 0;
129
130 mCurrentStateModified = TRUE;
131 mGuestPropertiesModified = FALSE;
132
133 mSession.mPid = NIL_RTPROCESS;
134 mSession.mState = SessionState_Unlocked;
135}
136
137Machine::Data::~Data()
138{
139 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
140 {
141 RTSemEventMultiDestroy(mMachineStateDepsSem);
142 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
143 }
144 if (pMachineConfigFile)
145 {
146 delete pMachineConfigFile;
147 pMachineConfigFile = NULL;
148 }
149}
150
151/////////////////////////////////////////////////////////////////////////////
152// Machine::HWData structure
153/////////////////////////////////////////////////////////////////////////////
154
155Machine::HWData::HWData()
156{
157 /* default values for a newly created machine */
158 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
159 mMemorySize = 128;
160 mCPUCount = 1;
161 mCPUHotPlugEnabled = false;
162 mMemoryBalloonSize = 0;
163 mPageFusionEnabled = false;
164 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 mHWVirtExEnabled = true;
169 mHWVirtExNestedPagingEnabled = true;
170#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
171 mHWVirtExLargePagesEnabled = true;
172#else
173 /* Not supported on 32 bits hosts. */
174 mHWVirtExLargePagesEnabled = false;
175#endif
176 mHWVirtExVPIDEnabled = true;
177 mHWVirtExForceEnabled = false;
178#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
179 mHWVirtExExclusive = false;
180#else
181 mHWVirtExExclusive = true;
182#endif
183#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
184 mPAEEnabled = true;
185#else
186 mPAEEnabled = false;
187#endif
188 mSyntheticCpu = false;
189 mHpetEnabled = false;
190
191 /* default boot order: floppy - DVD - HDD */
192 mBootOrder[0] = DeviceType_Floppy;
193 mBootOrder[1] = DeviceType_DVD;
194 mBootOrder[2] = DeviceType_HardDisk;
195 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
196 mBootOrder[i] = DeviceType_Null;
197
198 mClipboardMode = ClipboardMode_Disabled;
199 mGuestPropertyNotificationPatterns = "";
200
201 mFirmwareType = FirmwareType_BIOS;
202 mKeyboardHidType = KeyboardHidType_PS2Keyboard;
203 mPointingHidType = PointingHidType_PS2Mouse;
204 mChipsetType = ChipsetType_PIIX3;
205 mEmulatedUSBCardReaderEnabled = FALSE;
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 mSerialPorts(),
245 mParallelPorts(),
246 uRegistryNeedsSaving(0)
247{}
248
249Machine::~Machine()
250{}
251
252HRESULT Machine::FinalConstruct()
253{
254 LogFlowThisFunc(("\n"));
255 return BaseFinalConstruct();
256}
257
258void Machine::FinalRelease()
259{
260 LogFlowThisFunc(("\n"));
261 uninit();
262 BaseFinalRelease();
263}
264
265/**
266 * Initializes a new machine instance; this init() variant creates a new, empty machine.
267 * This gets called from VirtualBox::CreateMachine().
268 *
269 * @param aParent Associated parent object
270 * @param strConfigFile Local file system path to the VM settings file (can
271 * be relative to the VirtualBox config directory).
272 * @param strName name for the machine
273 * @param aId UUID for the new machine.
274 * @param aOsType OS Type of this machine or NULL.
275 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
276 *
277 * @return Success indicator. if not S_OK, the machine object is invalid
278 */
279HRESULT Machine::init(VirtualBox *aParent,
280 const Utf8Str &strConfigFile,
281 const Utf8Str &strName,
282 GuestOSType *aOsType,
283 const Guid &aId,
284 bool fForceOverwrite)
285{
286 LogFlowThisFuncEnter();
287 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
288
289 /* Enclose the state transition NotReady->InInit->Ready */
290 AutoInitSpan autoInitSpan(this);
291 AssertReturn(autoInitSpan.isOk(), E_FAIL);
292
293 HRESULT rc = initImpl(aParent, strConfigFile);
294 if (FAILED(rc)) return rc;
295
296 rc = tryCreateMachineConfigFile(fForceOverwrite);
297 if (FAILED(rc)) return rc;
298
299 if (SUCCEEDED(rc))
300 {
301 // create an empty machine config
302 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
303
304 rc = initDataAndChildObjects();
305 }
306
307 if (SUCCEEDED(rc))
308 {
309 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
310 mData->mAccessible = TRUE;
311
312 unconst(mData->mUuid) = aId;
313
314 mUserData->s.strName = strName;
315
316 // the "name sync" flag determines whether the machine directory gets renamed along
317 // with the machine file; say so if the settings file name is the same as the
318 // settings file parent directory (machine directory)
319 mUserData->s.fNameSync = isInOwnDir();
320
321 // initialize the default snapshots folder
322 rc = COMSETTER(SnapshotFolder)(NULL);
323 AssertComRC(rc);
324
325 if (aOsType)
326 {
327 /* Store OS type */
328 mUserData->s.strOsType = aOsType->id();
329
330 /* Apply BIOS defaults */
331 mBIOSSettings->applyDefaults(aOsType);
332
333 /* Apply network adapters defaults */
334 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
335 mNetworkAdapters[slot]->applyDefaults(aOsType);
336
337 /* Apply serial port defaults */
338 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
339 mSerialPorts[slot]->applyDefaults(aOsType);
340 }
341
342 /* At this point the changing of the current state modification
343 * flag is allowed. */
344 allowStateModification();
345
346 /* commit all changes made during the initialization */
347 commit();
348 }
349
350 /* Confirm a successful initialization when it's the case */
351 if (SUCCEEDED(rc))
352 {
353 if (mData->mAccessible)
354 autoInitSpan.setSucceeded();
355 else
356 autoInitSpan.setLimited();
357 }
358
359 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
360 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
361 mData->mRegistered,
362 mData->mAccessible,
363 rc));
364
365 LogFlowThisFuncLeave();
366
367 return rc;
368}
369
370/**
371 * Initializes a new instance with data from machine XML (formerly Init_Registered).
372 * Gets called in two modes:
373 *
374 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
375 * UUID is specified and we mark the machine as "registered";
376 *
377 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
378 * and the machine remains unregistered until RegisterMachine() is called.
379 *
380 * @param aParent Associated parent object
381 * @param aConfigFile Local file system path to the VM settings file (can
382 * be relative to the VirtualBox config directory).
383 * @param aId UUID of the machine or NULL (see above).
384 *
385 * @return Success indicator. if not S_OK, the machine object is invalid
386 */
387HRESULT Machine::init(VirtualBox *aParent,
388 const Utf8Str &strConfigFile,
389 const Guid *aId)
390{
391 LogFlowThisFuncEnter();
392 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
393
394 /* Enclose the state transition NotReady->InInit->Ready */
395 AutoInitSpan autoInitSpan(this);
396 AssertReturn(autoInitSpan.isOk(), E_FAIL);
397
398 HRESULT rc = initImpl(aParent, strConfigFile);
399 if (FAILED(rc)) return rc;
400
401 if (aId)
402 {
403 // loading a registered VM:
404 unconst(mData->mUuid) = *aId;
405 mData->mRegistered = TRUE;
406 // now load the settings from XML:
407 rc = registeredInit();
408 // this calls initDataAndChildObjects() and loadSettings()
409 }
410 else
411 {
412 // opening an unregistered VM (VirtualBox::OpenMachine()):
413 rc = initDataAndChildObjects();
414
415 if (SUCCEEDED(rc))
416 {
417 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
418 mData->mAccessible = TRUE;
419
420 try
421 {
422 // load and parse machine XML; this will throw on XML or logic errors
423 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
424
425 // reject VM UUID duplicates, they can happen if someone
426 // tries to register an already known VM config again
427 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
428 true /* fPermitInaccessible */,
429 false /* aDoSetError */,
430 NULL) != VBOX_E_OBJECT_NOT_FOUND)
431 {
432 throw setError(E_FAIL,
433 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
434 mData->m_strConfigFile.c_str());
435 }
436
437 // use UUID from machine config
438 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
439
440 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
441 NULL /* puuidRegistry */);
442 if (FAILED(rc)) throw rc;
443
444 /* At this point the changing of the current state modification
445 * flag is allowed. */
446 allowStateModification();
447
448 commit();
449 }
450 catch (HRESULT err)
451 {
452 /* we assume that error info is set by the thrower */
453 rc = err;
454 }
455 catch (...)
456 {
457 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
458 }
459 }
460 }
461
462 /* Confirm a successful initialization when it's the case */
463 if (SUCCEEDED(rc))
464 {
465 if (mData->mAccessible)
466 autoInitSpan.setSucceeded();
467 else
468 {
469 autoInitSpan.setLimited();
470
471 // uninit media from this machine's media registry, or else
472 // reloading the settings will fail
473 mParent->unregisterMachineMedia(getId());
474 }
475 }
476
477 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
478 "rc=%08X\n",
479 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
480 mData->mRegistered, mData->mAccessible, rc));
481
482 LogFlowThisFuncLeave();
483
484 return rc;
485}
486
487/**
488 * Initializes a new instance from a machine config that is already in memory
489 * (import OVF case). Since we are importing, the UUID in the machine
490 * config is ignored and we always generate a fresh one.
491 *
492 * @param strName Name for the new machine; this overrides what is specified in config and is used
493 * for the settings file as well.
494 * @param config Machine configuration loaded and parsed from XML.
495 *
496 * @return Success indicator. if not S_OK, the machine object is invalid
497 */
498HRESULT Machine::init(VirtualBox *aParent,
499 const Utf8Str &strName,
500 const settings::MachineConfigFile &config)
501{
502 LogFlowThisFuncEnter();
503
504 /* Enclose the state transition NotReady->InInit->Ready */
505 AutoInitSpan autoInitSpan(this);
506 AssertReturn(autoInitSpan.isOk(), E_FAIL);
507
508 Utf8Str strConfigFile;
509 aParent->getDefaultMachineFolder(strConfigFile);
510 strConfigFile.append(RTPATH_DELIMITER);
511 strConfigFile.append(strName);
512 strConfigFile.append(RTPATH_DELIMITER);
513 strConfigFile.append(strName);
514 strConfigFile.append(".vbox");
515
516 HRESULT rc = initImpl(aParent, strConfigFile);
517 if (FAILED(rc)) return rc;
518
519 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
520 if (FAILED(rc)) return rc;
521
522 rc = initDataAndChildObjects();
523
524 if (SUCCEEDED(rc))
525 {
526 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
527 mData->mAccessible = TRUE;
528
529 // create empty machine config for instance data
530 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
531
532 // generate fresh UUID, ignore machine config
533 unconst(mData->mUuid).create();
534
535 rc = loadMachineDataFromSettings(config,
536 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
537
538 // override VM name as well, it may be different
539 mUserData->s.strName = strName;
540
541 if (SUCCEEDED(rc))
542 {
543 /* At this point the changing of the current state modification
544 * flag is allowed. */
545 allowStateModification();
546
547 /* commit all changes made during the initialization */
548 commit();
549 }
550 }
551
552 /* Confirm a successful initialization when it's the case */
553 if (SUCCEEDED(rc))
554 {
555 if (mData->mAccessible)
556 autoInitSpan.setSucceeded();
557 else
558 {
559 autoInitSpan.setLimited();
560
561 // uninit media from this machine's media registry, or else
562 // reloading the settings will fail
563 mParent->unregisterMachineMedia(getId());
564 }
565 }
566
567 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
568 "rc=%08X\n",
569 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
570 mData->mRegistered, mData->mAccessible, rc));
571
572 LogFlowThisFuncLeave();
573
574 return rc;
575}
576
577/**
578 * Shared code between the various init() implementations.
579 * @param aParent
580 * @return
581 */
582HRESULT Machine::initImpl(VirtualBox *aParent,
583 const Utf8Str &strConfigFile)
584{
585 LogFlowThisFuncEnter();
586
587 AssertReturn(aParent, E_INVALIDARG);
588 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
589
590 HRESULT rc = S_OK;
591
592 /* share the parent weakly */
593 unconst(mParent) = aParent;
594
595 /* allocate the essential machine data structure (the rest will be
596 * allocated later by initDataAndChildObjects() */
597 mData.allocate();
598
599 /* memorize the config file name (as provided) */
600 mData->m_strConfigFile = strConfigFile;
601
602 /* get the full file name */
603 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
604 if (RT_FAILURE(vrc1))
605 return setError(VBOX_E_FILE_ERROR,
606 tr("Invalid machine settings file name '%s' (%Rrc)"),
607 strConfigFile.c_str(),
608 vrc1);
609
610 LogFlowThisFuncLeave();
611
612 return rc;
613}
614
615/**
616 * Tries to create a machine settings file in the path stored in the machine
617 * instance data. Used when a new machine is created to fail gracefully if
618 * the settings file could not be written (e.g. because machine dir is read-only).
619 * @return
620 */
621HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
622{
623 HRESULT rc = S_OK;
624
625 // when we create a new machine, we must be able to create the settings file
626 RTFILE f = NIL_RTFILE;
627 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
628 if ( RT_SUCCESS(vrc)
629 || vrc == VERR_SHARING_VIOLATION
630 )
631 {
632 if (RT_SUCCESS(vrc))
633 RTFileClose(f);
634 if (!fForceOverwrite)
635 rc = setError(VBOX_E_FILE_ERROR,
636 tr("Machine settings file '%s' already exists"),
637 mData->m_strConfigFileFull.c_str());
638 else
639 {
640 /* try to delete the config file, as otherwise the creation
641 * of a new settings file will fail. */
642 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
643 if (RT_FAILURE(vrc2))
644 rc = setError(VBOX_E_FILE_ERROR,
645 tr("Could not delete the existing settings file '%s' (%Rrc)"),
646 mData->m_strConfigFileFull.c_str(), vrc2);
647 }
648 }
649 else if ( vrc != VERR_FILE_NOT_FOUND
650 && vrc != VERR_PATH_NOT_FOUND
651 )
652 rc = setError(VBOX_E_FILE_ERROR,
653 tr("Invalid machine settings file name '%s' (%Rrc)"),
654 mData->m_strConfigFileFull.c_str(),
655 vrc);
656 return rc;
657}
658
659/**
660 * Initializes the registered machine by loading the settings file.
661 * This method is separated from #init() in order to make it possible to
662 * retry the operation after VirtualBox startup instead of refusing to
663 * startup the whole VirtualBox server in case if the settings file of some
664 * registered VM is invalid or inaccessible.
665 *
666 * @note Must be always called from this object's write lock
667 * (unless called from #init() that doesn't need any locking).
668 * @note Locks the mUSBController method for writing.
669 * @note Subclasses must not call this method.
670 */
671HRESULT Machine::registeredInit()
672{
673 AssertReturn(!isSessionMachine(), E_FAIL);
674 AssertReturn(!isSnapshotMachine(), E_FAIL);
675 AssertReturn(!mData->mUuid.isEmpty(), E_FAIL);
676 AssertReturn(!mData->mAccessible, E_FAIL);
677
678 HRESULT rc = initDataAndChildObjects();
679
680 if (SUCCEEDED(rc))
681 {
682 /* Temporarily reset the registered flag in order to let setters
683 * potentially called from loadSettings() succeed (isMutable() used in
684 * all setters will return FALSE for a Machine instance if mRegistered
685 * is TRUE). */
686 mData->mRegistered = FALSE;
687
688 try
689 {
690 // load and parse machine XML; this will throw on XML or logic errors
691 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
692
693 if (mData->mUuid != mData->pMachineConfigFile->uuid)
694 throw setError(E_FAIL,
695 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
696 mData->pMachineConfigFile->uuid.raw(),
697 mData->m_strConfigFileFull.c_str(),
698 mData->mUuid.toString().c_str(),
699 mParent->settingsFilePath().c_str());
700
701 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
702 NULL /* const Guid *puuidRegistry */);
703 if (FAILED(rc)) throw rc;
704 }
705 catch (HRESULT err)
706 {
707 /* we assume that error info is set by the thrower */
708 rc = err;
709 }
710 catch (...)
711 {
712 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
713 }
714
715 /* Restore the registered flag (even on failure) */
716 mData->mRegistered = TRUE;
717 }
718
719 if (SUCCEEDED(rc))
720 {
721 /* Set mAccessible to TRUE only if we successfully locked and loaded
722 * the settings file */
723 mData->mAccessible = TRUE;
724
725 /* commit all changes made during loading the settings file */
726 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
727 /// @todo r=klaus for some reason the settings loading logic backs up
728 // the settings, and therefore a commit is needed. Should probably be changed.
729 }
730 else
731 {
732 /* If the machine is registered, then, instead of returning a
733 * failure, we mark it as inaccessible and set the result to
734 * success to give it a try later */
735
736 /* fetch the current error info */
737 mData->mAccessError = com::ErrorInfo();
738 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
739 mData->mUuid.raw(),
740 mData->mAccessError.getText().raw()));
741
742 /* rollback all changes */
743 rollback(false /* aNotify */);
744
745 // uninit media from this machine's media registry, or else
746 // reloading the settings will fail
747 mParent->unregisterMachineMedia(getId());
748
749 /* uninitialize the common part to make sure all data is reset to
750 * default (null) values */
751 uninitDataAndChildObjects();
752
753 rc = S_OK;
754 }
755
756 return rc;
757}
758
759/**
760 * Uninitializes the instance.
761 * Called either from FinalRelease() or by the parent when it gets destroyed.
762 *
763 * @note The caller of this method must make sure that this object
764 * a) doesn't have active callers on the current thread and b) is not locked
765 * by the current thread; otherwise uninit() will hang either a) due to
766 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
767 * a dead-lock caused by this thread waiting for all callers on the other
768 * threads are done but preventing them from doing so by holding a lock.
769 */
770void Machine::uninit()
771{
772 LogFlowThisFuncEnter();
773
774 Assert(!isWriteLockOnCurrentThread());
775
776 Assert(!uRegistryNeedsSaving);
777 if (uRegistryNeedsSaving)
778 {
779 AutoCaller autoCaller(this);
780 if (SUCCEEDED(autoCaller.rc()))
781 {
782 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
783 saveSettings(NULL, Machine::SaveS_Force);
784 }
785 }
786
787 /* Enclose the state transition Ready->InUninit->NotReady */
788 AutoUninitSpan autoUninitSpan(this);
789 if (autoUninitSpan.uninitDone())
790 return;
791
792 Assert(!isSnapshotMachine());
793 Assert(!isSessionMachine());
794 Assert(!!mData);
795
796 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
797 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
798
799 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
800
801 if (!mData->mSession.mMachine.isNull())
802 {
803 /* Theoretically, this can only happen if the VirtualBox server has been
804 * terminated while there were clients running that owned open direct
805 * sessions. Since in this case we are definitely called by
806 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
807 * won't happen on the client watcher thread (because it does
808 * VirtualBox::addCaller() for the duration of the
809 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
810 * cannot happen until the VirtualBox caller is released). This is
811 * important, because SessionMachine::uninit() cannot correctly operate
812 * after we return from this method (it expects the Machine instance is
813 * still valid). We'll call it ourselves below.
814 */
815 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
816 (SessionMachine*)mData->mSession.mMachine));
817
818 if (Global::IsOnlineOrTransient(mData->mMachineState))
819 {
820 LogWarningThisFunc(("Setting state to Aborted!\n"));
821 /* set machine state using SessionMachine reimplementation */
822 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
823 }
824
825 /*
826 * Uninitialize SessionMachine using public uninit() to indicate
827 * an unexpected uninitialization.
828 */
829 mData->mSession.mMachine->uninit();
830 /* SessionMachine::uninit() must set mSession.mMachine to null */
831 Assert(mData->mSession.mMachine.isNull());
832 }
833
834 // uninit media from this machine's media registry, if they're still there
835 Guid uuidMachine(getId());
836
837 /* XXX This will fail with
838 * "cannot be closed because it is still attached to 1 virtual machines"
839 * because at this point we did not call uninitDataAndChildObjects() yet
840 * and therefore also removeBackReference() for all these mediums was not called! */
841 if (!uuidMachine.isEmpty()) // can be empty if we're called from a failure of Machine::init
842 mParent->unregisterMachineMedia(uuidMachine);
843
844 /* the lock is no more necessary (SessionMachine is uninitialized) */
845 alock.release();
846
847 // has machine been modified?
848 if (mData->flModifications)
849 {
850 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
851 rollback(false /* aNotify */);
852 }
853
854 if (mData->mAccessible)
855 uninitDataAndChildObjects();
856
857 /* free the essential data structure last */
858 mData.free();
859
860 LogFlowThisFuncLeave();
861}
862
863// IMachine properties
864/////////////////////////////////////////////////////////////////////////////
865
866STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
867{
868 CheckComArgOutPointerValid(aParent);
869
870 AutoLimitedCaller autoCaller(this);
871 if (FAILED(autoCaller.rc())) return autoCaller.rc();
872
873 /* mParent is constant during life time, no need to lock */
874 ComObjPtr<VirtualBox> pVirtualBox(mParent);
875 pVirtualBox.queryInterfaceTo(aParent);
876
877 return S_OK;
878}
879
880STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
881{
882 CheckComArgOutPointerValid(aAccessible);
883
884 AutoLimitedCaller autoCaller(this);
885 if (FAILED(autoCaller.rc())) return autoCaller.rc();
886
887 LogFlowThisFunc(("ENTER\n"));
888
889 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
890
891 HRESULT rc = S_OK;
892
893 if (!mData->mAccessible)
894 {
895 /* try to initialize the VM once more if not accessible */
896
897 AutoReinitSpan autoReinitSpan(this);
898 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
899
900#ifdef DEBUG
901 LogFlowThisFunc(("Dumping media backreferences\n"));
902 mParent->dumpAllBackRefs();
903#endif
904
905 if (mData->pMachineConfigFile)
906 {
907 // reset the XML file to force loadSettings() (called from registeredInit())
908 // to parse it again; the file might have changed
909 delete mData->pMachineConfigFile;
910 mData->pMachineConfigFile = NULL;
911 }
912
913 rc = registeredInit();
914
915 if (SUCCEEDED(rc) && mData->mAccessible)
916 {
917 autoReinitSpan.setSucceeded();
918
919 /* make sure interesting parties will notice the accessibility
920 * state change */
921 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
922 mParent->onMachineDataChange(mData->mUuid);
923 }
924 }
925
926 if (SUCCEEDED(rc))
927 *aAccessible = mData->mAccessible;
928
929 LogFlowThisFuncLeave();
930
931 return rc;
932}
933
934STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
935{
936 CheckComArgOutPointerValid(aAccessError);
937
938 AutoLimitedCaller autoCaller(this);
939 if (FAILED(autoCaller.rc())) return autoCaller.rc();
940
941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
942
943 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
944 {
945 /* return shortly */
946 aAccessError = NULL;
947 return S_OK;
948 }
949
950 HRESULT rc = S_OK;
951
952 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
953 rc = errorInfo.createObject();
954 if (SUCCEEDED(rc))
955 {
956 errorInfo->init(mData->mAccessError.getResultCode(),
957 mData->mAccessError.getInterfaceID().ref(),
958 Utf8Str(mData->mAccessError.getComponent()).c_str(),
959 Utf8Str(mData->mAccessError.getText()));
960 rc = errorInfo.queryInterfaceTo(aAccessError);
961 }
962
963 return rc;
964}
965
966STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
967{
968 CheckComArgOutPointerValid(aName);
969
970 AutoCaller autoCaller(this);
971 if (FAILED(autoCaller.rc())) return autoCaller.rc();
972
973 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
974
975 mUserData->s.strName.cloneTo(aName);
976
977 return S_OK;
978}
979
980STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
981{
982 CheckComArgStrNotEmptyOrNull(aName);
983
984 AutoCaller autoCaller(this);
985 if (FAILED(autoCaller.rc())) return autoCaller.rc();
986
987 // prohibit setting a UUID only as the machine name, or else it can
988 // never be found by findMachine()
989 Guid test(aName);
990 if (test.isNotEmpty())
991 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
992
993 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
994
995 HRESULT rc = checkStateDependency(MutableStateDep);
996 if (FAILED(rc)) return rc;
997
998 setModified(IsModified_MachineData);
999 mUserData.backup();
1000 mUserData->s.strName = aName;
1001
1002 return S_OK;
1003}
1004
1005STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1006{
1007 CheckComArgOutPointerValid(aDescription);
1008
1009 AutoCaller autoCaller(this);
1010 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1011
1012 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1013
1014 mUserData->s.strDescription.cloneTo(aDescription);
1015
1016 return S_OK;
1017}
1018
1019STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1020{
1021 AutoCaller autoCaller(this);
1022 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1023
1024 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1025
1026 HRESULT rc = checkStateDependency(MutableStateDep);
1027 if (FAILED(rc)) return rc;
1028
1029 setModified(IsModified_MachineData);
1030 mUserData.backup();
1031 mUserData->s.strDescription = aDescription;
1032
1033 return S_OK;
1034}
1035
1036STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1037{
1038 CheckComArgOutPointerValid(aId);
1039
1040 AutoLimitedCaller autoCaller(this);
1041 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1042
1043 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1044
1045 mData->mUuid.toUtf16().cloneTo(aId);
1046
1047 return S_OK;
1048}
1049
1050STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1051{
1052 CheckComArgOutPointerValid(aOSTypeId);
1053
1054 AutoCaller autoCaller(this);
1055 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1056
1057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1058
1059 mUserData->s.strOsType.cloneTo(aOSTypeId);
1060
1061 return S_OK;
1062}
1063
1064STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1065{
1066 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1067
1068 AutoCaller autoCaller(this);
1069 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1070
1071 /* look up the object by Id to check it is valid */
1072 ComPtr<IGuestOSType> guestOSType;
1073 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1074 if (FAILED(rc)) return rc;
1075
1076 /* when setting, always use the "etalon" value for consistency -- lookup
1077 * by ID is case-insensitive and the input value may have different case */
1078 Bstr osTypeId;
1079 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1080 if (FAILED(rc)) return rc;
1081
1082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1083
1084 rc = checkStateDependency(MutableStateDep);
1085 if (FAILED(rc)) return rc;
1086
1087 setModified(IsModified_MachineData);
1088 mUserData.backup();
1089 mUserData->s.strOsType = osTypeId;
1090
1091 return S_OK;
1092}
1093
1094
1095STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1096{
1097 CheckComArgOutPointerValid(aFirmwareType);
1098
1099 AutoCaller autoCaller(this);
1100 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1101
1102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1103
1104 *aFirmwareType = mHWData->mFirmwareType;
1105
1106 return S_OK;
1107}
1108
1109STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1110{
1111 AutoCaller autoCaller(this);
1112 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1113 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1114
1115 int rc = checkStateDependency(MutableStateDep);
1116 if (FAILED(rc)) return rc;
1117
1118 setModified(IsModified_MachineData);
1119 mHWData.backup();
1120 mHWData->mFirmwareType = aFirmwareType;
1121
1122 return S_OK;
1123}
1124
1125STDMETHODIMP Machine::COMGETTER(KeyboardHidType)(KeyboardHidType_T *aKeyboardHidType)
1126{
1127 CheckComArgOutPointerValid(aKeyboardHidType);
1128
1129 AutoCaller autoCaller(this);
1130 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1131
1132 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1133
1134 *aKeyboardHidType = mHWData->mKeyboardHidType;
1135
1136 return S_OK;
1137}
1138
1139STDMETHODIMP Machine::COMSETTER(KeyboardHidType)(KeyboardHidType_T aKeyboardHidType)
1140{
1141 AutoCaller autoCaller(this);
1142 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1143 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1144
1145 int rc = checkStateDependency(MutableStateDep);
1146 if (FAILED(rc)) return rc;
1147
1148 setModified(IsModified_MachineData);
1149 mHWData.backup();
1150 mHWData->mKeyboardHidType = aKeyboardHidType;
1151
1152 return S_OK;
1153}
1154
1155STDMETHODIMP Machine::COMGETTER(PointingHidType)(PointingHidType_T *aPointingHidType)
1156{
1157 CheckComArgOutPointerValid(aPointingHidType);
1158
1159 AutoCaller autoCaller(this);
1160 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1161
1162 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1163
1164 *aPointingHidType = mHWData->mPointingHidType;
1165
1166 return S_OK;
1167}
1168
1169STDMETHODIMP Machine::COMSETTER(PointingHidType)(PointingHidType_T aPointingHidType)
1170{
1171 AutoCaller autoCaller(this);
1172 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1173 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1174
1175 int rc = checkStateDependency(MutableStateDep);
1176 if (FAILED(rc)) return rc;
1177
1178 setModified(IsModified_MachineData);
1179 mHWData.backup();
1180 mHWData->mPointingHidType = aPointingHidType;
1181
1182 return S_OK;
1183}
1184
1185STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1186{
1187 CheckComArgOutPointerValid(aChipsetType);
1188
1189 AutoCaller autoCaller(this);
1190 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1191
1192 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1193
1194 *aChipsetType = mHWData->mChipsetType;
1195
1196 return S_OK;
1197}
1198
1199STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1200{
1201 AutoCaller autoCaller(this);
1202 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1203 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1204
1205 int rc = checkStateDependency(MutableStateDep);
1206 if (FAILED(rc)) return rc;
1207
1208 if (aChipsetType != mHWData->mChipsetType)
1209 {
1210 setModified(IsModified_MachineData);
1211 mHWData.backup();
1212 mHWData->mChipsetType = aChipsetType;
1213
1214 // Resize network adapter array, to be finalized on commit/rollback.
1215 // We must not throw away entries yet, otherwise settings are lost
1216 // without a way to roll back.
1217 uint32_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1218 uint32_t oldCount = mNetworkAdapters.size();
1219 if (newCount > oldCount)
1220 {
1221 mNetworkAdapters.resize(newCount);
1222 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1223 {
1224 unconst(mNetworkAdapters[slot]).createObject();
1225 mNetworkAdapters[slot]->init(this, slot);
1226 }
1227 }
1228 }
1229
1230 return S_OK;
1231}
1232
1233STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1234{
1235 if (!aHWVersion)
1236 return E_POINTER;
1237
1238 AutoCaller autoCaller(this);
1239 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1240
1241 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1242
1243 mHWData->mHWVersion.cloneTo(aHWVersion);
1244
1245 return S_OK;
1246}
1247
1248STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1249{
1250 /* check known version */
1251 Utf8Str hwVersion = aHWVersion;
1252 if ( hwVersion.compare("1") != 0
1253 && hwVersion.compare("2") != 0)
1254 return setError(E_INVALIDARG,
1255 tr("Invalid hardware version: %ls\n"), aHWVersion);
1256
1257 AutoCaller autoCaller(this);
1258 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1259
1260 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1261
1262 HRESULT rc = checkStateDependency(MutableStateDep);
1263 if (FAILED(rc)) return rc;
1264
1265 setModified(IsModified_MachineData);
1266 mHWData.backup();
1267 mHWData->mHWVersion = hwVersion;
1268
1269 return S_OK;
1270}
1271
1272STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1273{
1274 CheckComArgOutPointerValid(aUUID);
1275
1276 AutoCaller autoCaller(this);
1277 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1278
1279 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1280
1281 if (!mHWData->mHardwareUUID.isEmpty())
1282 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1283 else
1284 mData->mUuid.toUtf16().cloneTo(aUUID);
1285
1286 return S_OK;
1287}
1288
1289STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1290{
1291 Guid hardwareUUID(aUUID);
1292 if (hardwareUUID.isEmpty())
1293 return E_INVALIDARG;
1294
1295 AutoCaller autoCaller(this);
1296 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1297
1298 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1299
1300 HRESULT rc = checkStateDependency(MutableStateDep);
1301 if (FAILED(rc)) return rc;
1302
1303 setModified(IsModified_MachineData);
1304 mHWData.backup();
1305 if (hardwareUUID == mData->mUuid)
1306 mHWData->mHardwareUUID.clear();
1307 else
1308 mHWData->mHardwareUUID = hardwareUUID;
1309
1310 return S_OK;
1311}
1312
1313STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1314{
1315 if (!memorySize)
1316 return E_POINTER;
1317
1318 AutoCaller autoCaller(this);
1319 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1320
1321 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1322
1323 *memorySize = mHWData->mMemorySize;
1324
1325 return S_OK;
1326}
1327
1328STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1329{
1330 /* check RAM limits */
1331 if ( memorySize < MM_RAM_MIN_IN_MB
1332 || memorySize > MM_RAM_MAX_IN_MB
1333 )
1334 return setError(E_INVALIDARG,
1335 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1336 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1337
1338 AutoCaller autoCaller(this);
1339 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1340
1341 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1342
1343 HRESULT rc = checkStateDependency(MutableStateDep);
1344 if (FAILED(rc)) return rc;
1345
1346 setModified(IsModified_MachineData);
1347 mHWData.backup();
1348 mHWData->mMemorySize = memorySize;
1349
1350 return S_OK;
1351}
1352
1353STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1354{
1355 if (!CPUCount)
1356 return E_POINTER;
1357
1358 AutoCaller autoCaller(this);
1359 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1360
1361 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1362
1363 *CPUCount = mHWData->mCPUCount;
1364
1365 return S_OK;
1366}
1367
1368STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1369{
1370 /* check CPU limits */
1371 if ( CPUCount < SchemaDefs::MinCPUCount
1372 || CPUCount > SchemaDefs::MaxCPUCount
1373 )
1374 return setError(E_INVALIDARG,
1375 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1376 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1377
1378 AutoCaller autoCaller(this);
1379 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1380
1381 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1382
1383 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1384 if (mHWData->mCPUHotPlugEnabled)
1385 {
1386 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1387 {
1388 if (mHWData->mCPUAttached[idx])
1389 return setError(E_INVALIDARG,
1390 tr("There is still a CPU attached to socket %lu."
1391 "Detach the CPU before removing the socket"),
1392 CPUCount, idx+1);
1393 }
1394 }
1395
1396 HRESULT rc = checkStateDependency(MutableStateDep);
1397 if (FAILED(rc)) return rc;
1398
1399 setModified(IsModified_MachineData);
1400 mHWData.backup();
1401 mHWData->mCPUCount = CPUCount;
1402
1403 return S_OK;
1404}
1405
1406STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1407{
1408 if (!aExecutionCap)
1409 return E_POINTER;
1410
1411 AutoCaller autoCaller(this);
1412 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1413
1414 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1415
1416 *aExecutionCap = mHWData->mCpuExecutionCap;
1417
1418 return S_OK;
1419}
1420
1421STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1422{
1423 HRESULT rc = S_OK;
1424
1425 /* check throttle limits */
1426 if ( aExecutionCap < 1
1427 || aExecutionCap > 100
1428 )
1429 return setError(E_INVALIDARG,
1430 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1431 aExecutionCap, 1, 100);
1432
1433 AutoCaller autoCaller(this);
1434 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1435
1436 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1437
1438 alock.release();
1439 rc = onCPUExecutionCapChange(aExecutionCap);
1440 alock.acquire();
1441 if (FAILED(rc)) return rc;
1442
1443 setModified(IsModified_MachineData);
1444 mHWData.backup();
1445 mHWData->mCpuExecutionCap = aExecutionCap;
1446
1447 /* Save settings if online - todo why is this required?? */
1448 if (Global::IsOnline(mData->mMachineState))
1449 saveSettings(NULL);
1450
1451 return S_OK;
1452}
1453
1454
1455STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *enabled)
1456{
1457 if (!enabled)
1458 return E_POINTER;
1459
1460 AutoCaller autoCaller(this);
1461 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1462
1463 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1464
1465 *enabled = mHWData->mCPUHotPlugEnabled;
1466
1467 return S_OK;
1468}
1469
1470STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled)
1471{
1472 HRESULT rc = S_OK;
1473
1474 AutoCaller autoCaller(this);
1475 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1476
1477 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1478
1479 rc = checkStateDependency(MutableStateDep);
1480 if (FAILED(rc)) return rc;
1481
1482 if (mHWData->mCPUHotPlugEnabled != enabled)
1483 {
1484 if (enabled)
1485 {
1486 setModified(IsModified_MachineData);
1487 mHWData.backup();
1488
1489 /* Add the amount of CPUs currently attached */
1490 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1491 {
1492 mHWData->mCPUAttached[i] = true;
1493 }
1494 }
1495 else
1496 {
1497 /*
1498 * We can disable hotplug only if the amount of maximum CPUs is equal
1499 * to the amount of attached CPUs
1500 */
1501 unsigned cCpusAttached = 0;
1502 unsigned iHighestId = 0;
1503
1504 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1505 {
1506 if (mHWData->mCPUAttached[i])
1507 {
1508 cCpusAttached++;
1509 iHighestId = i;
1510 }
1511 }
1512
1513 if ( (cCpusAttached != mHWData->mCPUCount)
1514 || (iHighestId >= mHWData->mCPUCount))
1515 return setError(E_INVALIDARG,
1516 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1517
1518 setModified(IsModified_MachineData);
1519 mHWData.backup();
1520 }
1521 }
1522
1523 mHWData->mCPUHotPlugEnabled = enabled;
1524
1525 return rc;
1526}
1527
1528STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *enabled)
1529{
1530#ifdef VBOX_WITH_USB_CARDREADER
1531 CheckComArgOutPointerValid(enabled);
1532
1533 AutoCaller autoCaller(this);
1534 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1535
1536 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1537
1538 *enabled = mHWData->mEmulatedUSBCardReaderEnabled;
1539
1540 return S_OK;
1541#else
1542 NOREF(enabled);
1543 return E_NOTIMPL;
1544#endif
1545}
1546
1547STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL enabled)
1548{
1549#ifdef VBOX_WITH_USB_CARDREADER
1550 AutoCaller autoCaller(this);
1551 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1552 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1553
1554 int rc = checkStateDependency(MutableStateDep);
1555 if (FAILED(rc)) return rc;
1556
1557 setModified(IsModified_MachineData);
1558 mHWData.backup();
1559 mHWData->mEmulatedUSBCardReaderEnabled = enabled;
1560
1561 return S_OK;
1562#else
1563 NOREF(enabled);
1564 return E_NOTIMPL;
1565#endif
1566}
1567
1568STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *enabled)
1569{
1570 NOREF(enabled);
1571 return E_NOTIMPL;
1572}
1573
1574STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL enabled)
1575{
1576 NOREF(enabled);
1577 return E_NOTIMPL;
1578}
1579
1580STDMETHODIMP Machine::COMGETTER(HpetEnabled)(BOOL *enabled)
1581{
1582 CheckComArgOutPointerValid(enabled);
1583
1584 AutoCaller autoCaller(this);
1585 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1586 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1587
1588 *enabled = mHWData->mHpetEnabled;
1589
1590 return S_OK;
1591}
1592
1593STDMETHODIMP Machine::COMSETTER(HpetEnabled)(BOOL enabled)
1594{
1595 HRESULT rc = S_OK;
1596
1597 AutoCaller autoCaller(this);
1598 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1599 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1600
1601 rc = checkStateDependency(MutableStateDep);
1602 if (FAILED(rc)) return rc;
1603
1604 setModified(IsModified_MachineData);
1605 mHWData.backup();
1606
1607 mHWData->mHpetEnabled = enabled;
1608
1609 return rc;
1610}
1611
1612STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1613{
1614 if (!memorySize)
1615 return E_POINTER;
1616
1617 AutoCaller autoCaller(this);
1618 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1619
1620 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1621
1622 *memorySize = mHWData->mVRAMSize;
1623
1624 return S_OK;
1625}
1626
1627STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1628{
1629 /* check VRAM limits */
1630 if (memorySize < SchemaDefs::MinGuestVRAM ||
1631 memorySize > SchemaDefs::MaxGuestVRAM)
1632 return setError(E_INVALIDARG,
1633 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1634 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1635
1636 AutoCaller autoCaller(this);
1637 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1638
1639 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1640
1641 HRESULT rc = checkStateDependency(MutableStateDep);
1642 if (FAILED(rc)) return rc;
1643
1644 setModified(IsModified_MachineData);
1645 mHWData.backup();
1646 mHWData->mVRAMSize = memorySize;
1647
1648 return S_OK;
1649}
1650
1651/** @todo this method should not be public */
1652STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1653{
1654 if (!memoryBalloonSize)
1655 return E_POINTER;
1656
1657 AutoCaller autoCaller(this);
1658 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1659
1660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1661
1662 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
1663
1664 return S_OK;
1665}
1666
1667/**
1668 * Set the memory balloon size.
1669 *
1670 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1671 * we have to make sure that we never call IGuest from here.
1672 */
1673STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
1674{
1675 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1676#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1677 /* check limits */
1678 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1679 return setError(E_INVALIDARG,
1680 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1681 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1682
1683 AutoCaller autoCaller(this);
1684 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1685
1686 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1687
1688 setModified(IsModified_MachineData);
1689 mHWData.backup();
1690 mHWData->mMemoryBalloonSize = memoryBalloonSize;
1691
1692 return S_OK;
1693#else
1694 NOREF(memoryBalloonSize);
1695 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1696#endif
1697}
1698
1699STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *enabled)
1700{
1701 if (!enabled)
1702 return E_POINTER;
1703
1704 AutoCaller autoCaller(this);
1705 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1706
1707 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1708
1709 *enabled = mHWData->mPageFusionEnabled;
1710 return S_OK;
1711}
1712
1713STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL enabled)
1714{
1715#ifdef VBOX_WITH_PAGE_SHARING
1716 AutoCaller autoCaller(this);
1717 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1718
1719 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1720
1721 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1722 setModified(IsModified_MachineData);
1723 mHWData.backup();
1724 mHWData->mPageFusionEnabled = enabled;
1725 return S_OK;
1726#else
1727 NOREF(enabled);
1728 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1729#endif
1730}
1731
1732STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *enabled)
1733{
1734 if (!enabled)
1735 return E_POINTER;
1736
1737 AutoCaller autoCaller(this);
1738 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1739
1740 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1741
1742 *enabled = mHWData->mAccelerate3DEnabled;
1743
1744 return S_OK;
1745}
1746
1747STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
1748{
1749 AutoCaller autoCaller(this);
1750 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1751
1752 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1753
1754 HRESULT rc = checkStateDependency(MutableStateDep);
1755 if (FAILED(rc)) return rc;
1756
1757 /** @todo check validity! */
1758
1759 setModified(IsModified_MachineData);
1760 mHWData.backup();
1761 mHWData->mAccelerate3DEnabled = enable;
1762
1763 return S_OK;
1764}
1765
1766
1767STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *enabled)
1768{
1769 if (!enabled)
1770 return E_POINTER;
1771
1772 AutoCaller autoCaller(this);
1773 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1774
1775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1776
1777 *enabled = mHWData->mAccelerate2DVideoEnabled;
1778
1779 return S_OK;
1780}
1781
1782STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
1783{
1784 AutoCaller autoCaller(this);
1785 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1786
1787 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1788
1789 HRESULT rc = checkStateDependency(MutableStateDep);
1790 if (FAILED(rc)) return rc;
1791
1792 /** @todo check validity! */
1793
1794 setModified(IsModified_MachineData);
1795 mHWData.backup();
1796 mHWData->mAccelerate2DVideoEnabled = enable;
1797
1798 return S_OK;
1799}
1800
1801STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
1802{
1803 if (!monitorCount)
1804 return E_POINTER;
1805
1806 AutoCaller autoCaller(this);
1807 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1808
1809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1810
1811 *monitorCount = mHWData->mMonitorCount;
1812
1813 return S_OK;
1814}
1815
1816STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
1817{
1818 /* make sure monitor count is a sensible number */
1819 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
1820 return setError(E_INVALIDARG,
1821 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1822 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
1823
1824 AutoCaller autoCaller(this);
1825 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1826
1827 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1828
1829 HRESULT rc = checkStateDependency(MutableStateDep);
1830 if (FAILED(rc)) return rc;
1831
1832 setModified(IsModified_MachineData);
1833 mHWData.backup();
1834 mHWData->mMonitorCount = monitorCount;
1835
1836 return S_OK;
1837}
1838
1839STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
1840{
1841 if (!biosSettings)
1842 return E_POINTER;
1843
1844 AutoCaller autoCaller(this);
1845 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1846
1847 /* mBIOSSettings is constant during life time, no need to lock */
1848 mBIOSSettings.queryInterfaceTo(biosSettings);
1849
1850 return S_OK;
1851}
1852
1853STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
1854{
1855 if (!aVal)
1856 return E_POINTER;
1857
1858 AutoCaller autoCaller(this);
1859 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1860
1861 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1862
1863 switch(property)
1864 {
1865 case CPUPropertyType_PAE:
1866 *aVal = mHWData->mPAEEnabled;
1867 break;
1868
1869 case CPUPropertyType_Synthetic:
1870 *aVal = mHWData->mSyntheticCpu;
1871 break;
1872
1873 default:
1874 return E_INVALIDARG;
1875 }
1876 return S_OK;
1877}
1878
1879STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
1880{
1881 AutoCaller autoCaller(this);
1882 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1883
1884 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1885
1886 HRESULT rc = checkStateDependency(MutableStateDep);
1887 if (FAILED(rc)) return rc;
1888
1889 switch(property)
1890 {
1891 case CPUPropertyType_PAE:
1892 setModified(IsModified_MachineData);
1893 mHWData.backup();
1894 mHWData->mPAEEnabled = !!aVal;
1895 break;
1896
1897 case CPUPropertyType_Synthetic:
1898 setModified(IsModified_MachineData);
1899 mHWData.backup();
1900 mHWData->mSyntheticCpu = !!aVal;
1901 break;
1902
1903 default:
1904 return E_INVALIDARG;
1905 }
1906 return S_OK;
1907}
1908
1909STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
1910{
1911 CheckComArgOutPointerValid(aValEax);
1912 CheckComArgOutPointerValid(aValEbx);
1913 CheckComArgOutPointerValid(aValEcx);
1914 CheckComArgOutPointerValid(aValEdx);
1915
1916 AutoCaller autoCaller(this);
1917 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1918
1919 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1920
1921 switch(aId)
1922 {
1923 case 0x0:
1924 case 0x1:
1925 case 0x2:
1926 case 0x3:
1927 case 0x4:
1928 case 0x5:
1929 case 0x6:
1930 case 0x7:
1931 case 0x8:
1932 case 0x9:
1933 case 0xA:
1934 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
1935 return E_INVALIDARG;
1936
1937 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
1938 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
1939 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
1940 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
1941 break;
1942
1943 case 0x80000000:
1944 case 0x80000001:
1945 case 0x80000002:
1946 case 0x80000003:
1947 case 0x80000004:
1948 case 0x80000005:
1949 case 0x80000006:
1950 case 0x80000007:
1951 case 0x80000008:
1952 case 0x80000009:
1953 case 0x8000000A:
1954 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
1955 return E_INVALIDARG;
1956
1957 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
1958 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
1959 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
1960 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
1961 break;
1962
1963 default:
1964 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
1965 }
1966 return S_OK;
1967}
1968
1969STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
1970{
1971 AutoCaller autoCaller(this);
1972 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1973
1974 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1975
1976 HRESULT rc = checkStateDependency(MutableStateDep);
1977 if (FAILED(rc)) return rc;
1978
1979 switch(aId)
1980 {
1981 case 0x0:
1982 case 0x1:
1983 case 0x2:
1984 case 0x3:
1985 case 0x4:
1986 case 0x5:
1987 case 0x6:
1988 case 0x7:
1989 case 0x8:
1990 case 0x9:
1991 case 0xA:
1992 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
1993 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
1994 setModified(IsModified_MachineData);
1995 mHWData.backup();
1996 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
1997 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
1998 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
1999 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2000 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2001 break;
2002
2003 case 0x80000000:
2004 case 0x80000001:
2005 case 0x80000002:
2006 case 0x80000003:
2007 case 0x80000004:
2008 case 0x80000005:
2009 case 0x80000006:
2010 case 0x80000007:
2011 case 0x80000008:
2012 case 0x80000009:
2013 case 0x8000000A:
2014 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2015 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2016 setModified(IsModified_MachineData);
2017 mHWData.backup();
2018 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2019 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2020 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2021 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2022 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2023 break;
2024
2025 default:
2026 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2027 }
2028 return S_OK;
2029}
2030
2031STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2032{
2033 AutoCaller autoCaller(this);
2034 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2035
2036 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2037
2038 HRESULT rc = checkStateDependency(MutableStateDep);
2039 if (FAILED(rc)) return rc;
2040
2041 switch(aId)
2042 {
2043 case 0x0:
2044 case 0x1:
2045 case 0x2:
2046 case 0x3:
2047 case 0x4:
2048 case 0x5:
2049 case 0x6:
2050 case 0x7:
2051 case 0x8:
2052 case 0x9:
2053 case 0xA:
2054 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2055 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2056 setModified(IsModified_MachineData);
2057 mHWData.backup();
2058 /* Invalidate leaf. */
2059 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2060 break;
2061
2062 case 0x80000000:
2063 case 0x80000001:
2064 case 0x80000002:
2065 case 0x80000003:
2066 case 0x80000004:
2067 case 0x80000005:
2068 case 0x80000006:
2069 case 0x80000007:
2070 case 0x80000008:
2071 case 0x80000009:
2072 case 0x8000000A:
2073 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2074 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2075 setModified(IsModified_MachineData);
2076 mHWData.backup();
2077 /* Invalidate leaf. */
2078 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2079 break;
2080
2081 default:
2082 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2083 }
2084 return S_OK;
2085}
2086
2087STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2088{
2089 AutoCaller autoCaller(this);
2090 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2091
2092 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2093
2094 HRESULT rc = checkStateDependency(MutableStateDep);
2095 if (FAILED(rc)) return rc;
2096
2097 setModified(IsModified_MachineData);
2098 mHWData.backup();
2099
2100 /* Invalidate all standard leafs. */
2101 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2102 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2103
2104 /* Invalidate all extended leafs. */
2105 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2106 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2107
2108 return S_OK;
2109}
2110
2111STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2112{
2113 if (!aVal)
2114 return E_POINTER;
2115
2116 AutoCaller autoCaller(this);
2117 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2118
2119 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2120
2121 switch(property)
2122 {
2123 case HWVirtExPropertyType_Enabled:
2124 *aVal = mHWData->mHWVirtExEnabled;
2125 break;
2126
2127 case HWVirtExPropertyType_Exclusive:
2128 *aVal = mHWData->mHWVirtExExclusive;
2129 break;
2130
2131 case HWVirtExPropertyType_VPID:
2132 *aVal = mHWData->mHWVirtExVPIDEnabled;
2133 break;
2134
2135 case HWVirtExPropertyType_NestedPaging:
2136 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2137 break;
2138
2139 case HWVirtExPropertyType_LargePages:
2140 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2141#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2142 *aVal = FALSE;
2143#endif
2144 break;
2145
2146 case HWVirtExPropertyType_Force:
2147 *aVal = mHWData->mHWVirtExForceEnabled;
2148 break;
2149
2150 default:
2151 return E_INVALIDARG;
2152 }
2153 return S_OK;
2154}
2155
2156STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2157{
2158 AutoCaller autoCaller(this);
2159 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2160
2161 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2162
2163 HRESULT rc = checkStateDependency(MutableStateDep);
2164 if (FAILED(rc)) return rc;
2165
2166 switch(property)
2167 {
2168 case HWVirtExPropertyType_Enabled:
2169 setModified(IsModified_MachineData);
2170 mHWData.backup();
2171 mHWData->mHWVirtExEnabled = !!aVal;
2172 break;
2173
2174 case HWVirtExPropertyType_Exclusive:
2175 setModified(IsModified_MachineData);
2176 mHWData.backup();
2177 mHWData->mHWVirtExExclusive = !!aVal;
2178 break;
2179
2180 case HWVirtExPropertyType_VPID:
2181 setModified(IsModified_MachineData);
2182 mHWData.backup();
2183 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2184 break;
2185
2186 case HWVirtExPropertyType_NestedPaging:
2187 setModified(IsModified_MachineData);
2188 mHWData.backup();
2189 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2190 break;
2191
2192 case HWVirtExPropertyType_LargePages:
2193 setModified(IsModified_MachineData);
2194 mHWData.backup();
2195 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2196 break;
2197
2198 case HWVirtExPropertyType_Force:
2199 setModified(IsModified_MachineData);
2200 mHWData.backup();
2201 mHWData->mHWVirtExForceEnabled = !!aVal;
2202 break;
2203
2204 default:
2205 return E_INVALIDARG;
2206 }
2207
2208 return S_OK;
2209}
2210
2211STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2212{
2213 CheckComArgOutPointerValid(aSnapshotFolder);
2214
2215 AutoCaller autoCaller(this);
2216 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2217
2218 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2219
2220 Utf8Str strFullSnapshotFolder;
2221 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2222 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2223
2224 return S_OK;
2225}
2226
2227STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2228{
2229 /* @todo (r=dmik):
2230 * 1. Allow to change the name of the snapshot folder containing snapshots
2231 * 2. Rename the folder on disk instead of just changing the property
2232 * value (to be smart and not to leave garbage). Note that it cannot be
2233 * done here because the change may be rolled back. Thus, the right
2234 * place is #saveSettings().
2235 */
2236
2237 AutoCaller autoCaller(this);
2238 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2239
2240 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2241
2242 HRESULT rc = checkStateDependency(MutableStateDep);
2243 if (FAILED(rc)) return rc;
2244
2245 if (!mData->mCurrentSnapshot.isNull())
2246 return setError(E_FAIL,
2247 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2248
2249 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2250
2251 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2252 if (strSnapshotFolder.isEmpty())
2253 strSnapshotFolder = "Snapshots";
2254 int vrc = calculateFullPath(strSnapshotFolder,
2255 strSnapshotFolder);
2256 if (RT_FAILURE(vrc))
2257 return setError(E_FAIL,
2258 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2259 aSnapshotFolder, vrc);
2260
2261 setModified(IsModified_MachineData);
2262 mUserData.backup();
2263
2264 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2265
2266 return S_OK;
2267}
2268
2269STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2270{
2271 if (ComSafeArrayOutIsNull(aAttachments))
2272 return E_POINTER;
2273
2274 AutoCaller autoCaller(this);
2275 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2276
2277 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2278
2279 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2280 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2281
2282 return S_OK;
2283}
2284
2285STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2286{
2287 if (!vrdeServer)
2288 return E_POINTER;
2289
2290 AutoCaller autoCaller(this);
2291 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2292
2293 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2294
2295 Assert(!!mVRDEServer);
2296 mVRDEServer.queryInterfaceTo(vrdeServer);
2297
2298 return S_OK;
2299}
2300
2301STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2302{
2303 if (!audioAdapter)
2304 return E_POINTER;
2305
2306 AutoCaller autoCaller(this);
2307 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2308
2309 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2310
2311 mAudioAdapter.queryInterfaceTo(audioAdapter);
2312 return S_OK;
2313}
2314
2315STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2316{
2317#ifdef VBOX_WITH_VUSB
2318 CheckComArgOutPointerValid(aUSBController);
2319
2320 AutoCaller autoCaller(this);
2321 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2322
2323 clearError();
2324 MultiResult rc(S_OK);
2325
2326# ifdef VBOX_WITH_USB
2327 rc = mParent->host()->checkUSBProxyService();
2328 if (FAILED(rc)) return rc;
2329# endif
2330
2331 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2332
2333 return rc = mUSBController.queryInterfaceTo(aUSBController);
2334#else
2335 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2336 * extended error info to indicate that USB is simply not available
2337 * (w/o treating it as a failure), for example, as in OSE */
2338 NOREF(aUSBController);
2339 ReturnComNotImplemented();
2340#endif /* VBOX_WITH_VUSB */
2341}
2342
2343STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2344{
2345 CheckComArgOutPointerValid(aFilePath);
2346
2347 AutoLimitedCaller autoCaller(this);
2348 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2349
2350 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2351
2352 mData->m_strConfigFileFull.cloneTo(aFilePath);
2353 return S_OK;
2354}
2355
2356STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2357{
2358 CheckComArgOutPointerValid(aModified);
2359
2360 AutoCaller autoCaller(this);
2361 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2362
2363 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2364
2365 HRESULT rc = checkStateDependency(MutableStateDep);
2366 if (FAILED(rc)) return rc;
2367
2368 if (!mData->pMachineConfigFile->fileExists())
2369 // this is a new machine, and no config file exists yet:
2370 *aModified = TRUE;
2371 else
2372 *aModified = (mData->flModifications != 0);
2373
2374 return S_OK;
2375}
2376
2377STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2378{
2379 CheckComArgOutPointerValid(aSessionState);
2380
2381 AutoCaller autoCaller(this);
2382 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2383
2384 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2385
2386 *aSessionState = mData->mSession.mState;
2387
2388 return S_OK;
2389}
2390
2391STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2392{
2393 CheckComArgOutPointerValid(aSessionType);
2394
2395 AutoCaller autoCaller(this);
2396 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2397
2398 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2399
2400 mData->mSession.mType.cloneTo(aSessionType);
2401
2402 return S_OK;
2403}
2404
2405STDMETHODIMP Machine::COMGETTER(SessionPid)(ULONG *aSessionPid)
2406{
2407 CheckComArgOutPointerValid(aSessionPid);
2408
2409 AutoCaller autoCaller(this);
2410 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2411
2412 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2413
2414 *aSessionPid = mData->mSession.mPid;
2415
2416 return S_OK;
2417}
2418
2419STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2420{
2421 if (!machineState)
2422 return E_POINTER;
2423
2424 AutoCaller autoCaller(this);
2425 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2426
2427 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2428
2429 *machineState = mData->mMachineState;
2430
2431 return S_OK;
2432}
2433
2434STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2435{
2436 CheckComArgOutPointerValid(aLastStateChange);
2437
2438 AutoCaller autoCaller(this);
2439 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2440
2441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2442
2443 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2444
2445 return S_OK;
2446}
2447
2448STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2449{
2450 CheckComArgOutPointerValid(aStateFilePath);
2451
2452 AutoCaller autoCaller(this);
2453 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2454
2455 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2456
2457 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2458
2459 return S_OK;
2460}
2461
2462STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2463{
2464 CheckComArgOutPointerValid(aLogFolder);
2465
2466 AutoCaller autoCaller(this);
2467 AssertComRCReturnRC(autoCaller.rc());
2468
2469 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2470
2471 Utf8Str logFolder;
2472 getLogFolder(logFolder);
2473 logFolder.cloneTo(aLogFolder);
2474
2475 return S_OK;
2476}
2477
2478STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2479{
2480 CheckComArgOutPointerValid(aCurrentSnapshot);
2481
2482 AutoCaller autoCaller(this);
2483 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2484
2485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2486
2487 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2488
2489 return S_OK;
2490}
2491
2492STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2493{
2494 CheckComArgOutPointerValid(aSnapshotCount);
2495
2496 AutoCaller autoCaller(this);
2497 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2498
2499 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2500
2501 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2502 ? 0
2503 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2504
2505 return S_OK;
2506}
2507
2508STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2509{
2510 CheckComArgOutPointerValid(aCurrentStateModified);
2511
2512 AutoCaller autoCaller(this);
2513 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2514
2515 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2516
2517 /* Note: for machines with no snapshots, we always return FALSE
2518 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2519 * reasons :) */
2520
2521 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2522 ? FALSE
2523 : mData->mCurrentStateModified;
2524
2525 return S_OK;
2526}
2527
2528STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2529{
2530 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2531
2532 AutoCaller autoCaller(this);
2533 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2534
2535 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2536
2537 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2538 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2539
2540 return S_OK;
2541}
2542
2543STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2544{
2545 CheckComArgOutPointerValid(aClipboardMode);
2546
2547 AutoCaller autoCaller(this);
2548 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2549
2550 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2551
2552 *aClipboardMode = mHWData->mClipboardMode;
2553
2554 return S_OK;
2555}
2556
2557STDMETHODIMP
2558Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2559{
2560 HRESULT rc = S_OK;
2561
2562 AutoCaller autoCaller(this);
2563 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2564
2565 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2566
2567 alock.release();
2568 rc = onClipboardModeChange(aClipboardMode);
2569 alock.acquire();
2570 if (FAILED(rc)) return rc;
2571
2572 setModified(IsModified_MachineData);
2573 mHWData.backup();
2574 mHWData->mClipboardMode = aClipboardMode;
2575
2576 /* Save settings if online - todo why is this required?? */
2577 if (Global::IsOnline(mData->mMachineState))
2578 saveSettings(NULL);
2579
2580 return S_OK;
2581}
2582
2583STDMETHODIMP
2584Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
2585{
2586 CheckComArgOutPointerValid(aPatterns);
2587
2588 AutoCaller autoCaller(this);
2589 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2590
2591 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2592
2593 try
2594 {
2595 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
2596 }
2597 catch (...)
2598 {
2599 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2600 }
2601
2602 return S_OK;
2603}
2604
2605STDMETHODIMP
2606Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
2607{
2608 AutoCaller autoCaller(this);
2609 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2610
2611 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2612
2613 HRESULT rc = checkStateDependency(MutableStateDep);
2614 if (FAILED(rc)) return rc;
2615
2616 setModified(IsModified_MachineData);
2617 mHWData.backup();
2618 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
2619 return rc;
2620}
2621
2622STDMETHODIMP
2623Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
2624{
2625 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
2626
2627 AutoCaller autoCaller(this);
2628 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2629
2630 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2631
2632 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
2633 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
2634
2635 return S_OK;
2636}
2637
2638STDMETHODIMP
2639Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
2640{
2641 CheckComArgOutPointerValid(aEnabled);
2642
2643 AutoCaller autoCaller(this);
2644 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2645
2646 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2647
2648 *aEnabled = mUserData->s.fTeleporterEnabled;
2649
2650 return S_OK;
2651}
2652
2653STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
2654{
2655 AutoCaller autoCaller(this);
2656 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2657
2658 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2659
2660 /* Only allow it to be set to true when PoweredOff or Aborted.
2661 (Clearing it is always permitted.) */
2662 if ( aEnabled
2663 && mData->mRegistered
2664 && ( !isSessionMachine()
2665 || ( mData->mMachineState != MachineState_PoweredOff
2666 && mData->mMachineState != MachineState_Teleported
2667 && mData->mMachineState != MachineState_Aborted
2668 )
2669 )
2670 )
2671 return setError(VBOX_E_INVALID_VM_STATE,
2672 tr("The machine is not powered off (state is %s)"),
2673 Global::stringifyMachineState(mData->mMachineState));
2674
2675 setModified(IsModified_MachineData);
2676 mUserData.backup();
2677 mUserData->s.fTeleporterEnabled = !!aEnabled;
2678
2679 return S_OK;
2680}
2681
2682STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
2683{
2684 CheckComArgOutPointerValid(aPort);
2685
2686 AutoCaller autoCaller(this);
2687 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2688
2689 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2690
2691 *aPort = (ULONG)mUserData->s.uTeleporterPort;
2692
2693 return S_OK;
2694}
2695
2696STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
2697{
2698 if (aPort >= _64K)
2699 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
2700
2701 AutoCaller autoCaller(this);
2702 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2703
2704 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2705
2706 HRESULT rc = checkStateDependency(MutableStateDep);
2707 if (FAILED(rc)) return rc;
2708
2709 setModified(IsModified_MachineData);
2710 mUserData.backup();
2711 mUserData->s.uTeleporterPort = (uint32_t)aPort;
2712
2713 return S_OK;
2714}
2715
2716STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
2717{
2718 CheckComArgOutPointerValid(aAddress);
2719
2720 AutoCaller autoCaller(this);
2721 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2722
2723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2724
2725 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
2726
2727 return S_OK;
2728}
2729
2730STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
2731{
2732 AutoCaller autoCaller(this);
2733 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2734
2735 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2736
2737 HRESULT rc = checkStateDependency(MutableStateDep);
2738 if (FAILED(rc)) return rc;
2739
2740 setModified(IsModified_MachineData);
2741 mUserData.backup();
2742 mUserData->s.strTeleporterAddress = aAddress;
2743
2744 return S_OK;
2745}
2746
2747STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
2748{
2749 CheckComArgOutPointerValid(aPassword);
2750
2751 AutoCaller autoCaller(this);
2752 HRESULT hrc = autoCaller.rc();
2753 if (SUCCEEDED(hrc))
2754 {
2755 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2756 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
2757 }
2758
2759 return hrc;
2760}
2761
2762STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
2763{
2764 /*
2765 * Hash the password first.
2766 */
2767 Utf8Str strPassword(aPassword);
2768 if (!strPassword.isEmpty())
2769 {
2770 if (VBoxIsPasswordHashed(&strPassword))
2771 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2772 VBoxHashPassword(&strPassword);
2773 }
2774
2775 /*
2776 * Do the update.
2777 */
2778 AutoCaller autoCaller(this);
2779 HRESULT hrc = autoCaller.rc();
2780 if (SUCCEEDED(hrc))
2781 {
2782 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2783 hrc = checkStateDependency(MutableStateDep);
2784 if (SUCCEEDED(hrc))
2785 {
2786 setModified(IsModified_MachineData);
2787 mUserData.backup();
2788 mUserData->s.strTeleporterPassword = strPassword;
2789 }
2790 }
2791
2792 return hrc;
2793}
2794
2795STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
2796{
2797 CheckComArgOutPointerValid(aState);
2798
2799 AutoCaller autoCaller(this);
2800 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2801
2802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2803
2804 *aState = mUserData->s.enmFaultToleranceState;
2805 return S_OK;
2806}
2807
2808STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
2809{
2810 AutoCaller autoCaller(this);
2811 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2812
2813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2814
2815 /* @todo deal with running state change. */
2816 HRESULT rc = checkStateDependency(MutableStateDep);
2817 if (FAILED(rc)) return rc;
2818
2819 setModified(IsModified_MachineData);
2820 mUserData.backup();
2821 mUserData->s.enmFaultToleranceState = aState;
2822 return S_OK;
2823}
2824
2825STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
2826{
2827 CheckComArgOutPointerValid(aAddress);
2828
2829 AutoCaller autoCaller(this);
2830 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2831
2832 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2833
2834 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
2835 return S_OK;
2836}
2837
2838STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
2839{
2840 AutoCaller autoCaller(this);
2841 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2842
2843 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2844
2845 /* @todo deal with running state change. */
2846 HRESULT rc = checkStateDependency(MutableStateDep);
2847 if (FAILED(rc)) return rc;
2848
2849 setModified(IsModified_MachineData);
2850 mUserData.backup();
2851 mUserData->s.strFaultToleranceAddress = aAddress;
2852 return S_OK;
2853}
2854
2855STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
2856{
2857 CheckComArgOutPointerValid(aPort);
2858
2859 AutoCaller autoCaller(this);
2860 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2861
2862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2863
2864 *aPort = mUserData->s.uFaultTolerancePort;
2865 return S_OK;
2866}
2867
2868STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
2869{
2870 AutoCaller autoCaller(this);
2871 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2872
2873 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2874
2875 /* @todo deal with running state change. */
2876 HRESULT rc = checkStateDependency(MutableStateDep);
2877 if (FAILED(rc)) return rc;
2878
2879 setModified(IsModified_MachineData);
2880 mUserData.backup();
2881 mUserData->s.uFaultTolerancePort = aPort;
2882 return S_OK;
2883}
2884
2885STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
2886{
2887 CheckComArgOutPointerValid(aPassword);
2888
2889 AutoCaller autoCaller(this);
2890 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2891
2892 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2893
2894 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
2895
2896 return S_OK;
2897}
2898
2899STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
2900{
2901 AutoCaller autoCaller(this);
2902 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2903
2904 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2905
2906 /* @todo deal with running state change. */
2907 HRESULT rc = checkStateDependency(MutableStateDep);
2908 if (FAILED(rc)) return rc;
2909
2910 setModified(IsModified_MachineData);
2911 mUserData.backup();
2912 mUserData->s.strFaultTolerancePassword = aPassword;
2913
2914 return S_OK;
2915}
2916
2917STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
2918{
2919 CheckComArgOutPointerValid(aInterval);
2920
2921 AutoCaller autoCaller(this);
2922 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2923
2924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2925
2926 *aInterval = mUserData->s.uFaultToleranceInterval;
2927 return S_OK;
2928}
2929
2930STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
2931{
2932 AutoCaller autoCaller(this);
2933 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2934
2935 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2936
2937 /* @todo deal with running state change. */
2938 HRESULT rc = checkStateDependency(MutableStateDep);
2939 if (FAILED(rc)) return rc;
2940
2941 setModified(IsModified_MachineData);
2942 mUserData.backup();
2943 mUserData->s.uFaultToleranceInterval = aInterval;
2944 return S_OK;
2945}
2946
2947STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
2948{
2949 CheckComArgOutPointerValid(aEnabled);
2950
2951 AutoCaller autoCaller(this);
2952 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2953
2954 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2955
2956 *aEnabled = mUserData->s.fRTCUseUTC;
2957
2958 return S_OK;
2959}
2960
2961STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
2962{
2963 AutoCaller autoCaller(this);
2964 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2965
2966 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2967
2968 /* Only allow it to be set to true when PoweredOff or Aborted.
2969 (Clearing it is always permitted.) */
2970 if ( aEnabled
2971 && mData->mRegistered
2972 && ( !isSessionMachine()
2973 || ( mData->mMachineState != MachineState_PoweredOff
2974 && mData->mMachineState != MachineState_Teleported
2975 && mData->mMachineState != MachineState_Aborted
2976 )
2977 )
2978 )
2979 return setError(VBOX_E_INVALID_VM_STATE,
2980 tr("The machine is not powered off (state is %s)"),
2981 Global::stringifyMachineState(mData->mMachineState));
2982
2983 setModified(IsModified_MachineData);
2984 mUserData.backup();
2985 mUserData->s.fRTCUseUTC = !!aEnabled;
2986
2987 return S_OK;
2988}
2989
2990STDMETHODIMP Machine::COMGETTER(IoCacheEnabled)(BOOL *aEnabled)
2991{
2992 CheckComArgOutPointerValid(aEnabled);
2993
2994 AutoCaller autoCaller(this);
2995 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2996
2997 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2998
2999 *aEnabled = mHWData->mIoCacheEnabled;
3000
3001 return S_OK;
3002}
3003
3004STDMETHODIMP Machine::COMSETTER(IoCacheEnabled)(BOOL aEnabled)
3005{
3006 AutoCaller autoCaller(this);
3007 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3008
3009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3010
3011 HRESULT rc = checkStateDependency(MutableStateDep);
3012 if (FAILED(rc)) return rc;
3013
3014 setModified(IsModified_MachineData);
3015 mHWData.backup();
3016 mHWData->mIoCacheEnabled = aEnabled;
3017
3018 return S_OK;
3019}
3020
3021STDMETHODIMP Machine::COMGETTER(IoCacheSize)(ULONG *aIoCacheSize)
3022{
3023 CheckComArgOutPointerValid(aIoCacheSize);
3024
3025 AutoCaller autoCaller(this);
3026 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3027
3028 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3029
3030 *aIoCacheSize = mHWData->mIoCacheSize;
3031
3032 return S_OK;
3033}
3034
3035STDMETHODIMP Machine::COMSETTER(IoCacheSize)(ULONG aIoCacheSize)
3036{
3037 AutoCaller autoCaller(this);
3038 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3039
3040 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3041
3042 HRESULT rc = checkStateDependency(MutableStateDep);
3043 if (FAILED(rc)) return rc;
3044
3045 setModified(IsModified_MachineData);
3046 mHWData.backup();
3047 mHWData->mIoCacheSize = aIoCacheSize;
3048
3049 return S_OK;
3050}
3051
3052
3053/**
3054 * @note Locks objects!
3055 */
3056STDMETHODIMP Machine::LockMachine(ISession *aSession,
3057 LockType_T lockType)
3058{
3059 CheckComArgNotNull(aSession);
3060
3061 AutoCaller autoCaller(this);
3062 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3063
3064 /* check the session state */
3065 SessionState_T state;
3066 HRESULT rc = aSession->COMGETTER(State)(&state);
3067 if (FAILED(rc)) return rc;
3068
3069 if (state != SessionState_Unlocked)
3070 return setError(VBOX_E_INVALID_OBJECT_STATE,
3071 tr("The given session is busy"));
3072
3073 // get the client's IInternalSessionControl interface
3074 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3075 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3076 E_INVALIDARG);
3077
3078 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3079
3080 if (!mData->mRegistered)
3081 return setError(E_UNEXPECTED,
3082 tr("The machine '%s' is not registered"),
3083 mUserData->s.strName.c_str());
3084
3085 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3086
3087 SessionState_T oldState = mData->mSession.mState;
3088 /* Hack: in case the session is closing and there is a progress object
3089 * which allows waiting for the session to be closed, take the opportunity
3090 * and do a limited wait (max. 1 second). This helps a lot when the system
3091 * is busy and thus session closing can take a little while. */
3092 if ( mData->mSession.mState == SessionState_Unlocking
3093 && mData->mSession.mProgress)
3094 {
3095 alock.release();
3096 mData->mSession.mProgress->WaitForCompletion(1000);
3097 alock.acquire();
3098 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3099 }
3100
3101 // try again now
3102 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3103 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3104 )
3105 {
3106 // OK, share the session... we are now dealing with three processes:
3107 // 1) VBoxSVC (where this code runs);
3108 // 2) process C: the caller's client process (who wants a shared session);
3109 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3110
3111 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3112 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3113 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3114 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3115 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3116
3117 /*
3118 * Release the lock before calling the client process. It's safe here
3119 * since the only thing to do after we get the lock again is to add
3120 * the remote control to the list (which doesn't directly influence
3121 * anything).
3122 */
3123 alock.release();
3124
3125 // get the console of the session holding the write lock (this is a remote call)
3126 ComPtr<IConsole> pConsoleW;
3127 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3128 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3129 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3130 if (FAILED(rc))
3131 // the failure may occur w/o any error info (from RPC), so provide one
3132 return setError(VBOX_E_VM_ERROR,
3133 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3134
3135 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3136
3137 // share the session machine and W's console with the caller's session
3138 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3139 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3140 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3141
3142 if (FAILED(rc))
3143 // the failure may occur w/o any error info (from RPC), so provide one
3144 return setError(VBOX_E_VM_ERROR,
3145 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3146 alock.acquire();
3147
3148 // need to revalidate the state after acquiring the lock again
3149 if (mData->mSession.mState != SessionState_Locked)
3150 {
3151 pSessionControl->Uninitialize();
3152 return setError(VBOX_E_INVALID_SESSION_STATE,
3153 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3154 mUserData->s.strName.c_str());
3155 }
3156
3157 // add the caller's session to the list
3158 mData->mSession.mRemoteControls.push_back(pSessionControl);
3159 }
3160 else if ( mData->mSession.mState == SessionState_Locked
3161 || mData->mSession.mState == SessionState_Unlocking
3162 )
3163 {
3164 // sharing not permitted, or machine still unlocking:
3165 return setError(VBOX_E_INVALID_OBJECT_STATE,
3166 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3167 mUserData->s.strName.c_str());
3168 }
3169 else
3170 {
3171 // machine is not locked: then write-lock the machine (create the session machine)
3172
3173 // must not be busy
3174 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3175
3176 // get the caller's session PID
3177 RTPROCESS pid = NIL_RTPROCESS;
3178 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3179 pSessionControl->GetPID((ULONG*)&pid);
3180 Assert(pid != NIL_RTPROCESS);
3181
3182 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3183
3184 if (fLaunchingVMProcess)
3185 {
3186 // this machine is awaiting for a spawning session to be opened:
3187 // then the calling process must be the one that got started by
3188 // LaunchVMProcess()
3189
3190 LogFlowThisFunc(("mSession.mPid=%d(0x%x)\n", mData->mSession.mPid, mData->mSession.mPid));
3191 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3192
3193 if (mData->mSession.mPid != pid)
3194 return setError(E_ACCESSDENIED,
3195 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3196 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3197 pid, mUserData->s.strName.c_str(), mData->mSession.mPid);
3198 }
3199
3200 // create the mutable SessionMachine from the current machine
3201 ComObjPtr<SessionMachine> sessionMachine;
3202 sessionMachine.createObject();
3203 rc = sessionMachine->init(this);
3204 AssertComRC(rc);
3205
3206 /* NOTE: doing return from this function after this point but
3207 * before the end is forbidden since it may call SessionMachine::uninit()
3208 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3209 * lock while still holding the Machine lock in alock so that a deadlock
3210 * is possible due to the wrong lock order. */
3211
3212 if (SUCCEEDED(rc))
3213 {
3214 /*
3215 * Set the session state to Spawning to protect against subsequent
3216 * attempts to open a session and to unregister the machine after
3217 * we release the lock.
3218 */
3219 SessionState_T origState = mData->mSession.mState;
3220 mData->mSession.mState = SessionState_Spawning;
3221
3222 /*
3223 * Release the lock before calling the client process -- it will call
3224 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3225 * because the state is Spawning, so that LaunchVMProcess() and
3226 * LockMachine() calls will fail. This method, called before we
3227 * acquire the lock again, will fail because of the wrong PID.
3228 *
3229 * Note that mData->mSession.mRemoteControls accessed outside
3230 * the lock may not be modified when state is Spawning, so it's safe.
3231 */
3232 alock.release();
3233
3234 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3235 rc = pSessionControl->AssignMachine(sessionMachine);
3236 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3237
3238 /* The failure may occur w/o any error info (from RPC), so provide one */
3239 if (FAILED(rc))
3240 setError(VBOX_E_VM_ERROR,
3241 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3242
3243 if ( SUCCEEDED(rc)
3244 && fLaunchingVMProcess
3245 )
3246 {
3247 /* complete the remote session initialization */
3248
3249 /* get the console from the direct session */
3250 ComPtr<IConsole> console;
3251 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3252 ComAssertComRC(rc);
3253
3254 if (SUCCEEDED(rc) && !console)
3255 {
3256 ComAssert(!!console);
3257 rc = E_FAIL;
3258 }
3259
3260 /* assign machine & console to the remote session */
3261 if (SUCCEEDED(rc))
3262 {
3263 /*
3264 * after LaunchVMProcess(), the first and the only
3265 * entry in remoteControls is that remote session
3266 */
3267 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3268 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3269 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3270
3271 /* The failure may occur w/o any error info (from RPC), so provide one */
3272 if (FAILED(rc))
3273 setError(VBOX_E_VM_ERROR,
3274 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3275 }
3276
3277 if (FAILED(rc))
3278 pSessionControl->Uninitialize();
3279 }
3280
3281 /* acquire the lock again */
3282 alock.acquire();
3283
3284 /* Restore the session state */
3285 mData->mSession.mState = origState;
3286 }
3287
3288 // finalize spawning anyway (this is why we don't return on errors above)
3289 if (fLaunchingVMProcess)
3290 {
3291 /* Note that the progress object is finalized later */
3292 /** @todo Consider checking mData->mSession.mProgress for cancellation
3293 * around here. */
3294
3295 /* We don't reset mSession.mPid here because it is necessary for
3296 * SessionMachine::uninit() to reap the child process later. */
3297
3298 if (FAILED(rc))
3299 {
3300 /* Close the remote session, remove the remote control from the list
3301 * and reset session state to Closed (@note keep the code in sync
3302 * with the relevant part in openSession()). */
3303
3304 Assert(mData->mSession.mRemoteControls.size() == 1);
3305 if (mData->mSession.mRemoteControls.size() == 1)
3306 {
3307 ErrorInfoKeeper eik;
3308 mData->mSession.mRemoteControls.front()->Uninitialize();
3309 }
3310
3311 mData->mSession.mRemoteControls.clear();
3312 mData->mSession.mState = SessionState_Unlocked;
3313 }
3314 }
3315 else
3316 {
3317 /* memorize PID of the directly opened session */
3318 if (SUCCEEDED(rc))
3319 mData->mSession.mPid = pid;
3320 }
3321
3322 if (SUCCEEDED(rc))
3323 {
3324 /* memorize the direct session control and cache IUnknown for it */
3325 mData->mSession.mDirectControl = pSessionControl;
3326 mData->mSession.mState = SessionState_Locked;
3327 /* associate the SessionMachine with this Machine */
3328 mData->mSession.mMachine = sessionMachine;
3329
3330 /* request an IUnknown pointer early from the remote party for later
3331 * identity checks (it will be internally cached within mDirectControl
3332 * at least on XPCOM) */
3333 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3334 NOREF(unk);
3335 }
3336
3337 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3338 * would break the lock order */
3339 alock.release();
3340
3341 /* uninitialize the created session machine on failure */
3342 if (FAILED(rc))
3343 sessionMachine->uninit();
3344
3345 }
3346
3347 if (SUCCEEDED(rc))
3348 {
3349 /*
3350 * tell the client watcher thread to update the set of
3351 * machines that have open sessions
3352 */
3353 mParent->updateClientWatcher();
3354
3355 if (oldState != SessionState_Locked)
3356 /* fire an event */
3357 mParent->onSessionStateChange(getId(), SessionState_Locked);
3358 }
3359
3360 return rc;
3361}
3362
3363/**
3364 * @note Locks objects!
3365 */
3366STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3367 IN_BSTR aType,
3368 IN_BSTR aEnvironment,
3369 IProgress **aProgress)
3370{
3371 CheckComArgStrNotEmptyOrNull(aType);
3372 Utf8Str strType(aType);
3373 Utf8Str strEnvironment(aEnvironment);
3374 /* "emergencystop" doesn't need the session, so skip the checks/interface
3375 * retrieval. This code doesn't quite fit in here, but introducing a
3376 * special API method would be even more effort, and would require explicit
3377 * support by every API client. It's better to hide the feature a bit. */
3378 if (strType != "emergencystop")
3379 CheckComArgNotNull(aSession);
3380 CheckComArgOutPointerValid(aProgress);
3381
3382 AutoCaller autoCaller(this);
3383 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3384
3385 ComPtr<IInternalSessionControl> control;
3386 HRESULT rc = S_OK;
3387
3388 if (strType != "emergencystop")
3389 {
3390 /* check the session state */
3391 SessionState_T state;
3392 rc = aSession->COMGETTER(State)(&state);
3393 if (FAILED(rc))
3394 return rc;
3395
3396 if (state != SessionState_Unlocked)
3397 return setError(VBOX_E_INVALID_OBJECT_STATE,
3398 tr("The given session is busy"));
3399
3400 /* get the IInternalSessionControl interface */
3401 control = aSession;
3402 ComAssertMsgRet(!control.isNull(),
3403 ("No IInternalSessionControl interface"),
3404 E_INVALIDARG);
3405 }
3406
3407 /* get the teleporter enable state for the progress object init. */
3408 BOOL fTeleporterEnabled;
3409 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3410 if (FAILED(rc))
3411 return rc;
3412
3413 /* create a progress object */
3414 if (strType != "emergencystop")
3415 {
3416 ComObjPtr<ProgressProxy> progress;
3417 progress.createObject();
3418 rc = progress->init(mParent,
3419 static_cast<IMachine*>(this),
3420 Bstr(tr("Starting VM")).raw(),
3421 TRUE /* aCancelable */,
3422 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3423 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strType.c_str()).raw(),
3424 2 /* uFirstOperationWeight */,
3425 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3426
3427 if (SUCCEEDED(rc))
3428 {
3429 rc = launchVMProcess(control, strType, strEnvironment, progress);
3430 if (SUCCEEDED(rc))
3431 {
3432 progress.queryInterfaceTo(aProgress);
3433
3434 /* signal the client watcher thread */
3435 mParent->updateClientWatcher();
3436
3437 /* fire an event */
3438 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3439 }
3440 }
3441 }
3442 else
3443 {
3444 /* no progress object - either instant success or failure */
3445 *aProgress = NULL;
3446
3447 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3448
3449 if (mData->mSession.mState != SessionState_Locked)
3450 return setError(VBOX_E_INVALID_OBJECT_STATE,
3451 tr("The machine '%s' is not locked by a session"),
3452 mUserData->s.strName.c_str());
3453
3454 /* must have a VM process associated - do not kill normal API clients
3455 * with an open session */
3456 if (!Global::IsOnline(mData->mMachineState))
3457 return setError(VBOX_E_INVALID_OBJECT_STATE,
3458 tr("The machine '%s' does not have a VM process"),
3459 mUserData->s.strName.c_str());
3460
3461 /* forcibly terminate the VM process */
3462 if (mData->mSession.mPid != NIL_RTPROCESS)
3463 RTProcTerminate(mData->mSession.mPid);
3464
3465 /* signal the client watcher thread, as most likely the client has
3466 * been terminated */
3467 mParent->updateClientWatcher();
3468 }
3469
3470 return rc;
3471}
3472
3473STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3474{
3475 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3476 return setError(E_INVALIDARG,
3477 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3478 aPosition, SchemaDefs::MaxBootPosition);
3479
3480 if (aDevice == DeviceType_USB)
3481 return setError(E_NOTIMPL,
3482 tr("Booting from USB device is currently not supported"));
3483
3484 AutoCaller autoCaller(this);
3485 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3486
3487 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3488
3489 HRESULT rc = checkStateDependency(MutableStateDep);
3490 if (FAILED(rc)) return rc;
3491
3492 setModified(IsModified_MachineData);
3493 mHWData.backup();
3494 mHWData->mBootOrder[aPosition - 1] = aDevice;
3495
3496 return S_OK;
3497}
3498
3499STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3500{
3501 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3502 return setError(E_INVALIDARG,
3503 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3504 aPosition, SchemaDefs::MaxBootPosition);
3505
3506 AutoCaller autoCaller(this);
3507 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3508
3509 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3510
3511 *aDevice = mHWData->mBootOrder[aPosition - 1];
3512
3513 return S_OK;
3514}
3515
3516STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
3517 LONG aControllerPort,
3518 LONG aDevice,
3519 DeviceType_T aType,
3520 IMedium *aMedium)
3521{
3522 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3523 aControllerName, aControllerPort, aDevice, aType, aMedium));
3524
3525 CheckComArgStrNotEmptyOrNull(aControllerName);
3526
3527 AutoCaller autoCaller(this);
3528 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3529
3530 // request the host lock first, since might be calling Host methods for getting host drives;
3531 // next, protect the media tree all the while we're in here, as well as our member variables
3532 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
3533 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3534
3535 HRESULT rc = checkStateDependency(MutableStateDep);
3536 if (FAILED(rc)) return rc;
3537
3538 /// @todo NEWMEDIA implicit machine registration
3539 if (!mData->mRegistered)
3540 return setError(VBOX_E_INVALID_OBJECT_STATE,
3541 tr("Cannot attach storage devices to an unregistered machine"));
3542
3543 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3544
3545 /* Check for an existing controller. */
3546 ComObjPtr<StorageController> ctl;
3547 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
3548 if (FAILED(rc)) return rc;
3549
3550 StorageControllerType_T ctrlType;
3551 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3552 if (FAILED(rc))
3553 return setError(E_FAIL,
3554 tr("Could not get type of controller '%ls'"),
3555 aControllerName);
3556
3557 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3558 bool fHotplug = false;
3559 if (Global::IsOnlineOrTransient(mData->mMachineState))
3560 fHotplug = true;
3561
3562 if (fHotplug && !isControllerHotplugCapable(ctrlType))
3563 return setError(VBOX_E_INVALID_VM_STATE,
3564 tr("Controller '%ls' does not support hotplugging"),
3565 aControllerName);
3566
3567 // check that the port and device are not out of range
3568 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
3569 if (FAILED(rc)) return rc;
3570
3571 /* check if the device slot is already busy */
3572 MediumAttachment *pAttachTemp;
3573 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
3574 aControllerName,
3575 aControllerPort,
3576 aDevice)))
3577 {
3578 Medium *pMedium = pAttachTemp->getMedium();
3579 if (pMedium)
3580 {
3581 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3582 return setError(VBOX_E_OBJECT_IN_USE,
3583 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3584 pMedium->getLocationFull().c_str(),
3585 aControllerPort,
3586 aDevice,
3587 aControllerName);
3588 }
3589 else
3590 return setError(VBOX_E_OBJECT_IN_USE,
3591 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3592 aControllerPort, aDevice, aControllerName);
3593 }
3594
3595 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
3596 if (aMedium && medium.isNull())
3597 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3598
3599 AutoCaller mediumCaller(medium);
3600 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3601
3602 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3603
3604 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
3605 && !medium.isNull()
3606 )
3607 return setError(VBOX_E_OBJECT_IN_USE,
3608 tr("Medium '%s' is already attached to this virtual machine"),
3609 medium->getLocationFull().c_str());
3610
3611 if (!medium.isNull())
3612 {
3613 MediumType_T mtype = medium->getType();
3614 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3615 // For DVDs it's not written to the config file, so needs no global config
3616 // version bump. For floppies it's a new attribute "type", which is ignored
3617 // by older VirtualBox version, so needs no global config version bump either.
3618 // For hard disks this type is not accepted.
3619 if (mtype == MediumType_MultiAttach)
3620 {
3621 // This type is new with VirtualBox 4.0 and therefore requires settings
3622 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3623 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3624 // two reasons: The medium type is a property of the media registry tree, which
3625 // can reside in the global config file (for pre-4.0 media); we would therefore
3626 // possibly need to bump the global config version. We don't want to do that though
3627 // because that might make downgrading to pre-4.0 impossible.
3628 // As a result, we can only use these two new types if the medium is NOT in the
3629 // global registry:
3630 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
3631 if ( medium->isInRegistry(uuidGlobalRegistry)
3632 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3633 )
3634 return setError(VBOX_E_INVALID_OBJECT_STATE,
3635 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3636 "to machines that were created with VirtualBox 4.0 or later"),
3637 medium->getLocationFull().c_str());
3638 }
3639 }
3640
3641 bool fIndirect = false;
3642 if (!medium.isNull())
3643 fIndirect = medium->isReadOnly();
3644 bool associate = true;
3645
3646 do
3647 {
3648 if ( aType == DeviceType_HardDisk
3649 && mMediaData.isBackedUp())
3650 {
3651 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3652
3653 /* check if the medium was attached to the VM before we started
3654 * changing attachments in which case the attachment just needs to
3655 * be restored */
3656 if ((pAttachTemp = findAttachment(oldAtts, medium)))
3657 {
3658 AssertReturn(!fIndirect, E_FAIL);
3659
3660 /* see if it's the same bus/channel/device */
3661 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
3662 {
3663 /* the simplest case: restore the whole attachment
3664 * and return, nothing else to do */
3665 mMediaData->mAttachments.push_back(pAttachTemp);
3666 return S_OK;
3667 }
3668
3669 /* bus/channel/device differ; we need a new attachment object,
3670 * but don't try to associate it again */
3671 associate = false;
3672 break;
3673 }
3674 }
3675
3676 /* go further only if the attachment is to be indirect */
3677 if (!fIndirect)
3678 break;
3679
3680 /* perform the so called smart attachment logic for indirect
3681 * attachments. Note that smart attachment is only applicable to base
3682 * hard disks. */
3683
3684 if (medium->getParent().isNull())
3685 {
3686 /* first, investigate the backup copy of the current hard disk
3687 * attachments to make it possible to re-attach existing diffs to
3688 * another device slot w/o losing their contents */
3689 if (mMediaData.isBackedUp())
3690 {
3691 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3692
3693 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3694 uint32_t foundLevel = 0;
3695
3696 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
3697 it != oldAtts.end();
3698 ++it)
3699 {
3700 uint32_t level = 0;
3701 MediumAttachment *pAttach = *it;
3702 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3703 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3704 if (pMedium.isNull())
3705 continue;
3706
3707 if (pMedium->getBase(&level) == medium)
3708 {
3709 /* skip the hard disk if its currently attached (we
3710 * cannot attach the same hard disk twice) */
3711 if (findAttachment(mMediaData->mAttachments,
3712 pMedium))
3713 continue;
3714
3715 /* matched device, channel and bus (i.e. attached to the
3716 * same place) will win and immediately stop the search;
3717 * otherwise the attachment that has the youngest
3718 * descendant of medium will be used
3719 */
3720 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
3721 {
3722 /* the simplest case: restore the whole attachment
3723 * and return, nothing else to do */
3724 mMediaData->mAttachments.push_back(*it);
3725 return S_OK;
3726 }
3727 else if ( foundIt == oldAtts.end()
3728 || level > foundLevel /* prefer younger */
3729 )
3730 {
3731 foundIt = it;
3732 foundLevel = level;
3733 }
3734 }
3735 }
3736
3737 if (foundIt != oldAtts.end())
3738 {
3739 /* use the previously attached hard disk */
3740 medium = (*foundIt)->getMedium();
3741 mediumCaller.attach(medium);
3742 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3743 mediumLock.attach(medium);
3744 /* not implicit, doesn't require association with this VM */
3745 fIndirect = false;
3746 associate = false;
3747 /* go right to the MediumAttachment creation */
3748 break;
3749 }
3750 }
3751
3752 /* must give up the medium lock and medium tree lock as below we
3753 * go over snapshots, which needs a lock with higher lock order. */
3754 mediumLock.release();
3755 treeLock.release();
3756
3757 /* then, search through snapshots for the best diff in the given
3758 * hard disk's chain to base the new diff on */
3759
3760 ComObjPtr<Medium> base;
3761 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3762 while (snap)
3763 {
3764 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3765
3766 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
3767
3768 MediumAttachment *pAttachFound = NULL;
3769 uint32_t foundLevel = 0;
3770
3771 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
3772 it != snapAtts.end();
3773 ++it)
3774 {
3775 MediumAttachment *pAttach = *it;
3776 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3777 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3778 if (pMedium.isNull())
3779 continue;
3780
3781 uint32_t level = 0;
3782 if (pMedium->getBase(&level) == medium)
3783 {
3784 /* matched device, channel and bus (i.e. attached to the
3785 * same place) will win and immediately stop the search;
3786 * otherwise the attachment that has the youngest
3787 * descendant of medium will be used
3788 */
3789 if ( pAttach->getDevice() == aDevice
3790 && pAttach->getPort() == aControllerPort
3791 && pAttach->getControllerName() == aControllerName
3792 )
3793 {
3794 pAttachFound = pAttach;
3795 break;
3796 }
3797 else if ( !pAttachFound
3798 || level > foundLevel /* prefer younger */
3799 )
3800 {
3801 pAttachFound = pAttach;
3802 foundLevel = level;
3803 }
3804 }
3805 }
3806
3807 if (pAttachFound)
3808 {
3809 base = pAttachFound->getMedium();
3810 break;
3811 }
3812
3813 snap = snap->getParent();
3814 }
3815
3816 /* re-lock medium tree and the medium, as we need it below */
3817 treeLock.acquire();
3818 mediumLock.acquire();
3819
3820 /* found a suitable diff, use it as a base */
3821 if (!base.isNull())
3822 {
3823 medium = base;
3824 mediumCaller.attach(medium);
3825 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3826 mediumLock.attach(medium);
3827 }
3828 }
3829
3830 Utf8Str strFullSnapshotFolder;
3831 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3832
3833 ComObjPtr<Medium> diff;
3834 diff.createObject();
3835 // store this diff in the same registry as the parent
3836 Guid uuidRegistryParent;
3837 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
3838 {
3839 // parent image has no registry: this can happen if we're attaching a new immutable
3840 // image that has not yet been attached (medium then points to the base and we're
3841 // creating the diff image for the immutable, and the parent is not yet registered);
3842 // put the parent in the machine registry then
3843 mediumLock.release();
3844 treeLock.release();
3845 alock.release();
3846 addMediumToRegistry(medium);
3847 alock.acquire();
3848 treeLock.acquire();
3849 mediumLock.acquire();
3850 medium->getFirstRegistryMachineId(uuidRegistryParent);
3851 }
3852 rc = diff->init(mParent,
3853 medium->getPreferredDiffFormat(),
3854 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3855 uuidRegistryParent);
3856 if (FAILED(rc)) return rc;
3857
3858 /* Apply the normal locking logic to the entire chain. */
3859 MediumLockList *pMediumLockList(new MediumLockList());
3860 mediumLock.release();
3861 treeLock.release();
3862 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
3863 true /* fMediumLockWrite */,
3864 medium,
3865 *pMediumLockList);
3866 treeLock.acquire();
3867 mediumLock.acquire();
3868 if (SUCCEEDED(rc))
3869 {
3870 mediumLock.release();
3871 treeLock.release();
3872 rc = pMediumLockList->Lock();
3873 treeLock.acquire();
3874 mediumLock.acquire();
3875 if (FAILED(rc))
3876 setError(rc,
3877 tr("Could not lock medium when creating diff '%s'"),
3878 diff->getLocationFull().c_str());
3879 else
3880 {
3881 /* will release the lock before the potentially lengthy
3882 * operation, so protect with the special state */
3883 MachineState_T oldState = mData->mMachineState;
3884 setMachineState(MachineState_SettingUp);
3885
3886 mediumLock.release();
3887 treeLock.release();
3888 alock.release();
3889
3890 rc = medium->createDiffStorage(diff,
3891 MediumVariant_Standard,
3892 pMediumLockList,
3893 NULL /* aProgress */,
3894 true /* aWait */);
3895
3896 alock.acquire();
3897 treeLock.acquire();
3898 mediumLock.acquire();
3899
3900 setMachineState(oldState);
3901 }
3902 }
3903
3904 /* Unlock the media and free the associated memory. */
3905 delete pMediumLockList;
3906
3907 if (FAILED(rc)) return rc;
3908
3909 /* use the created diff for the actual attachment */
3910 medium = diff;
3911 mediumCaller.attach(medium);
3912 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3913 mediumLock.attach(medium);
3914 }
3915 while (0);
3916
3917 ComObjPtr<MediumAttachment> attachment;
3918 attachment.createObject();
3919 rc = attachment->init(this,
3920 medium,
3921 aControllerName,
3922 aControllerPort,
3923 aDevice,
3924 aType,
3925 fIndirect,
3926 false /* fPassthrough */,
3927 false /* fTempEject */,
3928 false /* fNonRotational */,
3929 false /* fDiscard */,
3930 Utf8Str::Empty);
3931 if (FAILED(rc)) return rc;
3932
3933 if (associate && !medium.isNull())
3934 {
3935 // as the last step, associate the medium to the VM
3936 rc = medium->addBackReference(mData->mUuid);
3937 // here we can fail because of Deleting, or being in process of creating a Diff
3938 if (FAILED(rc)) return rc;
3939
3940 mediumLock.release();
3941 treeLock.release();
3942 alock.release();
3943 addMediumToRegistry(medium);
3944 alock.acquire();
3945 treeLock.acquire();
3946 mediumLock.acquire();
3947 }
3948
3949 /* success: finally remember the attachment */
3950 setModified(IsModified_Storage);
3951 mMediaData.backup();
3952 mMediaData->mAttachments.push_back(attachment);
3953
3954 mediumLock.release();
3955 treeLock.release();
3956 alock.release();
3957
3958 if (fHotplug)
3959 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */);
3960
3961 mParent->saveModifiedRegistries();
3962
3963 return rc;
3964}
3965
3966STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
3967 LONG aDevice)
3968{
3969 CheckComArgStrNotEmptyOrNull(aControllerName);
3970
3971 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
3972 aControllerName, aControllerPort, aDevice));
3973
3974 AutoCaller autoCaller(this);
3975 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3976
3977 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3978
3979 HRESULT rc = checkStateDependency(MutableStateDep);
3980 if (FAILED(rc)) return rc;
3981
3982 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3983
3984 /* Check for an existing controller. */
3985 ComObjPtr<StorageController> ctl;
3986 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
3987 if (FAILED(rc)) return rc;
3988
3989 StorageControllerType_T ctrlType;
3990 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3991 if (FAILED(rc))
3992 return setError(E_FAIL,
3993 tr("Could not get type of controller '%ls'"),
3994 aControllerName);
3995
3996 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3997 bool fHotplug = false;
3998 if (Global::IsOnlineOrTransient(mData->mMachineState))
3999 fHotplug = true;
4000
4001 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4002 return setError(VBOX_E_INVALID_VM_STATE,
4003 tr("Controller '%ls' does not support hotplugging"),
4004 aControllerName);
4005
4006 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4007 aControllerName,
4008 aControllerPort,
4009 aDevice);
4010 if (!pAttach)
4011 return setError(VBOX_E_OBJECT_NOT_FOUND,
4012 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4013 aDevice, aControllerPort, aControllerName);
4014
4015 /*
4016 * The VM has to detach the device before we delete any implicit diffs.
4017 * If this fails we can roll back without loosing data.
4018 */
4019 if (fHotplug)
4020 {
4021 alock.release();
4022 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */);
4023 alock.acquire();
4024 }
4025 if (FAILED(rc)) return rc;
4026
4027 /* If we are here everything went well and we can delete the implicit now. */
4028 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4029
4030 alock.release();
4031
4032 mParent->saveModifiedRegistries();
4033
4034 return rc;
4035}
4036
4037STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4038 LONG aDevice, BOOL aPassthrough)
4039{
4040 CheckComArgStrNotEmptyOrNull(aControllerName);
4041
4042 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4043 aControllerName, aControllerPort, aDevice, aPassthrough));
4044
4045 AutoCaller autoCaller(this);
4046 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4047
4048 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4049
4050 HRESULT rc = checkStateDependency(MutableStateDep);
4051 if (FAILED(rc)) return rc;
4052
4053 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4054
4055 if (Global::IsOnlineOrTransient(mData->mMachineState))
4056 return setError(VBOX_E_INVALID_VM_STATE,
4057 tr("Invalid machine state: %s"),
4058 Global::stringifyMachineState(mData->mMachineState));
4059
4060 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4061 aControllerName,
4062 aControllerPort,
4063 aDevice);
4064 if (!pAttach)
4065 return setError(VBOX_E_OBJECT_NOT_FOUND,
4066 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4067 aDevice, aControllerPort, aControllerName);
4068
4069
4070 setModified(IsModified_Storage);
4071 mMediaData.backup();
4072
4073 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4074
4075 if (pAttach->getType() != DeviceType_DVD)
4076 return setError(E_INVALIDARG,
4077 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4078 aDevice, aControllerPort, aControllerName);
4079 pAttach->updatePassthrough(!!aPassthrough);
4080
4081 return S_OK;
4082}
4083
4084STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4085 LONG aDevice, BOOL aTemporaryEject)
4086{
4087 CheckComArgStrNotEmptyOrNull(aControllerName);
4088
4089 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4090 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4091
4092 AutoCaller autoCaller(this);
4093 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4094
4095 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4096
4097 HRESULT rc = checkStateDependency(MutableStateDep);
4098 if (FAILED(rc)) return rc;
4099
4100 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4101 aControllerName,
4102 aControllerPort,
4103 aDevice);
4104 if (!pAttach)
4105 return setError(VBOX_E_OBJECT_NOT_FOUND,
4106 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4107 aDevice, aControllerPort, aControllerName);
4108
4109
4110 setModified(IsModified_Storage);
4111 mMediaData.backup();
4112
4113 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4114
4115 if (pAttach->getType() != DeviceType_DVD)
4116 return setError(E_INVALIDARG,
4117 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4118 aDevice, aControllerPort, aControllerName);
4119 pAttach->updateTempEject(!!aTemporaryEject);
4120
4121 return S_OK;
4122}
4123
4124STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4125 LONG aDevice, BOOL aNonRotational)
4126{
4127 CheckComArgStrNotEmptyOrNull(aControllerName);
4128
4129 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4130 aControllerName, aControllerPort, aDevice, aNonRotational));
4131
4132 AutoCaller autoCaller(this);
4133 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4134
4135 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4136
4137 HRESULT rc = checkStateDependency(MutableStateDep);
4138 if (FAILED(rc)) return rc;
4139
4140 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4141
4142 if (Global::IsOnlineOrTransient(mData->mMachineState))
4143 return setError(VBOX_E_INVALID_VM_STATE,
4144 tr("Invalid machine state: %s"),
4145 Global::stringifyMachineState(mData->mMachineState));
4146
4147 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4148 aControllerName,
4149 aControllerPort,
4150 aDevice);
4151 if (!pAttach)
4152 return setError(VBOX_E_OBJECT_NOT_FOUND,
4153 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4154 aDevice, aControllerPort, aControllerName);
4155
4156
4157 setModified(IsModified_Storage);
4158 mMediaData.backup();
4159
4160 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4161
4162 if (pAttach->getType() != DeviceType_HardDisk)
4163 return setError(E_INVALIDARG,
4164 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"),
4165 aDevice, aControllerPort, aControllerName);
4166 pAttach->updateNonRotational(!!aNonRotational);
4167
4168 return S_OK;
4169}
4170
4171STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4172 LONG aDevice, BOOL aDiscard)
4173{
4174 CheckComArgStrNotEmptyOrNull(aControllerName);
4175
4176 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4177 aControllerName, aControllerPort, aDevice, aDiscard));
4178
4179 AutoCaller autoCaller(this);
4180 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4181
4182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4183
4184 HRESULT rc = checkStateDependency(MutableStateDep);
4185 if (FAILED(rc)) return rc;
4186
4187 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4188
4189 if (Global::IsOnlineOrTransient(mData->mMachineState))
4190 return setError(VBOX_E_INVALID_VM_STATE,
4191 tr("Invalid machine state: %s"),
4192 Global::stringifyMachineState(mData->mMachineState));
4193
4194 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4195 aControllerName,
4196 aControllerPort,
4197 aDevice);
4198 if (!pAttach)
4199 return setError(VBOX_E_OBJECT_NOT_FOUND,
4200 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4201 aDevice, aControllerPort, aControllerName);
4202
4203
4204 setModified(IsModified_Storage);
4205 mMediaData.backup();
4206
4207 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4208
4209 if (pAttach->getType() != DeviceType_HardDisk)
4210 return setError(E_INVALIDARG,
4211 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"),
4212 aDevice, aControllerPort, aControllerName);
4213 pAttach->updateDiscard(!!aDiscard);
4214
4215 return S_OK;
4216}
4217
4218STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4219 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4220{
4221 CheckComArgStrNotEmptyOrNull(aControllerName);
4222
4223 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4224 aControllerName, aControllerPort, aDevice));
4225
4226 AutoCaller autoCaller(this);
4227 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4228
4229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4230
4231 HRESULT rc = checkStateDependency(MutableStateDep);
4232 if (FAILED(rc)) return rc;
4233
4234 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4235
4236 if (Global::IsOnlineOrTransient(mData->mMachineState))
4237 return setError(VBOX_E_INVALID_VM_STATE,
4238 tr("Invalid machine state: %s"),
4239 Global::stringifyMachineState(mData->mMachineState));
4240
4241 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4242 aControllerName,
4243 aControllerPort,
4244 aDevice);
4245 if (!pAttach)
4246 return setError(VBOX_E_OBJECT_NOT_FOUND,
4247 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4248 aDevice, aControllerPort, aControllerName);
4249
4250
4251 setModified(IsModified_Storage);
4252 mMediaData.backup();
4253
4254 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4255 if (aBandwidthGroup && group.isNull())
4256 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4257
4258 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4259
4260 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4261 if (strBandwidthGroupOld.isNotEmpty())
4262 {
4263 /* Get the bandwidth group object and release it - this must not fail. */
4264 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4265 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4266 Assert(SUCCEEDED(rc));
4267
4268 pBandwidthGroupOld->release();
4269 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4270 }
4271
4272 if (!group.isNull())
4273 {
4274 group->reference();
4275 pAttach->updateBandwidthGroup(group->getName());
4276 }
4277
4278 return S_OK;
4279}
4280
4281
4282STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4283 LONG aControllerPort,
4284 LONG aDevice,
4285 IMedium *aMedium,
4286 BOOL aForce)
4287{
4288 int rc = S_OK;
4289 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4290 aControllerName, aControllerPort, aDevice, aForce));
4291
4292 CheckComArgStrNotEmptyOrNull(aControllerName);
4293
4294 AutoCaller autoCaller(this);
4295 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4296
4297 // request the host lock first, since might be calling Host methods for getting host drives;
4298 // next, protect the media tree all the while we're in here, as well as our member variables
4299 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4300 this->lockHandle(),
4301 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4302
4303 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4304 aControllerName,
4305 aControllerPort,
4306 aDevice);
4307 if (pAttach.isNull())
4308 return setError(VBOX_E_OBJECT_NOT_FOUND,
4309 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4310 aDevice, aControllerPort, aControllerName);
4311
4312 /* Remember previously mounted medium. The medium before taking the
4313 * backup is not necessarily the same thing. */
4314 ComObjPtr<Medium> oldmedium;
4315 oldmedium = pAttach->getMedium();
4316
4317 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4318 if (aMedium && pMedium.isNull())
4319 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4320
4321 AutoCaller mediumCaller(pMedium);
4322 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4323
4324 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4325 if (pMedium)
4326 {
4327 DeviceType_T mediumType = pAttach->getType();
4328 switch (mediumType)
4329 {
4330 case DeviceType_DVD:
4331 case DeviceType_Floppy:
4332 break;
4333
4334 default:
4335 return setError(VBOX_E_INVALID_OBJECT_STATE,
4336 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
4337 aControllerPort,
4338 aDevice,
4339 aControllerName);
4340 }
4341 }
4342
4343 setModified(IsModified_Storage);
4344 mMediaData.backup();
4345
4346 {
4347 // The backup operation makes the pAttach reference point to the
4348 // old settings. Re-get the correct reference.
4349 pAttach = findAttachment(mMediaData->mAttachments,
4350 aControllerName,
4351 aControllerPort,
4352 aDevice);
4353 if (!oldmedium.isNull())
4354 oldmedium->removeBackReference(mData->mUuid);
4355 if (!pMedium.isNull())
4356 {
4357 pMedium->addBackReference(mData->mUuid);
4358
4359 mediumLock.release();
4360 multiLock.release();
4361 addMediumToRegistry(pMedium);
4362 multiLock.acquire();
4363 mediumLock.acquire();
4364 }
4365
4366 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4367 pAttach->updateMedium(pMedium);
4368 }
4369
4370 setModified(IsModified_Storage);
4371
4372 mediumLock.release();
4373 multiLock.release();
4374 rc = onMediumChange(pAttach, aForce);
4375 multiLock.acquire();
4376 mediumLock.acquire();
4377
4378 /* On error roll back this change only. */
4379 if (FAILED(rc))
4380 {
4381 if (!pMedium.isNull())
4382 pMedium->removeBackReference(mData->mUuid);
4383 pAttach = findAttachment(mMediaData->mAttachments,
4384 aControllerName,
4385 aControllerPort,
4386 aDevice);
4387 /* If the attachment is gone in the meantime, bail out. */
4388 if (pAttach.isNull())
4389 return rc;
4390 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4391 if (!oldmedium.isNull())
4392 oldmedium->addBackReference(mData->mUuid);
4393 pAttach->updateMedium(oldmedium);
4394 }
4395
4396 mediumLock.release();
4397 multiLock.release();
4398
4399 mParent->saveModifiedRegistries();
4400
4401 return rc;
4402}
4403
4404STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
4405 LONG aControllerPort,
4406 LONG aDevice,
4407 IMedium **aMedium)
4408{
4409 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4410 aControllerName, aControllerPort, aDevice));
4411
4412 CheckComArgStrNotEmptyOrNull(aControllerName);
4413 CheckComArgOutPointerValid(aMedium);
4414
4415 AutoCaller autoCaller(this);
4416 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4417
4418 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4419
4420 *aMedium = NULL;
4421
4422 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4423 aControllerName,
4424 aControllerPort,
4425 aDevice);
4426 if (pAttach.isNull())
4427 return setError(VBOX_E_OBJECT_NOT_FOUND,
4428 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4429 aDevice, aControllerPort, aControllerName);
4430
4431 pAttach->getMedium().queryInterfaceTo(aMedium);
4432
4433 return S_OK;
4434}
4435
4436STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
4437{
4438 CheckComArgOutPointerValid(port);
4439 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
4440
4441 AutoCaller autoCaller(this);
4442 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4443
4444 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4445
4446 mSerialPorts[slot].queryInterfaceTo(port);
4447
4448 return S_OK;
4449}
4450
4451STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
4452{
4453 CheckComArgOutPointerValid(port);
4454 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
4455
4456 AutoCaller autoCaller(this);
4457 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4458
4459 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4460
4461 mParallelPorts[slot].queryInterfaceTo(port);
4462
4463 return S_OK;
4464}
4465
4466STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
4467{
4468 CheckComArgOutPointerValid(adapter);
4469 CheckComArgExpr(slot, slot < mNetworkAdapters.size());
4470
4471 AutoCaller autoCaller(this);
4472 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4473
4474 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4475
4476 mNetworkAdapters[slot].queryInterfaceTo(adapter);
4477
4478 return S_OK;
4479}
4480
4481STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
4482{
4483 if (ComSafeArrayOutIsNull(aKeys))
4484 return E_POINTER;
4485
4486 AutoCaller autoCaller(this);
4487 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4488
4489 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4490
4491 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4492 int i = 0;
4493 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4494 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4495 ++it, ++i)
4496 {
4497 const Utf8Str &strKey = it->first;
4498 strKey.cloneTo(&saKeys[i]);
4499 }
4500 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
4501
4502 return S_OK;
4503 }
4504
4505 /**
4506 * @note Locks this object for reading.
4507 */
4508STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
4509 BSTR *aValue)
4510{
4511 CheckComArgStrNotEmptyOrNull(aKey);
4512 CheckComArgOutPointerValid(aValue);
4513
4514 AutoCaller autoCaller(this);
4515 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4516
4517 /* start with nothing found */
4518 Bstr bstrResult("");
4519
4520 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4521
4522 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
4523 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4524 // found:
4525 bstrResult = it->second; // source is a Utf8Str
4526
4527 /* return the result to caller (may be empty) */
4528 bstrResult.cloneTo(aValue);
4529
4530 return S_OK;
4531}
4532
4533 /**
4534 * @note Locks mParent for writing + this object for writing.
4535 */
4536STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
4537{
4538 CheckComArgStrNotEmptyOrNull(aKey);
4539
4540 AutoCaller autoCaller(this);
4541 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4542
4543 Utf8Str strKey(aKey);
4544 Utf8Str strValue(aValue);
4545 Utf8Str strOldValue; // empty
4546
4547 // locking note: we only hold the read lock briefly to look up the old value,
4548 // then release it and call the onExtraCanChange callbacks. There is a small
4549 // chance of a race insofar as the callback might be called twice if two callers
4550 // change the same key at the same time, but that's a much better solution
4551 // than the deadlock we had here before. The actual changing of the extradata
4552 // is then performed under the write lock and race-free.
4553
4554 // look up the old value first; if nothing has changed then we need not do anything
4555 {
4556 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4557 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
4558 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4559 strOldValue = it->second;
4560 }
4561
4562 bool fChanged;
4563 if ((fChanged = (strOldValue != strValue)))
4564 {
4565 // ask for permission from all listeners outside the locks;
4566 // onExtraDataCanChange() only briefly requests the VirtualBox
4567 // lock to copy the list of callbacks to invoke
4568 Bstr error;
4569 Bstr bstrValue(aValue);
4570
4571 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
4572 {
4573 const char *sep = error.isEmpty() ? "" : ": ";
4574 CBSTR err = error.raw();
4575 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4576 sep, err));
4577 return setError(E_ACCESSDENIED,
4578 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
4579 aKey,
4580 bstrValue.raw(),
4581 sep,
4582 err);
4583 }
4584
4585 // data is changing and change not vetoed: then write it out under the lock
4586 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4587
4588 if (isSnapshotMachine())
4589 {
4590 HRESULT rc = checkStateDependency(MutableStateDep);
4591 if (FAILED(rc)) return rc;
4592 }
4593
4594 if (strValue.isEmpty())
4595 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
4596 else
4597 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
4598 // creates a new key if needed
4599
4600 bool fNeedsGlobalSaveSettings = false;
4601 saveSettings(&fNeedsGlobalSaveSettings);
4602
4603 if (fNeedsGlobalSaveSettings)
4604 {
4605 // save the global settings; for that we should hold only the VirtualBox lock
4606 alock.release();
4607 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4608 mParent->saveSettings();
4609 }
4610 }
4611
4612 // fire notification outside the lock
4613 if (fChanged)
4614 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
4615
4616 return S_OK;
4617}
4618
4619STDMETHODIMP Machine::SaveSettings()
4620{
4621 AutoCaller autoCaller(this);
4622 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4623
4624 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4625
4626 /* when there was auto-conversion, we want to save the file even if
4627 * the VM is saved */
4628 HRESULT rc = checkStateDependency(MutableStateDep);
4629 if (FAILED(rc)) return rc;
4630
4631 /* the settings file path may never be null */
4632 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4633
4634 /* save all VM data excluding snapshots */
4635 bool fNeedsGlobalSaveSettings = false;
4636 rc = saveSettings(&fNeedsGlobalSaveSettings);
4637 mlock.release();
4638
4639 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4640 {
4641 // save the global settings; for that we should hold only the VirtualBox lock
4642 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4643 rc = mParent->saveSettings();
4644 }
4645
4646 return rc;
4647}
4648
4649STDMETHODIMP Machine::DiscardSettings()
4650{
4651 AutoCaller autoCaller(this);
4652 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4653
4654 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4655
4656 HRESULT rc = checkStateDependency(MutableStateDep);
4657 if (FAILED(rc)) return rc;
4658
4659 /*
4660 * during this rollback, the session will be notified if data has
4661 * been actually changed
4662 */
4663 rollback(true /* aNotify */);
4664
4665 return S_OK;
4666}
4667
4668/** @note Locks objects! */
4669STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
4670 ComSafeArrayOut(IMedium*, aMedia))
4671{
4672 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
4673 AutoLimitedCaller autoCaller(this);
4674 AssertComRCReturnRC(autoCaller.rc());
4675
4676 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4677
4678 Guid id(getId());
4679
4680 if (mData->mSession.mState != SessionState_Unlocked)
4681 return setError(VBOX_E_INVALID_OBJECT_STATE,
4682 tr("Cannot unregister the machine '%s' while it is locked"),
4683 mUserData->s.strName.c_str());
4684
4685 // wait for state dependents to drop to zero
4686 ensureNoStateDependencies();
4687
4688 if (!mData->mAccessible)
4689 {
4690 // inaccessible maschines can only be unregistered; uninitialize ourselves
4691 // here because currently there may be no unregistered that are inaccessible
4692 // (this state combination is not supported). Note releasing the caller and
4693 // leaving the lock before calling uninit()
4694 alock.release();
4695 autoCaller.release();
4696
4697 uninit();
4698
4699 mParent->unregisterMachine(this, id);
4700 // calls VirtualBox::saveSettings()
4701
4702 return S_OK;
4703 }
4704
4705 HRESULT rc = S_OK;
4706
4707 // discard saved state
4708 if (mData->mMachineState == MachineState_Saved)
4709 {
4710 // add the saved state file to the list of files the caller should delete
4711 Assert(!mSSData->strStateFilePath.isEmpty());
4712 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4713
4714 mSSData->strStateFilePath.setNull();
4715
4716 // unconditionally set the machine state to powered off, we now
4717 // know no session has locked the machine
4718 mData->mMachineState = MachineState_PoweredOff;
4719 }
4720
4721 size_t cSnapshots = 0;
4722 if (mData->mFirstSnapshot)
4723 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
4724 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
4725 // fail now before we start detaching media
4726 return setError(VBOX_E_INVALID_OBJECT_STATE,
4727 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
4728 mUserData->s.strName.c_str(), cSnapshots);
4729
4730 // This list collects the medium objects from all medium attachments
4731 // which we will detach from the machine and its snapshots, in a specific
4732 // order which allows for closing all media without getting "media in use"
4733 // errors, simply by going through the list from the front to the back:
4734 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4735 // and must be closed before the parent media from the snapshots, or closing the parents
4736 // will fail because they still have children);
4737 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4738 // the root ("first") snapshot of the machine.
4739 MediaList llMedia;
4740
4741 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
4742 && mMediaData->mAttachments.size()
4743 )
4744 {
4745 // we have media attachments: detach them all and add the Medium objects to our list
4746 if (cleanupMode != CleanupMode_UnregisterOnly)
4747 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
4748 else
4749 return setError(VBOX_E_INVALID_OBJECT_STATE,
4750 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
4751 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
4752 }
4753
4754 if (cSnapshots)
4755 {
4756 // autoCleanup must be true here, or we would have failed above
4757
4758 // add the media from the medium attachments of the snapshots to llMedia
4759 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4760 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4761 // into the children first
4762
4763 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4764 MachineState_T oldState = mData->mMachineState;
4765 mData->mMachineState = MachineState_DeletingSnapshot;
4766
4767 // make a copy of the first snapshot so the refcount does not drop to 0
4768 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
4769 // because of the AutoCaller voodoo)
4770 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4771
4772 // GO!
4773 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
4774
4775 mData->mMachineState = oldState;
4776 }
4777
4778 if (FAILED(rc))
4779 {
4780 rollbackMedia();
4781 return rc;
4782 }
4783
4784 // commit all the media changes made above
4785 commitMedia();
4786
4787 mData->mRegistered = false;
4788
4789 // machine lock no longer needed
4790 alock.release();
4791
4792 // return media to caller
4793 SafeIfaceArray<IMedium> sfaMedia(llMedia);
4794 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
4795
4796 mParent->unregisterMachine(this, id);
4797 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
4798
4799 return S_OK;
4800}
4801
4802struct Machine::DeleteTask
4803{
4804 ComObjPtr<Machine> pMachine;
4805 RTCList< ComPtr<IMedium> > llMediums;
4806 std::list<Utf8Str> llFilesToDelete;
4807 ComObjPtr<Progress> pProgress;
4808};
4809
4810STDMETHODIMP Machine::Delete(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
4811{
4812 LogFlowFuncEnter();
4813
4814 AutoCaller autoCaller(this);
4815 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4816
4817 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4818
4819 HRESULT rc = checkStateDependency(MutableStateDep);
4820 if (FAILED(rc)) return rc;
4821
4822 if (mData->mRegistered)
4823 return setError(VBOX_E_INVALID_VM_STATE,
4824 tr("Cannot delete settings of a registered machine"));
4825
4826 DeleteTask *pTask = new DeleteTask;
4827 pTask->pMachine = this;
4828 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
4829
4830 // collect files to delete
4831 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
4832
4833 for (size_t i = 0; i < sfaMedia.size(); ++i)
4834 {
4835 IMedium *pIMedium(sfaMedia[i]);
4836 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
4837 if (pMedium.isNull())
4838 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
4839 SafeArray<BSTR> ids;
4840 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
4841 if (FAILED(rc)) return rc;
4842 /* At this point the medium should not have any back references
4843 * anymore. If it has it is attached to another VM and *must* not
4844 * deleted. */
4845 if (ids.size() < 1)
4846 pTask->llMediums.append(pMedium);
4847 }
4848 if (mData->pMachineConfigFile->fileExists())
4849 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
4850
4851 pTask->pProgress.createObject();
4852 pTask->pProgress->init(getVirtualBox(),
4853 static_cast<IMachine*>(this) /* aInitiator */,
4854 Bstr(tr("Deleting files")).raw(),
4855 true /* fCancellable */,
4856 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
4857 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
4858
4859 int vrc = RTThreadCreate(NULL,
4860 Machine::deleteThread,
4861 (void*)pTask,
4862 0,
4863 RTTHREADTYPE_MAIN_WORKER,
4864 0,
4865 "MachineDelete");
4866
4867 pTask->pProgress.queryInterfaceTo(aProgress);
4868
4869 if (RT_FAILURE(vrc))
4870 {
4871 delete pTask;
4872 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
4873 }
4874
4875 LogFlowFuncLeave();
4876
4877 return S_OK;
4878}
4879
4880/**
4881 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
4882 * calls Machine::deleteTaskWorker() on the actual machine object.
4883 * @param Thread
4884 * @param pvUser
4885 * @return
4886 */
4887/*static*/
4888DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
4889{
4890 LogFlowFuncEnter();
4891
4892 DeleteTask *pTask = (DeleteTask*)pvUser;
4893 Assert(pTask);
4894 Assert(pTask->pMachine);
4895 Assert(pTask->pProgress);
4896
4897 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
4898 pTask->pProgress->notifyComplete(rc);
4899
4900 delete pTask;
4901
4902 LogFlowFuncLeave();
4903
4904 NOREF(Thread);
4905
4906 return VINF_SUCCESS;
4907}
4908
4909/**
4910 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
4911 * @param task
4912 * @return
4913 */
4914HRESULT Machine::deleteTaskWorker(DeleteTask &task)
4915{
4916 AutoCaller autoCaller(this);
4917 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4918
4919 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4920
4921 HRESULT rc = S_OK;
4922
4923 try
4924 {
4925 ULONG uLogHistoryCount = 3;
4926 ComPtr<ISystemProperties> systemProperties;
4927 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
4928 if (FAILED(rc)) throw rc;
4929
4930 if (!systemProperties.isNull())
4931 {
4932 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
4933 if (FAILED(rc)) throw rc;
4934 }
4935
4936 MachineState_T oldState = mData->mMachineState;
4937 setMachineState(MachineState_SettingUp);
4938 alock.release();
4939 for (size_t i = 0; i < task.llMediums.size(); ++i)
4940 {
4941 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
4942 {
4943 AutoCaller mac(pMedium);
4944 if (FAILED(mac.rc())) throw mac.rc();
4945 Utf8Str strLocation = pMedium->getLocationFull();
4946 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
4947 if (FAILED(rc)) throw rc;
4948 LogFunc(("Deleting file %s\n", strLocation.c_str()));
4949 }
4950 ComPtr<IProgress> pProgress2;
4951 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
4952 if (FAILED(rc)) throw rc;
4953 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
4954 if (FAILED(rc)) throw rc;
4955 /* Check the result of the asynchrony process. */
4956 LONG iRc;
4957 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
4958 if (FAILED(rc)) throw rc;
4959 /* If the thread of the progress object has an error, then
4960 * retrieve the error info from there, or it'll be lost. */
4961 if (FAILED(iRc))
4962 throw setError(ProgressErrorInfo(pProgress2));
4963 }
4964 setMachineState(oldState);
4965 alock.acquire();
4966
4967 // delete the files pushed on the task list by Machine::Delete()
4968 // (this includes saved states of the machine and snapshots and
4969 // medium storage files from the IMedium list passed in, and the
4970 // machine XML file)
4971 std::list<Utf8Str>::const_iterator it = task.llFilesToDelete.begin();
4972 while (it != task.llFilesToDelete.end())
4973 {
4974 const Utf8Str &strFile = *it;
4975 LogFunc(("Deleting file %s\n", strFile.c_str()));
4976 int vrc = RTFileDelete(strFile.c_str());
4977 if (RT_FAILURE(vrc))
4978 throw setError(VBOX_E_IPRT_ERROR,
4979 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
4980
4981 ++it;
4982 if (it == task.llFilesToDelete.end())
4983 {
4984 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
4985 if (FAILED(rc)) throw rc;
4986 break;
4987 }
4988
4989 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
4990 if (FAILED(rc)) throw rc;
4991 }
4992
4993 /* delete the settings only when the file actually exists */
4994 if (mData->pMachineConfigFile->fileExists())
4995 {
4996 /* Delete any backup or uncommitted XML files. Ignore failures.
4997 See the fSafe parameter of xml::XmlFileWriter::write for details. */
4998 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
4999 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5000 RTFileDelete(otherXml.c_str());
5001 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5002 RTFileDelete(otherXml.c_str());
5003
5004 /* delete the Logs folder, nothing important should be left
5005 * there (we don't check for errors because the user might have
5006 * some private files there that we don't want to delete) */
5007 Utf8Str logFolder;
5008 getLogFolder(logFolder);
5009 Assert(logFolder.length());
5010 if (RTDirExists(logFolder.c_str()))
5011 {
5012 /* Delete all VBox.log[.N] files from the Logs folder
5013 * (this must be in sync with the rotation logic in
5014 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5015 * files that may have been created by the GUI. */
5016 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5017 logFolder.c_str(), RTPATH_DELIMITER);
5018 RTFileDelete(log.c_str());
5019 log = Utf8StrFmt("%s%cVBox.png",
5020 logFolder.c_str(), RTPATH_DELIMITER);
5021 RTFileDelete(log.c_str());
5022 for (int i = uLogHistoryCount; i > 0; i--)
5023 {
5024 log = Utf8StrFmt("%s%cVBox.log.%d",
5025 logFolder.c_str(), RTPATH_DELIMITER, i);
5026 RTFileDelete(log.c_str());
5027 log = Utf8StrFmt("%s%cVBox.png.%d",
5028 logFolder.c_str(), RTPATH_DELIMITER, i);
5029 RTFileDelete(log.c_str());
5030 }
5031
5032 RTDirRemove(logFolder.c_str());
5033 }
5034
5035 /* delete the Snapshots folder, nothing important should be left
5036 * there (we don't check for errors because the user might have
5037 * some private files there that we don't want to delete) */
5038 Utf8Str strFullSnapshotFolder;
5039 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5040 Assert(!strFullSnapshotFolder.isEmpty());
5041 if (RTDirExists(strFullSnapshotFolder.c_str()))
5042 RTDirRemove(strFullSnapshotFolder.c_str());
5043
5044 // delete the directory that contains the settings file, but only
5045 // if it matches the VM name
5046 Utf8Str settingsDir;
5047 if (isInOwnDir(&settingsDir))
5048 RTDirRemove(settingsDir.c_str());
5049 }
5050
5051 alock.release();
5052
5053 mParent->saveModifiedRegistries();
5054 }
5055 catch (HRESULT aRC) { rc = aRC; }
5056
5057 return rc;
5058}
5059
5060STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5061{
5062 CheckComArgOutPointerValid(aSnapshot);
5063
5064 AutoCaller autoCaller(this);
5065 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5066
5067 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5068
5069 ComObjPtr<Snapshot> pSnapshot;
5070 HRESULT rc;
5071
5072 if (!aNameOrId || !*aNameOrId)
5073 // null case (caller wants root snapshot): findSnapshotById() handles this
5074 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5075 else
5076 {
5077 Guid uuid(aNameOrId);
5078 if (!uuid.isEmpty())
5079 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5080 else
5081 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5082 }
5083 pSnapshot.queryInterfaceTo(aSnapshot);
5084
5085 return rc;
5086}
5087
5088STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5089{
5090 CheckComArgStrNotEmptyOrNull(aName);
5091 CheckComArgStrNotEmptyOrNull(aHostPath);
5092
5093 AutoCaller autoCaller(this);
5094 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5095
5096 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5097
5098 HRESULT rc = checkStateDependency(MutableStateDep);
5099 if (FAILED(rc)) return rc;
5100
5101 Utf8Str strName(aName);
5102
5103 ComObjPtr<SharedFolder> sharedFolder;
5104 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5105 if (SUCCEEDED(rc))
5106 return setError(VBOX_E_OBJECT_IN_USE,
5107 tr("Shared folder named '%s' already exists"),
5108 strName.c_str());
5109
5110 sharedFolder.createObject();
5111 rc = sharedFolder->init(getMachine(),
5112 strName,
5113 aHostPath,
5114 !!aWritable,
5115 !!aAutoMount,
5116 true /* fFailOnError */);
5117 if (FAILED(rc)) return rc;
5118
5119 setModified(IsModified_SharedFolders);
5120 mHWData.backup();
5121 mHWData->mSharedFolders.push_back(sharedFolder);
5122
5123 /* inform the direct session if any */
5124 alock.release();
5125 onSharedFolderChange();
5126
5127 return S_OK;
5128}
5129
5130STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5131{
5132 CheckComArgStrNotEmptyOrNull(aName);
5133
5134 AutoCaller autoCaller(this);
5135 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5136
5137 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5138
5139 HRESULT rc = checkStateDependency(MutableStateDep);
5140 if (FAILED(rc)) return rc;
5141
5142 ComObjPtr<SharedFolder> sharedFolder;
5143 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5144 if (FAILED(rc)) return rc;
5145
5146 setModified(IsModified_SharedFolders);
5147 mHWData.backup();
5148 mHWData->mSharedFolders.remove(sharedFolder);
5149
5150 /* inform the direct session if any */
5151 alock.release();
5152 onSharedFolderChange();
5153
5154 return S_OK;
5155}
5156
5157STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5158{
5159 CheckComArgOutPointerValid(aCanShow);
5160
5161 /* start with No */
5162 *aCanShow = FALSE;
5163
5164 AutoCaller autoCaller(this);
5165 AssertComRCReturnRC(autoCaller.rc());
5166
5167 ComPtr<IInternalSessionControl> directControl;
5168 {
5169 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5170
5171 if (mData->mSession.mState != SessionState_Locked)
5172 return setError(VBOX_E_INVALID_VM_STATE,
5173 tr("Machine is not locked for session (session state: %s)"),
5174 Global::stringifySessionState(mData->mSession.mState));
5175
5176 directControl = mData->mSession.mDirectControl;
5177 }
5178
5179 /* ignore calls made after #OnSessionEnd() is called */
5180 if (!directControl)
5181 return S_OK;
5182
5183 LONG64 dummy;
5184 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5185}
5186
5187STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5188{
5189 CheckComArgOutPointerValid(aWinId);
5190
5191 AutoCaller autoCaller(this);
5192 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5193
5194 ComPtr<IInternalSessionControl> directControl;
5195 {
5196 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5197
5198 if (mData->mSession.mState != SessionState_Locked)
5199 return setError(E_FAIL,
5200 tr("Machine is not locked for session (session state: %s)"),
5201 Global::stringifySessionState(mData->mSession.mState));
5202
5203 directControl = mData->mSession.mDirectControl;
5204 }
5205
5206 /* ignore calls made after #OnSessionEnd() is called */
5207 if (!directControl)
5208 return S_OK;
5209
5210 BOOL dummy;
5211 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5212}
5213
5214#ifdef VBOX_WITH_GUEST_PROPS
5215/**
5216 * Look up a guest property in VBoxSVC's internal structures.
5217 */
5218HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5219 BSTR *aValue,
5220 LONG64 *aTimestamp,
5221 BSTR *aFlags) const
5222{
5223 using namespace guestProp;
5224
5225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5226 Utf8Str strName(aName);
5227 HWData::GuestPropertyList::const_iterator it;
5228
5229 for (it = mHWData->mGuestProperties.begin();
5230 it != mHWData->mGuestProperties.end(); ++it)
5231 {
5232 if (it->strName == strName)
5233 {
5234 char szFlags[MAX_FLAGS_LEN + 1];
5235 it->strValue.cloneTo(aValue);
5236 *aTimestamp = it->mTimestamp;
5237 writeFlags(it->mFlags, szFlags);
5238 Bstr(szFlags).cloneTo(aFlags);
5239 break;
5240 }
5241 }
5242 return S_OK;
5243}
5244
5245/**
5246 * Query the VM that a guest property belongs to for the property.
5247 * @returns E_ACCESSDENIED if the VM process is not available or not
5248 * currently handling queries and the lookup should then be done in
5249 * VBoxSVC.
5250 */
5251HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5252 BSTR *aValue,
5253 LONG64 *aTimestamp,
5254 BSTR *aFlags) const
5255{
5256 HRESULT rc;
5257 ComPtr<IInternalSessionControl> directControl;
5258 directControl = mData->mSession.mDirectControl;
5259
5260 /* fail if we were called after #OnSessionEnd() is called. This is a
5261 * silly race condition. */
5262
5263 if (!directControl)
5264 rc = E_ACCESSDENIED;
5265 else
5266 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5267 false /* isSetter */,
5268 aValue, aTimestamp, aFlags);
5269 return rc;
5270}
5271#endif // VBOX_WITH_GUEST_PROPS
5272
5273STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5274 BSTR *aValue,
5275 LONG64 *aTimestamp,
5276 BSTR *aFlags)
5277{
5278#ifndef VBOX_WITH_GUEST_PROPS
5279 ReturnComNotImplemented();
5280#else // VBOX_WITH_GUEST_PROPS
5281 CheckComArgStrNotEmptyOrNull(aName);
5282 CheckComArgOutPointerValid(aValue);
5283 CheckComArgOutPointerValid(aTimestamp);
5284 CheckComArgOutPointerValid(aFlags);
5285
5286 AutoCaller autoCaller(this);
5287 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5288
5289 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5290 if (rc == E_ACCESSDENIED)
5291 /* The VM is not running or the service is not (yet) accessible */
5292 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5293 return rc;
5294#endif // VBOX_WITH_GUEST_PROPS
5295}
5296
5297STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5298{
5299 LONG64 dummyTimestamp;
5300 Bstr dummyFlags;
5301 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5302}
5303
5304STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5305{
5306 Bstr dummyValue;
5307 Bstr dummyFlags;
5308 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5309}
5310
5311#ifdef VBOX_WITH_GUEST_PROPS
5312/**
5313 * Set a guest property in VBoxSVC's internal structures.
5314 */
5315HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5316 IN_BSTR aFlags)
5317{
5318 using namespace guestProp;
5319
5320 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5321 HRESULT rc = S_OK;
5322 HWData::GuestProperty property;
5323 property.mFlags = NILFLAG;
5324 bool found = false;
5325
5326 rc = checkStateDependency(MutableStateDep);
5327 if (FAILED(rc)) return rc;
5328
5329 try
5330 {
5331 Utf8Str utf8Name(aName);
5332 Utf8Str utf8Flags(aFlags);
5333 uint32_t fFlags = NILFLAG;
5334 if ( (aFlags != NULL)
5335 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags))
5336 )
5337 return setError(E_INVALIDARG,
5338 tr("Invalid flag values: '%ls'"),
5339 aFlags);
5340
5341 /** @todo r=bird: see efficiency rant in PushGuestProperty. (Yeah, I
5342 * know, this is simple and do an OK job atm.) */
5343 HWData::GuestPropertyList::iterator it;
5344 for (it = mHWData->mGuestProperties.begin();
5345 it != mHWData->mGuestProperties.end(); ++it)
5346 if (it->strName == utf8Name)
5347 {
5348 property = *it;
5349 if (it->mFlags & (RDONLYHOST))
5350 rc = setError(E_ACCESSDENIED,
5351 tr("The property '%ls' cannot be changed by the host"),
5352 aName);
5353 else
5354 {
5355 setModified(IsModified_MachineData);
5356 mHWData.backup(); // @todo r=dj backup in a loop?!?
5357
5358 /* The backup() operation invalidates our iterator, so
5359 * get a new one. */
5360 for (it = mHWData->mGuestProperties.begin();
5361 it->strName != utf8Name;
5362 ++it)
5363 ;
5364 mHWData->mGuestProperties.erase(it);
5365 }
5366 found = true;
5367 break;
5368 }
5369 if (found && SUCCEEDED(rc))
5370 {
5371 if (aValue)
5372 {
5373 RTTIMESPEC time;
5374 property.strValue = aValue;
5375 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5376 if (aFlags != NULL)
5377 property.mFlags = fFlags;
5378 mHWData->mGuestProperties.push_back(property);
5379 }
5380 }
5381 else if (SUCCEEDED(rc) && aValue)
5382 {
5383 RTTIMESPEC time;
5384 setModified(IsModified_MachineData);
5385 mHWData.backup();
5386 property.strName = aName;
5387 property.strValue = aValue;
5388 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5389 property.mFlags = fFlags;
5390 mHWData->mGuestProperties.push_back(property);
5391 }
5392 if ( SUCCEEDED(rc)
5393 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5394 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5395 RTSTR_MAX,
5396 utf8Name.c_str(),
5397 RTSTR_MAX,
5398 NULL)
5399 )
5400 )
5401 {
5402 /** @todo r=bird: Why aren't we leaving the lock here? The
5403 * same code in PushGuestProperty does... */
5404 mParent->onGuestPropertyChange(mData->mUuid, aName,
5405 aValue ? aValue : Bstr("").raw(),
5406 aFlags ? aFlags : Bstr("").raw());
5407 }
5408 }
5409 catch (std::bad_alloc &)
5410 {
5411 rc = E_OUTOFMEMORY;
5412 }
5413
5414 return rc;
5415}
5416
5417/**
5418 * Set a property on the VM that that property belongs to.
5419 * @returns E_ACCESSDENIED if the VM process is not available or not
5420 * currently handling queries and the setting should then be done in
5421 * VBoxSVC.
5422 */
5423HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
5424 IN_BSTR aFlags)
5425{
5426 HRESULT rc;
5427
5428 try
5429 {
5430 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5431
5432 BSTR dummy = NULL; /* will not be changed (setter) */
5433 LONG64 dummy64;
5434 if (!directControl)
5435 rc = E_ACCESSDENIED;
5436 else
5437 /** @todo Fix when adding DeleteGuestProperty(),
5438 see defect. */
5439 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
5440 true /* isSetter */,
5441 &dummy, &dummy64, &dummy);
5442 }
5443 catch (std::bad_alloc &)
5444 {
5445 rc = E_OUTOFMEMORY;
5446 }
5447
5448 return rc;
5449}
5450#endif // VBOX_WITH_GUEST_PROPS
5451
5452STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
5453 IN_BSTR aFlags)
5454{
5455#ifndef VBOX_WITH_GUEST_PROPS
5456 ReturnComNotImplemented();
5457#else // VBOX_WITH_GUEST_PROPS
5458 CheckComArgStrNotEmptyOrNull(aName);
5459 CheckComArgMaybeNull(aFlags);
5460 CheckComArgMaybeNull(aValue);
5461
5462 AutoCaller autoCaller(this);
5463 if (FAILED(autoCaller.rc()))
5464 return autoCaller.rc();
5465
5466 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
5467 if (rc == E_ACCESSDENIED)
5468 /* The VM is not running or the service is not (yet) accessible */
5469 rc = setGuestPropertyToService(aName, aValue, aFlags);
5470 return rc;
5471#endif // VBOX_WITH_GUEST_PROPS
5472}
5473
5474STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
5475{
5476 return SetGuestProperty(aName, aValue, NULL);
5477}
5478
5479STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
5480{
5481 return SetGuestProperty(aName, NULL, NULL);
5482}
5483
5484#ifdef VBOX_WITH_GUEST_PROPS
5485/**
5486 * Enumerate the guest properties in VBoxSVC's internal structures.
5487 */
5488HRESULT Machine::enumerateGuestPropertiesInService
5489 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5490 ComSafeArrayOut(BSTR, aValues),
5491 ComSafeArrayOut(LONG64, aTimestamps),
5492 ComSafeArrayOut(BSTR, aFlags))
5493{
5494 using namespace guestProp;
5495
5496 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5497 Utf8Str strPatterns(aPatterns);
5498
5499 /*
5500 * Look for matching patterns and build up a list.
5501 */
5502 HWData::GuestPropertyList propList;
5503 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
5504 it != mHWData->mGuestProperties.end();
5505 ++it)
5506 if ( strPatterns.isEmpty()
5507 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5508 RTSTR_MAX,
5509 it->strName.c_str(),
5510 RTSTR_MAX,
5511 NULL)
5512 )
5513 propList.push_back(*it);
5514
5515 /*
5516 * And build up the arrays for returning the property information.
5517 */
5518 size_t cEntries = propList.size();
5519 SafeArray<BSTR> names(cEntries);
5520 SafeArray<BSTR> values(cEntries);
5521 SafeArray<LONG64> timestamps(cEntries);
5522 SafeArray<BSTR> flags(cEntries);
5523 size_t iProp = 0;
5524 for (HWData::GuestPropertyList::iterator it = propList.begin();
5525 it != propList.end();
5526 ++it)
5527 {
5528 char szFlags[MAX_FLAGS_LEN + 1];
5529 it->strName.cloneTo(&names[iProp]);
5530 it->strValue.cloneTo(&values[iProp]);
5531 timestamps[iProp] = it->mTimestamp;
5532 writeFlags(it->mFlags, szFlags);
5533 Bstr(szFlags).cloneTo(&flags[iProp]);
5534 ++iProp;
5535 }
5536 names.detachTo(ComSafeArrayOutArg(aNames));
5537 values.detachTo(ComSafeArrayOutArg(aValues));
5538 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
5539 flags.detachTo(ComSafeArrayOutArg(aFlags));
5540 return S_OK;
5541}
5542
5543/**
5544 * Enumerate the properties managed by a VM.
5545 * @returns E_ACCESSDENIED if the VM process is not available or not
5546 * currently handling queries and the setting should then be done in
5547 * VBoxSVC.
5548 */
5549HRESULT Machine::enumerateGuestPropertiesOnVM
5550 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5551 ComSafeArrayOut(BSTR, aValues),
5552 ComSafeArrayOut(LONG64, aTimestamps),
5553 ComSafeArrayOut(BSTR, aFlags))
5554{
5555 HRESULT rc;
5556 ComPtr<IInternalSessionControl> directControl;
5557 directControl = mData->mSession.mDirectControl;
5558
5559 if (!directControl)
5560 rc = E_ACCESSDENIED;
5561 else
5562 rc = directControl->EnumerateGuestProperties
5563 (aPatterns, ComSafeArrayOutArg(aNames),
5564 ComSafeArrayOutArg(aValues),
5565 ComSafeArrayOutArg(aTimestamps),
5566 ComSafeArrayOutArg(aFlags));
5567 return rc;
5568}
5569#endif // VBOX_WITH_GUEST_PROPS
5570
5571STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
5572 ComSafeArrayOut(BSTR, aNames),
5573 ComSafeArrayOut(BSTR, aValues),
5574 ComSafeArrayOut(LONG64, aTimestamps),
5575 ComSafeArrayOut(BSTR, aFlags))
5576{
5577#ifndef VBOX_WITH_GUEST_PROPS
5578 ReturnComNotImplemented();
5579#else // VBOX_WITH_GUEST_PROPS
5580 CheckComArgMaybeNull(aPatterns);
5581 CheckComArgOutSafeArrayPointerValid(aNames);
5582 CheckComArgOutSafeArrayPointerValid(aValues);
5583 CheckComArgOutSafeArrayPointerValid(aTimestamps);
5584 CheckComArgOutSafeArrayPointerValid(aFlags);
5585
5586 AutoCaller autoCaller(this);
5587 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5588
5589 HRESULT rc = enumerateGuestPropertiesOnVM
5590 (aPatterns, ComSafeArrayOutArg(aNames),
5591 ComSafeArrayOutArg(aValues),
5592 ComSafeArrayOutArg(aTimestamps),
5593 ComSafeArrayOutArg(aFlags));
5594 if (rc == E_ACCESSDENIED)
5595 /* The VM is not running or the service is not (yet) accessible */
5596 rc = enumerateGuestPropertiesInService
5597 (aPatterns, ComSafeArrayOutArg(aNames),
5598 ComSafeArrayOutArg(aValues),
5599 ComSafeArrayOutArg(aTimestamps),
5600 ComSafeArrayOutArg(aFlags));
5601 return rc;
5602#endif // VBOX_WITH_GUEST_PROPS
5603}
5604
5605STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
5606 ComSafeArrayOut(IMediumAttachment*, aAttachments))
5607{
5608 MediaData::AttachmentList atts;
5609
5610 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
5611 if (FAILED(rc)) return rc;
5612
5613 SafeIfaceArray<IMediumAttachment> attachments(atts);
5614 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
5615
5616 return S_OK;
5617}
5618
5619STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
5620 LONG aControllerPort,
5621 LONG aDevice,
5622 IMediumAttachment **aAttachment)
5623{
5624 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5625 aControllerName, aControllerPort, aDevice));
5626
5627 CheckComArgStrNotEmptyOrNull(aControllerName);
5628 CheckComArgOutPointerValid(aAttachment);
5629
5630 AutoCaller autoCaller(this);
5631 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5632
5633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5634
5635 *aAttachment = NULL;
5636
5637 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5638 aControllerName,
5639 aControllerPort,
5640 aDevice);
5641 if (pAttach.isNull())
5642 return setError(VBOX_E_OBJECT_NOT_FOUND,
5643 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5644 aDevice, aControllerPort, aControllerName);
5645
5646 pAttach.queryInterfaceTo(aAttachment);
5647
5648 return S_OK;
5649}
5650
5651STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
5652 StorageBus_T aConnectionType,
5653 IStorageController **controller)
5654{
5655 CheckComArgStrNotEmptyOrNull(aName);
5656
5657 if ( (aConnectionType <= StorageBus_Null)
5658 || (aConnectionType > StorageBus_SAS))
5659 return setError(E_INVALIDARG,
5660 tr("Invalid connection type: %d"),
5661 aConnectionType);
5662
5663 AutoCaller autoCaller(this);
5664 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5665
5666 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5667
5668 HRESULT rc = checkStateDependency(MutableStateDep);
5669 if (FAILED(rc)) return rc;
5670
5671 /* try to find one with the name first. */
5672 ComObjPtr<StorageController> ctrl;
5673
5674 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
5675 if (SUCCEEDED(rc))
5676 return setError(VBOX_E_OBJECT_IN_USE,
5677 tr("Storage controller named '%ls' already exists"),
5678 aName);
5679
5680 ctrl.createObject();
5681
5682 /* get a new instance number for the storage controller */
5683 ULONG ulInstance = 0;
5684 bool fBootable = true;
5685 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5686 it != mStorageControllers->end();
5687 ++it)
5688 {
5689 if ((*it)->getStorageBus() == aConnectionType)
5690 {
5691 ULONG ulCurInst = (*it)->getInstance();
5692
5693 if (ulCurInst >= ulInstance)
5694 ulInstance = ulCurInst + 1;
5695
5696 /* Only one controller of each type can be marked as bootable. */
5697 if ((*it)->getBootable())
5698 fBootable = false;
5699 }
5700 }
5701
5702 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5703 if (FAILED(rc)) return rc;
5704
5705 setModified(IsModified_Storage);
5706 mStorageControllers.backup();
5707 mStorageControllers->push_back(ctrl);
5708
5709 ctrl.queryInterfaceTo(controller);
5710
5711 /* inform the direct session if any */
5712 alock.release();
5713 onStorageControllerChange();
5714
5715 return S_OK;
5716}
5717
5718STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
5719 IStorageController **aStorageController)
5720{
5721 CheckComArgStrNotEmptyOrNull(aName);
5722
5723 AutoCaller autoCaller(this);
5724 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5725
5726 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5727
5728 ComObjPtr<StorageController> ctrl;
5729
5730 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5731 if (SUCCEEDED(rc))
5732 ctrl.queryInterfaceTo(aStorageController);
5733
5734 return rc;
5735}
5736
5737STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
5738 IStorageController **aStorageController)
5739{
5740 AutoCaller autoCaller(this);
5741 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5742
5743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5744
5745 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5746 it != mStorageControllers->end();
5747 ++it)
5748 {
5749 if ((*it)->getInstance() == aInstance)
5750 {
5751 (*it).queryInterfaceTo(aStorageController);
5752 return S_OK;
5753 }
5754 }
5755
5756 return setError(VBOX_E_OBJECT_NOT_FOUND,
5757 tr("Could not find a storage controller with instance number '%lu'"),
5758 aInstance);
5759}
5760
5761STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
5762{
5763 AutoCaller autoCaller(this);
5764 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5765
5766 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5767
5768 HRESULT rc = checkStateDependency(MutableStateDep);
5769 if (FAILED(rc)) return rc;
5770
5771 ComObjPtr<StorageController> ctrl;
5772
5773 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5774 if (SUCCEEDED(rc))
5775 {
5776 /* Ensure that only one controller of each type is marked as bootable. */
5777 if (fBootable == TRUE)
5778 {
5779 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5780 it != mStorageControllers->end();
5781 ++it)
5782 {
5783 ComObjPtr<StorageController> aCtrl = (*it);
5784
5785 if ( (aCtrl->getName() != Utf8Str(aName))
5786 && aCtrl->getBootable() == TRUE
5787 && aCtrl->getStorageBus() == ctrl->getStorageBus()
5788 && aCtrl->getControllerType() == ctrl->getControllerType())
5789 {
5790 aCtrl->setBootable(FALSE);
5791 break;
5792 }
5793 }
5794 }
5795
5796 if (SUCCEEDED(rc))
5797 {
5798 ctrl->setBootable(fBootable);
5799 setModified(IsModified_Storage);
5800 }
5801 }
5802
5803 if (SUCCEEDED(rc))
5804 {
5805 /* inform the direct session if any */
5806 alock.release();
5807 onStorageControllerChange();
5808 }
5809
5810 return rc;
5811}
5812
5813STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
5814{
5815 CheckComArgStrNotEmptyOrNull(aName);
5816
5817 AutoCaller autoCaller(this);
5818 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5819
5820 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5821
5822 HRESULT rc = checkStateDependency(MutableStateDep);
5823 if (FAILED(rc)) return rc;
5824
5825 ComObjPtr<StorageController> ctrl;
5826 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5827 if (FAILED(rc)) return rc;
5828
5829 {
5830 /* find all attached devices to the appropriate storage controller and detach them all*/
5831 MediaData::AttachmentList::const_iterator endList = mMediaData->mAttachments.end();
5832 MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
5833 for (;it != endList; it++)
5834 {
5835 MediumAttachment *pAttachTemp = *it;
5836 AutoCaller localAutoCaller(pAttachTemp);
5837 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
5838
5839 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
5840
5841 if (pAttachTemp->getControllerName() == aName)
5842 {
5843 LONG port = pAttachTemp->getPort();
5844 LONG device = pAttachTemp->getDevice();
5845 rc = DetachDevice(aName, port, device);
5846 if (FAILED(rc)) return rc;
5847 }
5848 }
5849 }
5850
5851 /* We can remove it now. */
5852 setModified(IsModified_Storage);
5853 mStorageControllers.backup();
5854
5855 ctrl->unshare();
5856
5857 mStorageControllers->remove(ctrl);
5858
5859 /* inform the direct session if any */
5860 alock.release();
5861 onStorageControllerChange();
5862
5863 return S_OK;
5864}
5865
5866STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
5867 ULONG *puOriginX,
5868 ULONG *puOriginY,
5869 ULONG *puWidth,
5870 ULONG *puHeight,
5871 BOOL *pfEnabled)
5872{
5873 LogFlowThisFunc(("\n"));
5874
5875 CheckComArgNotNull(puOriginX);
5876 CheckComArgNotNull(puOriginY);
5877 CheckComArgNotNull(puWidth);
5878 CheckComArgNotNull(puHeight);
5879 CheckComArgNotNull(pfEnabled);
5880
5881 uint32_t u32OriginX= 0;
5882 uint32_t u32OriginY= 0;
5883 uint32_t u32Width = 0;
5884 uint32_t u32Height = 0;
5885 uint16_t u16Flags = 0;
5886
5887 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
5888 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
5889 if (RT_FAILURE(vrc))
5890 {
5891#ifdef RT_OS_WINDOWS
5892 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
5893 * This works with XPCOM. But Windows COM sets all output parameters to zero.
5894 * So just assign fEnable to TRUE again.
5895 * The right fix would be to change GUI API wrappers to make sure that parameters
5896 * are changed only if API succeeds.
5897 */
5898 *pfEnabled = TRUE;
5899#endif
5900 return setError(VBOX_E_IPRT_ERROR,
5901 tr("Saved guest size is not available (%Rrc)"),
5902 vrc);
5903 }
5904
5905 *puOriginX = u32OriginX;
5906 *puOriginY = u32OriginY;
5907 *puWidth = u32Width;
5908 *puHeight = u32Height;
5909 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
5910
5911 return S_OK;
5912}
5913
5914STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
5915{
5916 LogFlowThisFunc(("\n"));
5917
5918 CheckComArgNotNull(aSize);
5919 CheckComArgNotNull(aWidth);
5920 CheckComArgNotNull(aHeight);
5921
5922 if (aScreenId != 0)
5923 return E_NOTIMPL;
5924
5925 AutoCaller autoCaller(this);
5926 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5927
5928 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5929
5930 uint8_t *pu8Data = NULL;
5931 uint32_t cbData = 0;
5932 uint32_t u32Width = 0;
5933 uint32_t u32Height = 0;
5934
5935 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
5936
5937 if (RT_FAILURE(vrc))
5938 return setError(VBOX_E_IPRT_ERROR,
5939 tr("Saved screenshot data is not available (%Rrc)"),
5940 vrc);
5941
5942 *aSize = cbData;
5943 *aWidth = u32Width;
5944 *aHeight = u32Height;
5945
5946 freeSavedDisplayScreenshot(pu8Data);
5947
5948 return S_OK;
5949}
5950
5951STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
5952{
5953 LogFlowThisFunc(("\n"));
5954
5955 CheckComArgNotNull(aWidth);
5956 CheckComArgNotNull(aHeight);
5957 CheckComArgOutSafeArrayPointerValid(aData);
5958
5959 if (aScreenId != 0)
5960 return E_NOTIMPL;
5961
5962 AutoCaller autoCaller(this);
5963 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5964
5965 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5966
5967 uint8_t *pu8Data = NULL;
5968 uint32_t cbData = 0;
5969 uint32_t u32Width = 0;
5970 uint32_t u32Height = 0;
5971
5972 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
5973
5974 if (RT_FAILURE(vrc))
5975 return setError(VBOX_E_IPRT_ERROR,
5976 tr("Saved screenshot data is not available (%Rrc)"),
5977 vrc);
5978
5979 *aWidth = u32Width;
5980 *aHeight = u32Height;
5981
5982 com::SafeArray<BYTE> bitmap(cbData);
5983 /* Convert pixels to format expected by the API caller. */
5984 if (aBGR)
5985 {
5986 /* [0] B, [1] G, [2] R, [3] A. */
5987 for (unsigned i = 0; i < cbData; i += 4)
5988 {
5989 bitmap[i] = pu8Data[i];
5990 bitmap[i + 1] = pu8Data[i + 1];
5991 bitmap[i + 2] = pu8Data[i + 2];
5992 bitmap[i + 3] = 0xff;
5993 }
5994 }
5995 else
5996 {
5997 /* [0] R, [1] G, [2] B, [3] A. */
5998 for (unsigned i = 0; i < cbData; i += 4)
5999 {
6000 bitmap[i] = pu8Data[i + 2];
6001 bitmap[i + 1] = pu8Data[i + 1];
6002 bitmap[i + 2] = pu8Data[i];
6003 bitmap[i + 3] = 0xff;
6004 }
6005 }
6006 bitmap.detachTo(ComSafeArrayOutArg(aData));
6007
6008 freeSavedDisplayScreenshot(pu8Data);
6009
6010 return S_OK;
6011}
6012
6013
6014STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(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, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6036
6037 if (RT_FAILURE(vrc))
6038 return setError(VBOX_E_IPRT_ERROR,
6039 tr("Saved screenshot data is not available (%Rrc)"),
6040 vrc);
6041
6042 *aWidth = u32Width;
6043 *aHeight = u32Height;
6044
6045 HRESULT rc = S_OK;
6046 uint8_t *pu8PNG = NULL;
6047 uint32_t cbPNG = 0;
6048 uint32_t cxPNG = 0;
6049 uint32_t cyPNG = 0;
6050
6051 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6052
6053 if (RT_SUCCESS(vrc))
6054 {
6055 com::SafeArray<BYTE> screenData(cbPNG);
6056 screenData.initFrom(pu8PNG, cbPNG);
6057 if (pu8PNG)
6058 RTMemFree(pu8PNG);
6059 screenData.detachTo(ComSafeArrayOutArg(aData));
6060 }
6061 else
6062 {
6063 if (pu8PNG)
6064 RTMemFree(pu8PNG);
6065 return setError(VBOX_E_IPRT_ERROR,
6066 tr("Could not convert screenshot to PNG (%Rrc)"),
6067 vrc);
6068 }
6069
6070 freeSavedDisplayScreenshot(pu8Data);
6071
6072 return rc;
6073}
6074
6075STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6076{
6077 LogFlowThisFunc(("\n"));
6078
6079 CheckComArgNotNull(aSize);
6080 CheckComArgNotNull(aWidth);
6081 CheckComArgNotNull(aHeight);
6082
6083 if (aScreenId != 0)
6084 return E_NOTIMPL;
6085
6086 AutoCaller autoCaller(this);
6087 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6088
6089 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6090
6091 uint8_t *pu8Data = NULL;
6092 uint32_t cbData = 0;
6093 uint32_t u32Width = 0;
6094 uint32_t u32Height = 0;
6095
6096 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6097
6098 if (RT_FAILURE(vrc))
6099 return setError(VBOX_E_IPRT_ERROR,
6100 tr("Saved screenshot data is not available (%Rrc)"),
6101 vrc);
6102
6103 *aSize = cbData;
6104 *aWidth = u32Width;
6105 *aHeight = u32Height;
6106
6107 freeSavedDisplayScreenshot(pu8Data);
6108
6109 return S_OK;
6110}
6111
6112STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6113{
6114 LogFlowThisFunc(("\n"));
6115
6116 CheckComArgNotNull(aWidth);
6117 CheckComArgNotNull(aHeight);
6118 CheckComArgOutSafeArrayPointerValid(aData);
6119
6120 if (aScreenId != 0)
6121 return E_NOTIMPL;
6122
6123 AutoCaller autoCaller(this);
6124 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6125
6126 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6127
6128 uint8_t *pu8Data = NULL;
6129 uint32_t cbData = 0;
6130 uint32_t u32Width = 0;
6131 uint32_t u32Height = 0;
6132
6133 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6134
6135 if (RT_FAILURE(vrc))
6136 return setError(VBOX_E_IPRT_ERROR,
6137 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6138 vrc);
6139
6140 *aWidth = u32Width;
6141 *aHeight = u32Height;
6142
6143 com::SafeArray<BYTE> png(cbData);
6144 png.initFrom(pu8Data, cbData);
6145 png.detachTo(ComSafeArrayOutArg(aData));
6146
6147 freeSavedDisplayScreenshot(pu8Data);
6148
6149 return S_OK;
6150}
6151
6152STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6153{
6154 HRESULT rc = S_OK;
6155 LogFlowThisFunc(("\n"));
6156
6157 AutoCaller autoCaller(this);
6158 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6159
6160 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6161
6162 if (!mHWData->mCPUHotPlugEnabled)
6163 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6164
6165 if (aCpu >= mHWData->mCPUCount)
6166 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6167
6168 if (mHWData->mCPUAttached[aCpu])
6169 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6170
6171 alock.release();
6172 rc = onCPUChange(aCpu, false);
6173 alock.acquire();
6174 if (FAILED(rc)) return rc;
6175
6176 setModified(IsModified_MachineData);
6177 mHWData.backup();
6178 mHWData->mCPUAttached[aCpu] = true;
6179
6180 /* Save settings if online */
6181 if (Global::IsOnline(mData->mMachineState))
6182 saveSettings(NULL);
6183
6184 return S_OK;
6185}
6186
6187STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6188{
6189 HRESULT rc = S_OK;
6190 LogFlowThisFunc(("\n"));
6191
6192 AutoCaller autoCaller(this);
6193 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6194
6195 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6196
6197 if (!mHWData->mCPUHotPlugEnabled)
6198 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6199
6200 if (aCpu >= SchemaDefs::MaxCPUCount)
6201 return setError(E_INVALIDARG,
6202 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6203 SchemaDefs::MaxCPUCount);
6204
6205 if (!mHWData->mCPUAttached[aCpu])
6206 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6207
6208 /* CPU 0 can't be detached */
6209 if (aCpu == 0)
6210 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6211
6212 alock.release();
6213 rc = onCPUChange(aCpu, true);
6214 alock.acquire();
6215 if (FAILED(rc)) return rc;
6216
6217 setModified(IsModified_MachineData);
6218 mHWData.backup();
6219 mHWData->mCPUAttached[aCpu] = false;
6220
6221 /* Save settings if online */
6222 if (Global::IsOnline(mData->mMachineState))
6223 saveSettings(NULL);
6224
6225 return S_OK;
6226}
6227
6228STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6229{
6230 LogFlowThisFunc(("\n"));
6231
6232 CheckComArgNotNull(aCpuAttached);
6233
6234 *aCpuAttached = false;
6235
6236 AutoCaller autoCaller(this);
6237 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6238
6239 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6240
6241 /* If hotplug is enabled the CPU is always enabled. */
6242 if (!mHWData->mCPUHotPlugEnabled)
6243 {
6244 if (aCpu < mHWData->mCPUCount)
6245 *aCpuAttached = true;
6246 }
6247 else
6248 {
6249 if (aCpu < SchemaDefs::MaxCPUCount)
6250 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6251 }
6252
6253 return S_OK;
6254}
6255
6256STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6257{
6258 CheckComArgOutPointerValid(aName);
6259
6260 AutoCaller autoCaller(this);
6261 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6262
6263 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6264
6265 Utf8Str log = queryLogFilename(aIdx);
6266 if (!RTFileExists(log.c_str()))
6267 log.setNull();
6268 log.cloneTo(aName);
6269
6270 return S_OK;
6271}
6272
6273STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6274{
6275 LogFlowThisFunc(("\n"));
6276 CheckComArgOutSafeArrayPointerValid(aData);
6277 if (aSize < 0)
6278 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6279
6280 AutoCaller autoCaller(this);
6281 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6282
6283 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6284
6285 HRESULT rc = S_OK;
6286 Utf8Str log = queryLogFilename(aIdx);
6287
6288 /* do not unnecessarily hold the lock while doing something which does
6289 * not need the lock and potentially takes a long time. */
6290 alock.release();
6291
6292 /* Limit the chunk size to 32K for now, as that gives better performance
6293 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6294 * One byte expands to approx. 25 bytes of breathtaking XML. */
6295 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6296 com::SafeArray<BYTE> logData(cbData);
6297
6298 RTFILE LogFile;
6299 int vrc = RTFileOpen(&LogFile, log.c_str(),
6300 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6301 if (RT_SUCCESS(vrc))
6302 {
6303 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6304 if (RT_SUCCESS(vrc))
6305 logData.resize(cbData);
6306 else
6307 rc = setError(VBOX_E_IPRT_ERROR,
6308 tr("Could not read log file '%s' (%Rrc)"),
6309 log.c_str(), vrc);
6310 RTFileClose(LogFile);
6311 }
6312 else
6313 rc = setError(VBOX_E_IPRT_ERROR,
6314 tr("Could not open log file '%s' (%Rrc)"),
6315 log.c_str(), vrc);
6316
6317 if (FAILED(rc))
6318 logData.resize(0);
6319 logData.detachTo(ComSafeArrayOutArg(aData));
6320
6321 return rc;
6322}
6323
6324
6325/**
6326 * Currently this method doesn't attach device to the running VM,
6327 * just makes sure it's plugged on next VM start.
6328 */
6329STDMETHODIMP Machine::AttachHostPciDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6330{
6331 AutoCaller autoCaller(this);
6332 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6333
6334 // lock scope
6335 {
6336 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6337
6338 HRESULT rc = checkStateDependency(MutableStateDep);
6339 if (FAILED(rc)) return rc;
6340
6341 ChipsetType_T aChipset = ChipsetType_PIIX3;
6342 COMGETTER(ChipsetType)(&aChipset);
6343
6344 if (aChipset != ChipsetType_ICH9)
6345 {
6346 return setError(E_INVALIDARG,
6347 tr("Host PCI attachment only supported with ICH9 chipset"));
6348 }
6349
6350 // check if device with this host PCI address already attached
6351 for (HWData::PciDeviceAssignmentList::iterator it = mHWData->mPciDeviceAssignments.begin();
6352 it != mHWData->mPciDeviceAssignments.end();
6353 ++it)
6354 {
6355 LONG iHostAddress = -1;
6356 ComPtr<PciDeviceAttachment> pAttach;
6357 pAttach = *it;
6358 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6359 if (iHostAddress == hostAddress)
6360 return setError(E_INVALIDARG,
6361 tr("Device with host PCI address already attached to this VM"));
6362 }
6363
6364 ComObjPtr<PciDeviceAttachment> pda;
6365 char name[32];
6366
6367 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6368 Bstr bname(name);
6369 pda.createObject();
6370 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
6371 setModified(IsModified_MachineData);
6372 mHWData.backup();
6373 mHWData->mPciDeviceAssignments.push_back(pda);
6374 }
6375
6376 return S_OK;
6377}
6378
6379/**
6380 * Currently this method doesn't detach device from the running VM,
6381 * just makes sure it's not plugged on next VM start.
6382 */
6383STDMETHODIMP Machine::DetachHostPciDevice(LONG hostAddress)
6384{
6385 AutoCaller autoCaller(this);
6386 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6387
6388 ComObjPtr<PciDeviceAttachment> pAttach;
6389 bool fRemoved = false;
6390 HRESULT rc;
6391
6392 // lock scope
6393 {
6394 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6395
6396 rc = checkStateDependency(MutableStateDep);
6397 if (FAILED(rc)) return rc;
6398
6399 for (HWData::PciDeviceAssignmentList::iterator it = mHWData->mPciDeviceAssignments.begin();
6400 it != mHWData->mPciDeviceAssignments.end();
6401 ++it)
6402 {
6403 LONG iHostAddress = -1;
6404 pAttach = *it;
6405 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6406 if (iHostAddress != -1 && iHostAddress == hostAddress)
6407 {
6408 setModified(IsModified_MachineData);
6409 mHWData.backup();
6410 mHWData->mPciDeviceAssignments.remove(pAttach);
6411 fRemoved = true;
6412 break;
6413 }
6414 }
6415 }
6416
6417
6418 /* Fire event outside of the lock */
6419 if (fRemoved)
6420 {
6421 Assert(!pAttach.isNull());
6422 ComPtr<IEventSource> es;
6423 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6424 Assert(SUCCEEDED(rc));
6425 Bstr mid;
6426 rc = this->COMGETTER(Id)(mid.asOutParam());
6427 Assert(SUCCEEDED(rc));
6428 fireHostPciDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6429 }
6430
6431 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6432 tr("No host PCI device %08x attached"),
6433 hostAddress
6434 );
6435}
6436
6437STDMETHODIMP Machine::COMGETTER(PciDeviceAssignments)(ComSafeArrayOut(IPciDeviceAttachment *, aAssignments))
6438{
6439 CheckComArgOutSafeArrayPointerValid(aAssignments);
6440
6441 AutoCaller autoCaller(this);
6442 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6443
6444 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6445
6446 SafeIfaceArray<IPciDeviceAttachment> assignments(mHWData->mPciDeviceAssignments);
6447 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
6448
6449 return S_OK;
6450}
6451
6452STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
6453{
6454 CheckComArgOutPointerValid(aBandwidthControl);
6455
6456 AutoCaller autoCaller(this);
6457 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6458
6459 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
6460
6461 return S_OK;
6462}
6463
6464STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
6465{
6466 CheckComArgOutPointerValid(pfEnabled);
6467 AutoCaller autoCaller(this);
6468 HRESULT hrc = autoCaller.rc();
6469 if (SUCCEEDED(hrc))
6470 {
6471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6472 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
6473 }
6474 return hrc;
6475}
6476
6477STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
6478{
6479 AutoCaller autoCaller(this);
6480 HRESULT hrc = autoCaller.rc();
6481 if (SUCCEEDED(hrc))
6482 {
6483 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6484 hrc = checkStateDependency(MutableStateDep);
6485 if (SUCCEEDED(hrc))
6486 {
6487 hrc = mHWData.backupEx();
6488 if (SUCCEEDED(hrc))
6489 {
6490 setModified(IsModified_MachineData);
6491 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
6492 }
6493 }
6494 }
6495 return hrc;
6496}
6497
6498STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
6499{
6500 CheckComArgOutPointerValid(pbstrConfig);
6501 AutoCaller autoCaller(this);
6502 HRESULT hrc = autoCaller.rc();
6503 if (SUCCEEDED(hrc))
6504 {
6505 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6506 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
6507 }
6508 return hrc;
6509}
6510
6511STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
6512{
6513 CheckComArgStr(bstrConfig);
6514 AutoCaller autoCaller(this);
6515 HRESULT hrc = autoCaller.rc();
6516 if (SUCCEEDED(hrc))
6517 {
6518 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6519 hrc = checkStateDependency(MutableStateDep);
6520 if (SUCCEEDED(hrc))
6521 {
6522 hrc = mHWData.backupEx();
6523 if (SUCCEEDED(hrc))
6524 {
6525 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
6526 if (SUCCEEDED(hrc))
6527 setModified(IsModified_MachineData);
6528 }
6529 }
6530 }
6531 return hrc;
6532
6533}
6534
6535STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
6536{
6537 CheckComArgOutPointerValid(pfAllow);
6538 AutoCaller autoCaller(this);
6539 HRESULT hrc = autoCaller.rc();
6540 if (SUCCEEDED(hrc))
6541 {
6542 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6543 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
6544 }
6545 return hrc;
6546}
6547
6548STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
6549{
6550 AutoCaller autoCaller(this);
6551 HRESULT hrc = autoCaller.rc();
6552 if (SUCCEEDED(hrc))
6553 {
6554 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6555 hrc = checkStateDependency(MutableStateDep);
6556 if (SUCCEEDED(hrc))
6557 {
6558 hrc = mHWData.backupEx();
6559 if (SUCCEEDED(hrc))
6560 {
6561 setModified(IsModified_MachineData);
6562 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
6563 }
6564 }
6565 }
6566 return hrc;
6567}
6568
6569STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
6570{
6571 CheckComArgOutPointerValid(pfEnabled);
6572 AutoCaller autoCaller(this);
6573 HRESULT hrc = autoCaller.rc();
6574 if (SUCCEEDED(hrc))
6575 {
6576 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6577 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
6578 }
6579 return hrc;
6580}
6581
6582STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
6583{
6584 AutoCaller autoCaller(this);
6585 HRESULT hrc = autoCaller.rc();
6586 if (SUCCEEDED(hrc))
6587 {
6588 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6589 hrc = checkStateDependency(MutableStateDep);
6590 if ( SUCCEEDED(hrc)
6591 && mHWData->mAutostart.fAutostartEnabled != fEnabled)
6592 {
6593 AutostartDb *autostartDb = mParent->getAutostartDb();
6594 int vrc;
6595
6596 if (fEnabled)
6597 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6598 else
6599 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6600
6601 if (RT_SUCCESS(vrc))
6602 {
6603 hrc = mHWData.backupEx();
6604 if (SUCCEEDED(hrc))
6605 {
6606 setModified(IsModified_MachineData);
6607 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
6608 }
6609 }
6610 else if (vrc == VERR_NOT_SUPPORTED)
6611 hrc = setError(VBOX_E_NOT_SUPPORTED,
6612 tr("The VM autostart feature is not supported on this platform"));
6613 else
6614 hrc = setError(E_UNEXPECTED,
6615 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6616 fEnabled ? "Adding" : "Removing",
6617 mUserData->s.strName.c_str(), vrc);
6618 }
6619 }
6620 return hrc;
6621}
6622
6623STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
6624{
6625 CheckComArgOutPointerValid(puDelay);
6626 AutoCaller autoCaller(this);
6627 HRESULT hrc = autoCaller.rc();
6628 if (SUCCEEDED(hrc))
6629 {
6630 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6631 *puDelay = mHWData->mAutostart.uAutostartDelay;
6632 }
6633 return hrc;
6634}
6635
6636STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
6637{
6638 AutoCaller autoCaller(this);
6639 HRESULT hrc = autoCaller.rc();
6640 if (SUCCEEDED(hrc))
6641 {
6642 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6643 hrc = checkStateDependency(MutableStateDep);
6644 if (SUCCEEDED(hrc))
6645 {
6646 hrc = mHWData.backupEx();
6647 if (SUCCEEDED(hrc))
6648 {
6649 setModified(IsModified_MachineData);
6650 mHWData->mAutostart.uAutostartDelay = uDelay;
6651 }
6652 }
6653 }
6654 return hrc;
6655}
6656
6657STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
6658{
6659 CheckComArgOutPointerValid(penmAutostopType);
6660 AutoCaller autoCaller(this);
6661 HRESULT hrc = autoCaller.rc();
6662 if (SUCCEEDED(hrc))
6663 {
6664 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6665 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
6666 }
6667 return hrc;
6668}
6669
6670STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
6671{
6672 AutoCaller autoCaller(this);
6673 HRESULT hrc = autoCaller.rc();
6674 if (SUCCEEDED(hrc))
6675 {
6676 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6677 hrc = checkStateDependency(MutableStateDep);
6678 if ( SUCCEEDED(hrc)
6679 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
6680 {
6681 AutostartDb *autostartDb = mParent->getAutostartDb();
6682 int vrc;
6683
6684 if (enmAutostopType != AutostopType_Disabled)
6685 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6686 else
6687 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6688
6689 if (RT_SUCCESS(vrc))
6690 {
6691 hrc = mHWData.backupEx();
6692 if (SUCCEEDED(hrc))
6693 {
6694 setModified(IsModified_MachineData);
6695 mHWData->mAutostart.enmAutostopType = enmAutostopType;
6696 }
6697 }
6698 else if (vrc == VERR_NOT_SUPPORTED)
6699 hrc = setError(VBOX_E_NOT_SUPPORTED,
6700 tr("The VM autostop feature is not supported on this platform"));
6701 else
6702 hrc = setError(E_UNEXPECTED,
6703 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6704 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6705 mUserData->s.strName.c_str(), vrc);
6706 }
6707 }
6708 return hrc;
6709}
6710
6711
6712STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
6713{
6714 LogFlowFuncEnter();
6715
6716 CheckComArgNotNull(pTarget);
6717 CheckComArgOutPointerValid(pProgress);
6718
6719 /* Convert the options. */
6720 RTCList<CloneOptions_T> optList;
6721 if (options != NULL)
6722 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
6723
6724 if (optList.contains(CloneOptions_Link))
6725 {
6726 if (!isSnapshotMachine())
6727 return setError(E_INVALIDARG,
6728 tr("Linked clone can only be created from a snapshot"));
6729 if (mode != CloneMode_MachineState)
6730 return setError(E_INVALIDARG,
6731 tr("Linked clone can only be created for a single machine state"));
6732 }
6733 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6734
6735 AutoCaller autoCaller(this);
6736 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6737
6738
6739 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
6740
6741 HRESULT rc = pWorker->start(pProgress);
6742
6743 LogFlowFuncLeave();
6744
6745 return rc;
6746}
6747
6748// public methods for internal purposes
6749/////////////////////////////////////////////////////////////////////////////
6750
6751/**
6752 * Adds the given IsModified_* flag to the dirty flags of the machine.
6753 * This must be called either during loadSettings or under the machine write lock.
6754 * @param fl
6755 */
6756void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
6757{
6758 mData->flModifications |= fl;
6759 if (fAllowStateModification && isStateModificationAllowed())
6760 mData->mCurrentStateModified = true;
6761}
6762
6763/**
6764 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
6765 * care of the write locking.
6766 *
6767 * @param fModifications The flag to add.
6768 */
6769void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
6770{
6771 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6772 setModified(fModification, fAllowStateModification);
6773}
6774
6775/**
6776 * Saves the registry entry of this machine to the given configuration node.
6777 *
6778 * @param aEntryNode Node to save the registry entry to.
6779 *
6780 * @note locks this object for reading.
6781 */
6782HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
6783{
6784 AutoLimitedCaller autoCaller(this);
6785 AssertComRCReturnRC(autoCaller.rc());
6786
6787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6788
6789 data.uuid = mData->mUuid;
6790 data.strSettingsFile = mData->m_strConfigFile;
6791
6792 return S_OK;
6793}
6794
6795/**
6796 * Calculates the absolute path of the given path taking the directory of the
6797 * machine settings file as the current directory.
6798 *
6799 * @param aPath Path to calculate the absolute path for.
6800 * @param aResult Where to put the result (used only on success, can be the
6801 * same Utf8Str instance as passed in @a aPath).
6802 * @return IPRT result.
6803 *
6804 * @note Locks this object for reading.
6805 */
6806int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
6807{
6808 AutoCaller autoCaller(this);
6809 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
6810
6811 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6812
6813 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
6814
6815 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
6816
6817 strSettingsDir.stripFilename();
6818 char folder[RTPATH_MAX];
6819 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
6820 if (RT_SUCCESS(vrc))
6821 aResult = folder;
6822
6823 return vrc;
6824}
6825
6826/**
6827 * Copies strSource to strTarget, making it relative to the machine folder
6828 * if it is a subdirectory thereof, or simply copying it otherwise.
6829 *
6830 * @param strSource Path to evaluate and copy.
6831 * @param strTarget Buffer to receive target path.
6832 *
6833 * @note Locks this object for reading.
6834 */
6835void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
6836 Utf8Str &strTarget)
6837{
6838 AutoCaller autoCaller(this);
6839 AssertComRCReturn(autoCaller.rc(), (void)0);
6840
6841 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6842
6843 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
6844 // use strTarget as a temporary buffer to hold the machine settings dir
6845 strTarget = mData->m_strConfigFileFull;
6846 strTarget.stripFilename();
6847 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
6848 {
6849 // is relative: then append what's left
6850 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
6851 // for empty paths (only possible for subdirs) use "." to avoid
6852 // triggering default settings for not present config attributes.
6853 if (strTarget.isEmpty())
6854 strTarget = ".";
6855 }
6856 else
6857 // is not relative: then overwrite
6858 strTarget = strSource;
6859}
6860
6861/**
6862 * Returns the full path to the machine's log folder in the
6863 * \a aLogFolder argument.
6864 */
6865void Machine::getLogFolder(Utf8Str &aLogFolder)
6866{
6867 AutoCaller autoCaller(this);
6868 AssertComRCReturnVoid(autoCaller.rc());
6869
6870 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6871
6872 char szTmp[RTPATH_MAX];
6873 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
6874 if (RT_SUCCESS(vrc))
6875 {
6876 if (szTmp[0] && !mUserData.isNull())
6877 {
6878 char szTmp2[RTPATH_MAX];
6879 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
6880 if (RT_SUCCESS(vrc))
6881 aLogFolder = BstrFmt("%s%c%s",
6882 szTmp2,
6883 RTPATH_DELIMITER,
6884 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
6885 }
6886 else
6887 vrc = VERR_PATH_IS_RELATIVE;
6888 }
6889
6890 if (RT_FAILURE(vrc))
6891 {
6892 // fallback if VBOX_USER_LOGHOME is not set or invalid
6893 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
6894 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
6895 aLogFolder.append(RTPATH_DELIMITER);
6896 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
6897 }
6898}
6899
6900/**
6901 * Returns the full path to the machine's log file for an given index.
6902 */
6903Utf8Str Machine::queryLogFilename(ULONG idx)
6904{
6905 Utf8Str logFolder;
6906 getLogFolder(logFolder);
6907 Assert(logFolder.length());
6908 Utf8Str log;
6909 if (idx == 0)
6910 log = Utf8StrFmt("%s%cVBox.log",
6911 logFolder.c_str(), RTPATH_DELIMITER);
6912 else
6913 log = Utf8StrFmt("%s%cVBox.log.%d",
6914 logFolder.c_str(), RTPATH_DELIMITER, idx);
6915 return log;
6916}
6917
6918/**
6919 * Composes a unique saved state filename based on the current system time. The filename is
6920 * granular to the second so this will work so long as no more than one snapshot is taken on
6921 * a machine per second.
6922 *
6923 * Before version 4.1, we used this formula for saved state files:
6924 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
6925 * which no longer works because saved state files can now be shared between the saved state of the
6926 * "saved" machine and an online snapshot, and the following would cause problems:
6927 * 1) save machine
6928 * 2) create online snapshot from that machine state --> reusing saved state file
6929 * 3) save machine again --> filename would be reused, breaking the online snapshot
6930 *
6931 * So instead we now use a timestamp.
6932 *
6933 * @param str
6934 */
6935void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
6936{
6937 AutoCaller autoCaller(this);
6938 AssertComRCReturnVoid(autoCaller.rc());
6939
6940 {
6941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6942 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
6943 }
6944
6945 RTTIMESPEC ts;
6946 RTTimeNow(&ts);
6947 RTTIME time;
6948 RTTimeExplode(&time, &ts);
6949
6950 strStateFilePath += RTPATH_DELIMITER;
6951 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
6952 time.i32Year, time.u8Month, time.u8MonthDay,
6953 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
6954}
6955
6956/**
6957 * @note Locks this object for writing, calls the client process
6958 * (inside the lock).
6959 */
6960HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
6961 const Utf8Str &strType,
6962 const Utf8Str &strEnvironment,
6963 ProgressProxy *aProgress)
6964{
6965 LogFlowThisFuncEnter();
6966
6967 AssertReturn(aControl, E_FAIL);
6968 AssertReturn(aProgress, E_FAIL);
6969
6970 AutoCaller autoCaller(this);
6971 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6972
6973 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6974
6975 if (!mData->mRegistered)
6976 return setError(E_UNEXPECTED,
6977 tr("The machine '%s' is not registered"),
6978 mUserData->s.strName.c_str());
6979
6980 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
6981
6982 if ( mData->mSession.mState == SessionState_Locked
6983 || mData->mSession.mState == SessionState_Spawning
6984 || mData->mSession.mState == SessionState_Unlocking)
6985 return setError(VBOX_E_INVALID_OBJECT_STATE,
6986 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
6987 mUserData->s.strName.c_str());
6988
6989 /* may not be busy */
6990 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
6991
6992 /* get the path to the executable */
6993 char szPath[RTPATH_MAX];
6994 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
6995 size_t sz = strlen(szPath);
6996 szPath[sz++] = RTPATH_DELIMITER;
6997 szPath[sz] = 0;
6998 char *cmd = szPath + sz;
6999 sz = RTPATH_MAX - sz;
7000
7001 int vrc = VINF_SUCCESS;
7002 RTPROCESS pid = NIL_RTPROCESS;
7003
7004 RTENV env = RTENV_DEFAULT;
7005
7006 if (!strEnvironment.isEmpty())
7007 {
7008 char *newEnvStr = NULL;
7009
7010 do
7011 {
7012 /* clone the current environment */
7013 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7014 AssertRCBreakStmt(vrc2, vrc = vrc2);
7015
7016 newEnvStr = RTStrDup(strEnvironment.c_str());
7017 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7018
7019 /* put new variables to the environment
7020 * (ignore empty variable names here since RTEnv API
7021 * intentionally doesn't do that) */
7022 char *var = newEnvStr;
7023 for (char *p = newEnvStr; *p; ++p)
7024 {
7025 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7026 {
7027 *p = '\0';
7028 if (*var)
7029 {
7030 char *val = strchr(var, '=');
7031 if (val)
7032 {
7033 *val++ = '\0';
7034 vrc2 = RTEnvSetEx(env, var, val);
7035 }
7036 else
7037 vrc2 = RTEnvUnsetEx(env, var);
7038 if (RT_FAILURE(vrc2))
7039 break;
7040 }
7041 var = p + 1;
7042 }
7043 }
7044 if (RT_SUCCESS(vrc2) && *var)
7045 vrc2 = RTEnvPutEx(env, var);
7046
7047 AssertRCBreakStmt(vrc2, vrc = vrc2);
7048 }
7049 while (0);
7050
7051 if (newEnvStr != NULL)
7052 RTStrFree(newEnvStr);
7053 }
7054
7055 /* Qt is default */
7056#ifdef VBOX_WITH_QTGUI
7057 if (strType == "gui" || strType == "GUI/Qt")
7058 {
7059# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7060 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7061# else
7062 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7063# endif
7064 Assert(sz >= sizeof(VirtualBox_exe));
7065 strcpy(cmd, VirtualBox_exe);
7066
7067 Utf8Str idStr = mData->mUuid.toString();
7068 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7069 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7070 }
7071#else /* !VBOX_WITH_QTGUI */
7072 if (0)
7073 ;
7074#endif /* VBOX_WITH_QTGUI */
7075
7076 else
7077
7078#ifdef VBOX_WITH_VBOXSDL
7079 if (strType == "sdl" || strType == "GUI/SDL")
7080 {
7081 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7082 Assert(sz >= sizeof(VBoxSDL_exe));
7083 strcpy(cmd, VBoxSDL_exe);
7084
7085 Utf8Str idStr = mData->mUuid.toString();
7086 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7087 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7088 }
7089#else /* !VBOX_WITH_VBOXSDL */
7090 if (0)
7091 ;
7092#endif /* !VBOX_WITH_VBOXSDL */
7093
7094 else
7095
7096#ifdef VBOX_WITH_HEADLESS
7097 if ( strType == "headless"
7098 || strType == "capture"
7099 || strType == "vrdp" /* Deprecated. Same as headless. */
7100 )
7101 {
7102 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7103 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7104 * and a VM works even if the server has not been installed.
7105 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7106 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7107 * differently in 4.0 and 3.x.
7108 */
7109 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7110 Assert(sz >= sizeof(VBoxHeadless_exe));
7111 strcpy(cmd, VBoxHeadless_exe);
7112
7113 Utf8Str idStr = mData->mUuid.toString();
7114 /* Leave space for "--capture" arg. */
7115 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7116 "--startvm", idStr.c_str(),
7117 "--vrde", "config",
7118 0, /* For "--capture". */
7119 0 };
7120 if (strType == "capture")
7121 {
7122 unsigned pos = RT_ELEMENTS(args) - 2;
7123 args[pos] = "--capture";
7124 }
7125 vrc = RTProcCreate(szPath, args, env,
7126#ifdef RT_OS_WINDOWS
7127 RTPROC_FLAGS_NO_WINDOW
7128#else
7129 0
7130#endif
7131 , &pid);
7132 }
7133#else /* !VBOX_WITH_HEADLESS */
7134 if (0)
7135 ;
7136#endif /* !VBOX_WITH_HEADLESS */
7137 else
7138 {
7139 RTEnvDestroy(env);
7140 return setError(E_INVALIDARG,
7141 tr("Invalid session type: '%s'"),
7142 strType.c_str());
7143 }
7144
7145 RTEnvDestroy(env);
7146
7147 if (RT_FAILURE(vrc))
7148 return setError(VBOX_E_IPRT_ERROR,
7149 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7150 mUserData->s.strName.c_str(), vrc);
7151
7152 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7153
7154 /*
7155 * Note that we don't release the lock here before calling the client,
7156 * because it doesn't need to call us back if called with a NULL argument.
7157 * Releasing the lock here is dangerous because we didn't prepare the
7158 * launch data yet, but the client we've just started may happen to be
7159 * too fast and call openSession() that will fail (because of PID, etc.),
7160 * so that the Machine will never get out of the Spawning session state.
7161 */
7162
7163 /* inform the session that it will be a remote one */
7164 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7165 HRESULT rc = aControl->AssignMachine(NULL);
7166 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7167
7168 if (FAILED(rc))
7169 {
7170 /* restore the session state */
7171 mData->mSession.mState = SessionState_Unlocked;
7172 /* The failure may occur w/o any error info (from RPC), so provide one */
7173 return setError(VBOX_E_VM_ERROR,
7174 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7175 }
7176
7177 /* attach launch data to the machine */
7178 Assert(mData->mSession.mPid == NIL_RTPROCESS);
7179 mData->mSession.mRemoteControls.push_back(aControl);
7180 mData->mSession.mProgress = aProgress;
7181 mData->mSession.mPid = pid;
7182 mData->mSession.mState = SessionState_Spawning;
7183 mData->mSession.mType = strType;
7184
7185 LogFlowThisFuncLeave();
7186 return S_OK;
7187}
7188
7189/**
7190 * Returns @c true if the given machine has an open direct session and returns
7191 * the session machine instance and additional session data (on some platforms)
7192 * if so.
7193 *
7194 * Note that when the method returns @c false, the arguments remain unchanged.
7195 *
7196 * @param aMachine Session machine object.
7197 * @param aControl Direct session control object (optional).
7198 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7199 *
7200 * @note locks this object for reading.
7201 */
7202#if defined(RT_OS_WINDOWS)
7203bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7204 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7205 HANDLE *aIPCSem /*= NULL*/,
7206 bool aAllowClosing /*= false*/)
7207#elif defined(RT_OS_OS2)
7208bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7209 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7210 HMTX *aIPCSem /*= NULL*/,
7211 bool aAllowClosing /*= false*/)
7212#else
7213bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7214 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7215 bool aAllowClosing /*= false*/)
7216#endif
7217{
7218 AutoLimitedCaller autoCaller(this);
7219 AssertComRCReturn(autoCaller.rc(), false);
7220
7221 /* just return false for inaccessible machines */
7222 if (autoCaller.state() != Ready)
7223 return false;
7224
7225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7226
7227 if ( mData->mSession.mState == SessionState_Locked
7228 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7229 )
7230 {
7231 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7232
7233 aMachine = mData->mSession.mMachine;
7234
7235 if (aControl != NULL)
7236 *aControl = mData->mSession.mDirectControl;
7237
7238#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7239 /* Additional session data */
7240 if (aIPCSem != NULL)
7241 *aIPCSem = aMachine->mIPCSem;
7242#endif
7243 return true;
7244 }
7245
7246 return false;
7247}
7248
7249/**
7250 * Returns @c true if the given machine has an spawning direct session and
7251 * returns and additional session data (on some platforms) if so.
7252 *
7253 * Note that when the method returns @c false, the arguments remain unchanged.
7254 *
7255 * @param aPID PID of the spawned direct session process.
7256 *
7257 * @note locks this object for reading.
7258 */
7259#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7260bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7261#else
7262bool Machine::isSessionSpawning()
7263#endif
7264{
7265 AutoLimitedCaller autoCaller(this);
7266 AssertComRCReturn(autoCaller.rc(), false);
7267
7268 /* just return false for inaccessible machines */
7269 if (autoCaller.state() != Ready)
7270 return false;
7271
7272 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7273
7274 if (mData->mSession.mState == SessionState_Spawning)
7275 {
7276#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7277 /* Additional session data */
7278 if (aPID != NULL)
7279 {
7280 AssertReturn(mData->mSession.mPid != NIL_RTPROCESS, false);
7281 *aPID = mData->mSession.mPid;
7282 }
7283#endif
7284 return true;
7285 }
7286
7287 return false;
7288}
7289
7290/**
7291 * Called from the client watcher thread to check for unexpected client process
7292 * death during Session_Spawning state (e.g. before it successfully opened a
7293 * direct session).
7294 *
7295 * On Win32 and on OS/2, this method is called only when we've got the
7296 * direct client's process termination notification, so it always returns @c
7297 * true.
7298 *
7299 * On other platforms, this method returns @c true if the client process is
7300 * terminated and @c false if it's still alive.
7301 *
7302 * @note Locks this object for writing.
7303 */
7304bool Machine::checkForSpawnFailure()
7305{
7306 AutoCaller autoCaller(this);
7307 if (!autoCaller.isOk())
7308 {
7309 /* nothing to do */
7310 LogFlowThisFunc(("Already uninitialized!\n"));
7311 return true;
7312 }
7313
7314 /* VirtualBox::addProcessToReap() needs a write lock */
7315 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
7316
7317 if (mData->mSession.mState != SessionState_Spawning)
7318 {
7319 /* nothing to do */
7320 LogFlowThisFunc(("Not spawning any more!\n"));
7321 return true;
7322 }
7323
7324 HRESULT rc = S_OK;
7325
7326#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7327
7328 /* the process was already unexpectedly terminated, we just need to set an
7329 * error and finalize session spawning */
7330 rc = setError(E_FAIL,
7331 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
7332 getName().c_str());
7333#else
7334
7335 /* PID not yet initialized, skip check. */
7336 if (mData->mSession.mPid == NIL_RTPROCESS)
7337 return false;
7338
7339 RTPROCSTATUS status;
7340 int vrc = ::RTProcWait(mData->mSession.mPid, RTPROCWAIT_FLAGS_NOBLOCK,
7341 &status);
7342
7343 if (vrc != VERR_PROCESS_RUNNING)
7344 {
7345 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7346 rc = setError(E_FAIL,
7347 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7348 getName().c_str(), status.iStatus);
7349 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7350 rc = setError(E_FAIL,
7351 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7352 getName().c_str(), status.iStatus);
7353 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7354 rc = setError(E_FAIL,
7355 tr("The virtual machine '%s' has terminated abnormally"),
7356 getName().c_str(), status.iStatus);
7357 else
7358 rc = setError(E_FAIL,
7359 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7360 getName().c_str(), rc);
7361 }
7362
7363#endif
7364
7365 if (FAILED(rc))
7366 {
7367 /* Close the remote session, remove the remote control from the list
7368 * and reset session state to Closed (@note keep the code in sync with
7369 * the relevant part in checkForSpawnFailure()). */
7370
7371 Assert(mData->mSession.mRemoteControls.size() == 1);
7372 if (mData->mSession.mRemoteControls.size() == 1)
7373 {
7374 ErrorInfoKeeper eik;
7375 mData->mSession.mRemoteControls.front()->Uninitialize();
7376 }
7377
7378 mData->mSession.mRemoteControls.clear();
7379 mData->mSession.mState = SessionState_Unlocked;
7380
7381 /* finalize the progress after setting the state */
7382 if (!mData->mSession.mProgress.isNull())
7383 {
7384 mData->mSession.mProgress->notifyComplete(rc);
7385 mData->mSession.mProgress.setNull();
7386 }
7387
7388 mParent->addProcessToReap(mData->mSession.mPid);
7389 mData->mSession.mPid = NIL_RTPROCESS;
7390
7391 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7392 return true;
7393 }
7394
7395 return false;
7396}
7397
7398/**
7399 * Checks whether the machine can be registered. If so, commits and saves
7400 * all settings.
7401 *
7402 * @note Must be called from mParent's write lock. Locks this object and
7403 * children for writing.
7404 */
7405HRESULT Machine::prepareRegister()
7406{
7407 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7408
7409 AutoLimitedCaller autoCaller(this);
7410 AssertComRCReturnRC(autoCaller.rc());
7411
7412 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7413
7414 /* wait for state dependents to drop to zero */
7415 ensureNoStateDependencies();
7416
7417 if (!mData->mAccessible)
7418 return setError(VBOX_E_INVALID_OBJECT_STATE,
7419 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7420 mUserData->s.strName.c_str(),
7421 mData->mUuid.toString().c_str());
7422
7423 AssertReturn(autoCaller.state() == Ready, E_FAIL);
7424
7425 if (mData->mRegistered)
7426 return setError(VBOX_E_INVALID_OBJECT_STATE,
7427 tr("The machine '%s' with UUID {%s} is already registered"),
7428 mUserData->s.strName.c_str(),
7429 mData->mUuid.toString().c_str());
7430
7431 HRESULT rc = S_OK;
7432
7433 // Ensure the settings are saved. If we are going to be registered and
7434 // no config file exists yet, create it by calling saveSettings() too.
7435 if ( (mData->flModifications)
7436 || (!mData->pMachineConfigFile->fileExists())
7437 )
7438 {
7439 rc = saveSettings(NULL);
7440 // no need to check whether VirtualBox.xml needs saving too since
7441 // we can't have a machine XML file rename pending
7442 if (FAILED(rc)) return rc;
7443 }
7444
7445 /* more config checking goes here */
7446
7447 if (SUCCEEDED(rc))
7448 {
7449 /* we may have had implicit modifications we want to fix on success */
7450 commit();
7451
7452 mData->mRegistered = true;
7453 }
7454 else
7455 {
7456 /* we may have had implicit modifications we want to cancel on failure*/
7457 rollback(false /* aNotify */);
7458 }
7459
7460 return rc;
7461}
7462
7463/**
7464 * Increases the number of objects dependent on the machine state or on the
7465 * registered state. Guarantees that these two states will not change at least
7466 * until #releaseStateDependency() is called.
7467 *
7468 * Depending on the @a aDepType value, additional state checks may be made.
7469 * These checks will set extended error info on failure. See
7470 * #checkStateDependency() for more info.
7471 *
7472 * If this method returns a failure, the dependency is not added and the caller
7473 * is not allowed to rely on any particular machine state or registration state
7474 * value and may return the failed result code to the upper level.
7475 *
7476 * @param aDepType Dependency type to add.
7477 * @param aState Current machine state (NULL if not interested).
7478 * @param aRegistered Current registered state (NULL if not interested).
7479 *
7480 * @note Locks this object for writing.
7481 */
7482HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7483 MachineState_T *aState /* = NULL */,
7484 BOOL *aRegistered /* = NULL */)
7485{
7486 AutoCaller autoCaller(this);
7487 AssertComRCReturnRC(autoCaller.rc());
7488
7489 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7490
7491 HRESULT rc = checkStateDependency(aDepType);
7492 if (FAILED(rc)) return rc;
7493
7494 {
7495 if (mData->mMachineStateChangePending != 0)
7496 {
7497 /* ensureNoStateDependencies() is waiting for state dependencies to
7498 * drop to zero so don't add more. It may make sense to wait a bit
7499 * and retry before reporting an error (since the pending state
7500 * transition should be really quick) but let's just assert for
7501 * now to see if it ever happens on practice. */
7502
7503 AssertFailed();
7504
7505 return setError(E_ACCESSDENIED,
7506 tr("Machine state change is in progress. Please retry the operation later."));
7507 }
7508
7509 ++mData->mMachineStateDeps;
7510 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7511 }
7512
7513 if (aState)
7514 *aState = mData->mMachineState;
7515 if (aRegistered)
7516 *aRegistered = mData->mRegistered;
7517
7518 return S_OK;
7519}
7520
7521/**
7522 * Decreases the number of objects dependent on the machine state.
7523 * Must always complete the #addStateDependency() call after the state
7524 * dependency is no more necessary.
7525 */
7526void Machine::releaseStateDependency()
7527{
7528 AutoCaller autoCaller(this);
7529 AssertComRCReturnVoid(autoCaller.rc());
7530
7531 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7532
7533 /* releaseStateDependency() w/o addStateDependency()? */
7534 AssertReturnVoid(mData->mMachineStateDeps != 0);
7535 -- mData->mMachineStateDeps;
7536
7537 if (mData->mMachineStateDeps == 0)
7538 {
7539 /* inform ensureNoStateDependencies() that there are no more deps */
7540 if (mData->mMachineStateChangePending != 0)
7541 {
7542 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7543 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7544 }
7545 }
7546}
7547
7548// protected methods
7549/////////////////////////////////////////////////////////////////////////////
7550
7551/**
7552 * Performs machine state checks based on the @a aDepType value. If a check
7553 * fails, this method will set extended error info, otherwise it will return
7554 * S_OK. It is supposed, that on failure, the caller will immediately return
7555 * the return value of this method to the upper level.
7556 *
7557 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7558 *
7559 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7560 * current state of this machine object allows to change settings of the
7561 * machine (i.e. the machine is not registered, or registered but not running
7562 * and not saved). It is useful to call this method from Machine setters
7563 * before performing any change.
7564 *
7565 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7566 * as for MutableStateDep except that if the machine is saved, S_OK is also
7567 * returned. This is useful in setters which allow changing machine
7568 * properties when it is in the saved state.
7569 *
7570 * @param aDepType Dependency type to check.
7571 *
7572 * @note Non Machine based classes should use #addStateDependency() and
7573 * #releaseStateDependency() methods or the smart AutoStateDependency
7574 * template.
7575 *
7576 * @note This method must be called from under this object's read or write
7577 * lock.
7578 */
7579HRESULT Machine::checkStateDependency(StateDependency aDepType)
7580{
7581 switch (aDepType)
7582 {
7583 case AnyStateDep:
7584 {
7585 break;
7586 }
7587 case MutableStateDep:
7588 {
7589 if ( mData->mRegistered
7590 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7591 || ( mData->mMachineState != MachineState_Paused
7592 && mData->mMachineState != MachineState_Running
7593 && mData->mMachineState != MachineState_Aborted
7594 && mData->mMachineState != MachineState_Teleported
7595 && mData->mMachineState != MachineState_PoweredOff
7596 )
7597 )
7598 )
7599 return setError(VBOX_E_INVALID_VM_STATE,
7600 tr("The machine is not mutable (state is %s)"),
7601 Global::stringifyMachineState(mData->mMachineState));
7602 break;
7603 }
7604 case MutableOrSavedStateDep:
7605 {
7606 if ( mData->mRegistered
7607 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7608 || ( mData->mMachineState != MachineState_Paused
7609 && mData->mMachineState != MachineState_Running
7610 && mData->mMachineState != MachineState_Aborted
7611 && mData->mMachineState != MachineState_Teleported
7612 && mData->mMachineState != MachineState_Saved
7613 && mData->mMachineState != MachineState_PoweredOff
7614 )
7615 )
7616 )
7617 return setError(VBOX_E_INVALID_VM_STATE,
7618 tr("The machine is not mutable (state is %s)"),
7619 Global::stringifyMachineState(mData->mMachineState));
7620 break;
7621 }
7622 }
7623
7624 return S_OK;
7625}
7626
7627/**
7628 * Helper to initialize all associated child objects and allocate data
7629 * structures.
7630 *
7631 * This method must be called as a part of the object's initialization procedure
7632 * (usually done in the #init() method).
7633 *
7634 * @note Must be called only from #init() or from #registeredInit().
7635 */
7636HRESULT Machine::initDataAndChildObjects()
7637{
7638 AutoCaller autoCaller(this);
7639 AssertComRCReturnRC(autoCaller.rc());
7640 AssertComRCReturn(autoCaller.state() == InInit ||
7641 autoCaller.state() == Limited, E_FAIL);
7642
7643 AssertReturn(!mData->mAccessible, E_FAIL);
7644
7645 /* allocate data structures */
7646 mSSData.allocate();
7647 mUserData.allocate();
7648 mHWData.allocate();
7649 mMediaData.allocate();
7650 mStorageControllers.allocate();
7651
7652 /* initialize mOSTypeId */
7653 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
7654
7655 /* create associated BIOS settings object */
7656 unconst(mBIOSSettings).createObject();
7657 mBIOSSettings->init(this);
7658
7659 /* create an associated VRDE object (default is disabled) */
7660 unconst(mVRDEServer).createObject();
7661 mVRDEServer->init(this);
7662
7663 /* create associated serial port objects */
7664 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
7665 {
7666 unconst(mSerialPorts[slot]).createObject();
7667 mSerialPorts[slot]->init(this, slot);
7668 }
7669
7670 /* create associated parallel port objects */
7671 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7672 {
7673 unconst(mParallelPorts[slot]).createObject();
7674 mParallelPorts[slot]->init(this, slot);
7675 }
7676
7677 /* create the audio adapter object (always present, default is disabled) */
7678 unconst(mAudioAdapter).createObject();
7679 mAudioAdapter->init(this);
7680
7681 /* create the USB controller object (always present, default is disabled) */
7682 unconst(mUSBController).createObject();
7683 mUSBController->init(this);
7684
7685 /* create associated network adapter objects */
7686 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
7687 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7688 {
7689 unconst(mNetworkAdapters[slot]).createObject();
7690 mNetworkAdapters[slot]->init(this, slot);
7691 }
7692
7693 /* create the bandwidth control */
7694 unconst(mBandwidthControl).createObject();
7695 mBandwidthControl->init(this);
7696
7697 return S_OK;
7698}
7699
7700/**
7701 * Helper to uninitialize all associated child objects and to free all data
7702 * structures.
7703 *
7704 * This method must be called as a part of the object's uninitialization
7705 * procedure (usually done in the #uninit() method).
7706 *
7707 * @note Must be called only from #uninit() or from #registeredInit().
7708 */
7709void Machine::uninitDataAndChildObjects()
7710{
7711 AutoCaller autoCaller(this);
7712 AssertComRCReturnVoid(autoCaller.rc());
7713 AssertComRCReturnVoid( autoCaller.state() == InUninit
7714 || autoCaller.state() == Limited);
7715
7716 /* tell all our other child objects we've been uninitialized */
7717 if (mBandwidthControl)
7718 {
7719 mBandwidthControl->uninit();
7720 unconst(mBandwidthControl).setNull();
7721 }
7722
7723 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7724 {
7725 if (mNetworkAdapters[slot])
7726 {
7727 mNetworkAdapters[slot]->uninit();
7728 unconst(mNetworkAdapters[slot]).setNull();
7729 }
7730 }
7731
7732 if (mUSBController)
7733 {
7734 mUSBController->uninit();
7735 unconst(mUSBController).setNull();
7736 }
7737
7738 if (mAudioAdapter)
7739 {
7740 mAudioAdapter->uninit();
7741 unconst(mAudioAdapter).setNull();
7742 }
7743
7744 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7745 {
7746 if (mParallelPorts[slot])
7747 {
7748 mParallelPorts[slot]->uninit();
7749 unconst(mParallelPorts[slot]).setNull();
7750 }
7751 }
7752
7753 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
7754 {
7755 if (mSerialPorts[slot])
7756 {
7757 mSerialPorts[slot]->uninit();
7758 unconst(mSerialPorts[slot]).setNull();
7759 }
7760 }
7761
7762 if (mVRDEServer)
7763 {
7764 mVRDEServer->uninit();
7765 unconst(mVRDEServer).setNull();
7766 }
7767
7768 if (mBIOSSettings)
7769 {
7770 mBIOSSettings->uninit();
7771 unconst(mBIOSSettings).setNull();
7772 }
7773
7774 /* Deassociate hard disks (only when a real Machine or a SnapshotMachine
7775 * instance is uninitialized; SessionMachine instances refer to real
7776 * Machine hard disks). This is necessary for a clean re-initialization of
7777 * the VM after successfully re-checking the accessibility state. Note
7778 * that in case of normal Machine or SnapshotMachine uninitialization (as
7779 * a result of unregistering or deleting the snapshot), outdated hard
7780 * disk attachments will already be uninitialized and deleted, so this
7781 * code will not affect them. */
7782 if ( !!mMediaData
7783 && (!isSessionMachine())
7784 )
7785 {
7786 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
7787 it != mMediaData->mAttachments.end();
7788 ++it)
7789 {
7790 ComObjPtr<Medium> hd = (*it)->getMedium();
7791 if (hd.isNull())
7792 continue;
7793 HRESULT rc = hd->removeBackReference(mData->mUuid, getSnapshotId());
7794 AssertComRC(rc);
7795 }
7796 }
7797
7798 if (!isSessionMachine() && !isSnapshotMachine())
7799 {
7800 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
7801 if (mData->mFirstSnapshot)
7802 {
7803 // snapshots tree is protected by media write lock; strictly
7804 // this isn't necessary here since we're deleting the entire
7805 // machine, but otherwise we assert in Snapshot::uninit()
7806 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7807 mData->mFirstSnapshot->uninit();
7808 mData->mFirstSnapshot.setNull();
7809 }
7810
7811 mData->mCurrentSnapshot.setNull();
7812 }
7813
7814 /* free data structures (the essential mData structure is not freed here
7815 * since it may be still in use) */
7816 mMediaData.free();
7817 mStorageControllers.free();
7818 mHWData.free();
7819 mUserData.free();
7820 mSSData.free();
7821}
7822
7823/**
7824 * Returns a pointer to the Machine object for this machine that acts like a
7825 * parent for complex machine data objects such as shared folders, etc.
7826 *
7827 * For primary Machine objects and for SnapshotMachine objects, returns this
7828 * object's pointer itself. For SessionMachine objects, returns the peer
7829 * (primary) machine pointer.
7830 */
7831Machine* Machine::getMachine()
7832{
7833 if (isSessionMachine())
7834 return (Machine*)mPeer;
7835 return this;
7836}
7837
7838/**
7839 * Makes sure that there are no machine state dependents. If necessary, waits
7840 * for the number of dependents to drop to zero.
7841 *
7842 * Make sure this method is called from under this object's write lock to
7843 * guarantee that no new dependents may be added when this method returns
7844 * control to the caller.
7845 *
7846 * @note Locks this object for writing. The lock will be released while waiting
7847 * (if necessary).
7848 *
7849 * @warning To be used only in methods that change the machine state!
7850 */
7851void Machine::ensureNoStateDependencies()
7852{
7853 AssertReturnVoid(isWriteLockOnCurrentThread());
7854
7855 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7856
7857 /* Wait for all state dependents if necessary */
7858 if (mData->mMachineStateDeps != 0)
7859 {
7860 /* lazy semaphore creation */
7861 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
7862 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
7863
7864 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
7865 mData->mMachineStateDeps));
7866
7867 ++mData->mMachineStateChangePending;
7868
7869 /* reset the semaphore before waiting, the last dependent will signal
7870 * it */
7871 RTSemEventMultiReset(mData->mMachineStateDepsSem);
7872
7873 alock.release();
7874
7875 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
7876
7877 alock.acquire();
7878
7879 -- mData->mMachineStateChangePending;
7880 }
7881}
7882
7883/**
7884 * Changes the machine state and informs callbacks.
7885 *
7886 * This method is not intended to fail so it either returns S_OK or asserts (and
7887 * returns a failure).
7888 *
7889 * @note Locks this object for writing.
7890 */
7891HRESULT Machine::setMachineState(MachineState_T aMachineState)
7892{
7893 LogFlowThisFuncEnter();
7894 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
7895
7896 AutoCaller autoCaller(this);
7897 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7898
7899 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7900
7901 /* wait for state dependents to drop to zero */
7902 ensureNoStateDependencies();
7903
7904 if (mData->mMachineState != aMachineState)
7905 {
7906 mData->mMachineState = aMachineState;
7907
7908 RTTimeNow(&mData->mLastStateChange);
7909
7910 mParent->onMachineStateChange(mData->mUuid, aMachineState);
7911 }
7912
7913 LogFlowThisFuncLeave();
7914 return S_OK;
7915}
7916
7917/**
7918 * Searches for a shared folder with the given logical name
7919 * in the collection of shared folders.
7920 *
7921 * @param aName logical name of the shared folder
7922 * @param aSharedFolder where to return the found object
7923 * @param aSetError whether to set the error info if the folder is
7924 * not found
7925 * @return
7926 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
7927 *
7928 * @note
7929 * must be called from under the object's lock!
7930 */
7931HRESULT Machine::findSharedFolder(const Utf8Str &aName,
7932 ComObjPtr<SharedFolder> &aSharedFolder,
7933 bool aSetError /* = false */)
7934{
7935 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
7936 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
7937 it != mHWData->mSharedFolders.end();
7938 ++it)
7939 {
7940 SharedFolder *pSF = *it;
7941 AutoCaller autoCaller(pSF);
7942 if (pSF->getName() == aName)
7943 {
7944 aSharedFolder = pSF;
7945 rc = S_OK;
7946 break;
7947 }
7948 }
7949
7950 if (aSetError && FAILED(rc))
7951 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
7952
7953 return rc;
7954}
7955
7956/**
7957 * Initializes all machine instance data from the given settings structures
7958 * from XML. The exception is the machine UUID which needs special handling
7959 * depending on the caller's use case, so the caller needs to set that herself.
7960 *
7961 * This gets called in several contexts during machine initialization:
7962 *
7963 * -- When machine XML exists on disk already and needs to be loaded into memory,
7964 * for example, from registeredInit() to load all registered machines on
7965 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
7966 * attached to the machine should be part of some media registry already.
7967 *
7968 * -- During OVF import, when a machine config has been constructed from an
7969 * OVF file. In this case, puuidRegistry is set to the machine UUID to
7970 * ensure that the media listed as attachments in the config (which have
7971 * been imported from the OVF) receive the correct registry ID.
7972 *
7973 * -- During VM cloning.
7974 *
7975 * @param config Machine settings from XML.
7976 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
7977 * @return
7978 */
7979HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
7980 const Guid *puuidRegistry)
7981{
7982 // copy name, description, OS type, teleporter, UTC etc.
7983 mUserData->s = config.machineUserData;
7984
7985 // look up the object by Id to check it is valid
7986 ComPtr<IGuestOSType> guestOSType;
7987 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
7988 guestOSType.asOutParam());
7989 if (FAILED(rc)) return rc;
7990
7991 // stateFile (optional)
7992 if (config.strStateFile.isEmpty())
7993 mSSData->strStateFilePath.setNull();
7994 else
7995 {
7996 Utf8Str stateFilePathFull(config.strStateFile);
7997 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
7998 if (RT_FAILURE(vrc))
7999 return setError(E_FAIL,
8000 tr("Invalid saved state file path '%s' (%Rrc)"),
8001 config.strStateFile.c_str(),
8002 vrc);
8003 mSSData->strStateFilePath = stateFilePathFull;
8004 }
8005
8006 // snapshot folder needs special processing so set it again
8007 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8008 if (FAILED(rc)) return rc;
8009
8010 /* Copy the extra data items (Not in any case config is already the same as
8011 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8012 * make sure the extra data map is copied). */
8013 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8014
8015 /* currentStateModified (optional, default is true) */
8016 mData->mCurrentStateModified = config.fCurrentStateModified;
8017
8018 mData->mLastStateChange = config.timeLastStateChange;
8019
8020 /*
8021 * note: all mUserData members must be assigned prior this point because
8022 * we need to commit changes in order to let mUserData be shared by all
8023 * snapshot machine instances.
8024 */
8025 mUserData.commitCopy();
8026
8027 // machine registry, if present (must be loaded before snapshots)
8028 if (config.canHaveOwnMediaRegistry())
8029 {
8030 // determine machine folder
8031 Utf8Str strMachineFolder = getSettingsFileFull();
8032 strMachineFolder.stripFilename();
8033 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8034 config.mediaRegistry,
8035 strMachineFolder);
8036 if (FAILED(rc)) return rc;
8037 }
8038
8039 /* Snapshot node (optional) */
8040 size_t cRootSnapshots;
8041 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8042 {
8043 // there must be only one root snapshot
8044 Assert(cRootSnapshots == 1);
8045
8046 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8047
8048 rc = loadSnapshot(snap,
8049 config.uuidCurrentSnapshot,
8050 NULL); // no parent == first snapshot
8051 if (FAILED(rc)) return rc;
8052 }
8053
8054 // hardware data
8055 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8056 if (FAILED(rc)) return rc;
8057
8058 // load storage controllers
8059 rc = loadStorageControllers(config.storageMachine,
8060 puuidRegistry,
8061 NULL /* puuidSnapshot */);
8062 if (FAILED(rc)) return rc;
8063
8064 /*
8065 * NOTE: the assignment below must be the last thing to do,
8066 * otherwise it will be not possible to change the settings
8067 * somewhere in the code above because all setters will be
8068 * blocked by checkStateDependency(MutableStateDep).
8069 */
8070
8071 /* set the machine state to Aborted or Saved when appropriate */
8072 if (config.fAborted)
8073 {
8074 mSSData->strStateFilePath.setNull();
8075
8076 /* no need to use setMachineState() during init() */
8077 mData->mMachineState = MachineState_Aborted;
8078 }
8079 else if (!mSSData->strStateFilePath.isEmpty())
8080 {
8081 /* no need to use setMachineState() during init() */
8082 mData->mMachineState = MachineState_Saved;
8083 }
8084
8085 // after loading settings, we are no longer different from the XML on disk
8086 mData->flModifications = 0;
8087
8088 return S_OK;
8089}
8090
8091/**
8092 * Recursively loads all snapshots starting from the given.
8093 *
8094 * @param aNode <Snapshot> node.
8095 * @param aCurSnapshotId Current snapshot ID from the settings file.
8096 * @param aParentSnapshot Parent snapshot.
8097 */
8098HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8099 const Guid &aCurSnapshotId,
8100 Snapshot *aParentSnapshot)
8101{
8102 AssertReturn(!isSnapshotMachine(), E_FAIL);
8103 AssertReturn(!isSessionMachine(), E_FAIL);
8104
8105 HRESULT rc = S_OK;
8106
8107 Utf8Str strStateFile;
8108 if (!data.strStateFile.isEmpty())
8109 {
8110 /* optional */
8111 strStateFile = data.strStateFile;
8112 int vrc = calculateFullPath(strStateFile, strStateFile);
8113 if (RT_FAILURE(vrc))
8114 return setError(E_FAIL,
8115 tr("Invalid saved state file path '%s' (%Rrc)"),
8116 strStateFile.c_str(),
8117 vrc);
8118 }
8119
8120 /* create a snapshot machine object */
8121 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8122 pSnapshotMachine.createObject();
8123 rc = pSnapshotMachine->initFromSettings(this,
8124 data.hardware,
8125 &data.debugging,
8126 &data.autostart,
8127 data.storage,
8128 data.uuid.ref(),
8129 strStateFile);
8130 if (FAILED(rc)) return rc;
8131
8132 /* create a snapshot object */
8133 ComObjPtr<Snapshot> pSnapshot;
8134 pSnapshot.createObject();
8135 /* initialize the snapshot */
8136 rc = pSnapshot->init(mParent, // VirtualBox object
8137 data.uuid,
8138 data.strName,
8139 data.strDescription,
8140 data.timestamp,
8141 pSnapshotMachine,
8142 aParentSnapshot);
8143 if (FAILED(rc)) return rc;
8144
8145 /* memorize the first snapshot if necessary */
8146 if (!mData->mFirstSnapshot)
8147 mData->mFirstSnapshot = pSnapshot;
8148
8149 /* memorize the current snapshot when appropriate */
8150 if ( !mData->mCurrentSnapshot
8151 && pSnapshot->getId() == aCurSnapshotId
8152 )
8153 mData->mCurrentSnapshot = pSnapshot;
8154
8155 // now create the children
8156 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8157 it != data.llChildSnapshots.end();
8158 ++it)
8159 {
8160 const settings::Snapshot &childData = *it;
8161 // recurse
8162 rc = loadSnapshot(childData,
8163 aCurSnapshotId,
8164 pSnapshot); // parent = the one we created above
8165 if (FAILED(rc)) return rc;
8166 }
8167
8168 return rc;
8169}
8170
8171/**
8172 * Loads settings into mHWData.
8173 *
8174 * @param data Reference to the hardware settings.
8175 * @param pDbg Pointer to the debugging settings.
8176 * @param pAutostart Pointer to the autostart settings.
8177 */
8178HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8179 const settings::Autostart *pAutostart)
8180{
8181 AssertReturn(!isSessionMachine(), E_FAIL);
8182
8183 HRESULT rc = S_OK;
8184
8185 try
8186 {
8187 /* The hardware version attribute (optional). */
8188 mHWData->mHWVersion = data.strVersion;
8189 mHWData->mHardwareUUID = data.uuid;
8190
8191 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8192 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8193 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8194 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8195 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8196 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8197 mHWData->mPAEEnabled = data.fPAE;
8198 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8199
8200 mHWData->mCPUCount = data.cCPUs;
8201 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8202 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8203
8204 // cpu
8205 if (mHWData->mCPUHotPlugEnabled)
8206 {
8207 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8208 it != data.llCpus.end();
8209 ++it)
8210 {
8211 const settings::Cpu &cpu = *it;
8212
8213 mHWData->mCPUAttached[cpu.ulId] = true;
8214 }
8215 }
8216
8217 // cpuid leafs
8218 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8219 it != data.llCpuIdLeafs.end();
8220 ++it)
8221 {
8222 const settings::CpuIdLeaf &leaf = *it;
8223
8224 switch (leaf.ulId)
8225 {
8226 case 0x0:
8227 case 0x1:
8228 case 0x2:
8229 case 0x3:
8230 case 0x4:
8231 case 0x5:
8232 case 0x6:
8233 case 0x7:
8234 case 0x8:
8235 case 0x9:
8236 case 0xA:
8237 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8238 break;
8239
8240 case 0x80000000:
8241 case 0x80000001:
8242 case 0x80000002:
8243 case 0x80000003:
8244 case 0x80000004:
8245 case 0x80000005:
8246 case 0x80000006:
8247 case 0x80000007:
8248 case 0x80000008:
8249 case 0x80000009:
8250 case 0x8000000A:
8251 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8252 break;
8253
8254 default:
8255 /* just ignore */
8256 break;
8257 }
8258 }
8259
8260 mHWData->mMemorySize = data.ulMemorySizeMB;
8261 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8262
8263 // boot order
8264 for (size_t i = 0;
8265 i < RT_ELEMENTS(mHWData->mBootOrder);
8266 i++)
8267 {
8268 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8269 if (it == data.mapBootOrder.end())
8270 mHWData->mBootOrder[i] = DeviceType_Null;
8271 else
8272 mHWData->mBootOrder[i] = it->second;
8273 }
8274
8275 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8276 mHWData->mMonitorCount = data.cMonitors;
8277 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8278 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8279 mHWData->mFirmwareType = data.firmwareType;
8280 mHWData->mPointingHidType = data.pointingHidType;
8281 mHWData->mKeyboardHidType = data.keyboardHidType;
8282 mHWData->mChipsetType = data.chipsetType;
8283 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8284 mHWData->mHpetEnabled = data.fHpetEnabled;
8285
8286 /* VRDEServer */
8287 rc = mVRDEServer->loadSettings(data.vrdeSettings);
8288 if (FAILED(rc)) return rc;
8289
8290 /* BIOS */
8291 rc = mBIOSSettings->loadSettings(data.biosSettings);
8292 if (FAILED(rc)) return rc;
8293
8294 // Bandwidth control (must come before network adapters)
8295 rc = mBandwidthControl->loadSettings(data.ioSettings);
8296 if (FAILED(rc)) return rc;
8297
8298 /* USB Controller */
8299 rc = mUSBController->loadSettings(data.usbController);
8300 if (FAILED(rc)) return rc;
8301
8302 // network adapters
8303 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8304 uint32_t oldCount = mNetworkAdapters.size();
8305 if (newCount > oldCount)
8306 {
8307 mNetworkAdapters.resize(newCount);
8308 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
8309 {
8310 unconst(mNetworkAdapters[slot]).createObject();
8311 mNetworkAdapters[slot]->init(this, slot);
8312 }
8313 }
8314 else if (newCount < oldCount)
8315 mNetworkAdapters.resize(newCount);
8316 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8317 it != data.llNetworkAdapters.end();
8318 ++it)
8319 {
8320 const settings::NetworkAdapter &nic = *it;
8321
8322 /* slot unicity is guaranteed by XML Schema */
8323 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8324 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
8325 if (FAILED(rc)) return rc;
8326 }
8327
8328 // serial ports
8329 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8330 it != data.llSerialPorts.end();
8331 ++it)
8332 {
8333 const settings::SerialPort &s = *it;
8334
8335 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8336 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
8337 if (FAILED(rc)) return rc;
8338 }
8339
8340 // parallel ports (optional)
8341 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8342 it != data.llParallelPorts.end();
8343 ++it)
8344 {
8345 const settings::ParallelPort &p = *it;
8346
8347 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8348 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
8349 if (FAILED(rc)) return rc;
8350 }
8351
8352 /* AudioAdapter */
8353 rc = mAudioAdapter->loadSettings(data.audioAdapter);
8354 if (FAILED(rc)) return rc;
8355
8356 /* Shared folders */
8357 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8358 it != data.llSharedFolders.end();
8359 ++it)
8360 {
8361 const settings::SharedFolder &sf = *it;
8362
8363 ComObjPtr<SharedFolder> sharedFolder;
8364 /* Check for double entries. Not allowed! */
8365 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8366 if (SUCCEEDED(rc))
8367 return setError(VBOX_E_OBJECT_IN_USE,
8368 tr("Shared folder named '%s' already exists"),
8369 sf.strName.c_str());
8370
8371 /* Create the new shared folder. Don't break on error. This will be
8372 * reported when the machine starts. */
8373 sharedFolder.createObject();
8374 rc = sharedFolder->init(getMachine(),
8375 sf.strName,
8376 sf.strHostPath,
8377 RT_BOOL(sf.fWritable),
8378 RT_BOOL(sf.fAutoMount),
8379 false /* fFailOnError */);
8380 if (FAILED(rc)) return rc;
8381 mHWData->mSharedFolders.push_back(sharedFolder);
8382 }
8383
8384 // Clipboard
8385 mHWData->mClipboardMode = data.clipboardMode;
8386
8387 // guest settings
8388 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8389
8390 // IO settings
8391 mHWData->mIoCacheEnabled = data.ioSettings.fIoCacheEnabled;
8392 mHWData->mIoCacheSize = data.ioSettings.ulIoCacheSize;
8393
8394 // Host PCI devices
8395 for (settings::HostPciDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8396 it != data.pciAttachments.end();
8397 ++it)
8398 {
8399 const settings::HostPciDeviceAttachment &hpda = *it;
8400 ComObjPtr<PciDeviceAttachment> pda;
8401
8402 pda.createObject();
8403 pda->loadSettings(this, hpda);
8404 mHWData->mPciDeviceAssignments.push_back(pda);
8405 }
8406
8407 /*
8408 * (The following isn't really real hardware, but it lives in HWData
8409 * for reasons of convenience.)
8410 */
8411
8412#ifdef VBOX_WITH_GUEST_PROPS
8413 /* Guest properties (optional) */
8414 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8415 it != data.llGuestProperties.end();
8416 ++it)
8417 {
8418 const settings::GuestProperty &prop = *it;
8419 uint32_t fFlags = guestProp::NILFLAG;
8420 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8421 HWData::GuestProperty property = { prop.strName, prop.strValue, (LONG64) prop.timestamp, fFlags };
8422 mHWData->mGuestProperties.push_back(property);
8423 }
8424
8425 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8426#endif /* VBOX_WITH_GUEST_PROPS defined */
8427
8428 rc = loadDebugging(pDbg);
8429 if (FAILED(rc))
8430 return rc;
8431
8432 mHWData->mAutostart = *pAutostart;
8433 }
8434 catch(std::bad_alloc &)
8435 {
8436 return E_OUTOFMEMORY;
8437 }
8438
8439 AssertComRC(rc);
8440 return rc;
8441}
8442
8443/**
8444 * Called from Machine::loadHardware() to load the debugging settings of the
8445 * machine.
8446 *
8447 * @param pDbg Pointer to the settings.
8448 */
8449HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
8450{
8451 mHWData->mDebugging = *pDbg;
8452 /* no more processing currently required, this will probably change. */
8453 return S_OK;
8454}
8455
8456/**
8457 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
8458 *
8459 * @param data
8460 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8461 * @param puuidSnapshot
8462 * @return
8463 */
8464HRESULT Machine::loadStorageControllers(const settings::Storage &data,
8465 const Guid *puuidRegistry,
8466 const Guid *puuidSnapshot)
8467{
8468 AssertReturn(!isSessionMachine(), E_FAIL);
8469
8470 HRESULT rc = S_OK;
8471
8472 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8473 it != data.llStorageControllers.end();
8474 ++it)
8475 {
8476 const settings::StorageController &ctlData = *it;
8477
8478 ComObjPtr<StorageController> pCtl;
8479 /* Try to find one with the name first. */
8480 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8481 if (SUCCEEDED(rc))
8482 return setError(VBOX_E_OBJECT_IN_USE,
8483 tr("Storage controller named '%s' already exists"),
8484 ctlData.strName.c_str());
8485
8486 pCtl.createObject();
8487 rc = pCtl->init(this,
8488 ctlData.strName,
8489 ctlData.storageBus,
8490 ctlData.ulInstance,
8491 ctlData.fBootable);
8492 if (FAILED(rc)) return rc;
8493
8494 mStorageControllers->push_back(pCtl);
8495
8496 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8497 if (FAILED(rc)) return rc;
8498
8499 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8500 if (FAILED(rc)) return rc;
8501
8502 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8503 if (FAILED(rc)) return rc;
8504
8505 /* Set IDE emulation settings (only for AHCI controller). */
8506 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8507 {
8508 if ( (FAILED(rc = pCtl->SetIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8509 || (FAILED(rc = pCtl->SetIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8510 || (FAILED(rc = pCtl->SetIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8511 || (FAILED(rc = pCtl->SetIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8512 )
8513 return rc;
8514 }
8515
8516 /* Load the attached devices now. */
8517 rc = loadStorageDevices(pCtl,
8518 ctlData,
8519 puuidRegistry,
8520 puuidSnapshot);
8521 if (FAILED(rc)) return rc;
8522 }
8523
8524 return S_OK;
8525}
8526
8527/**
8528 * Called from loadStorageControllers for a controller's devices.
8529 *
8530 * @param aStorageController
8531 * @param data
8532 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8533 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
8534 * @return
8535 */
8536HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
8537 const settings::StorageController &data,
8538 const Guid *puuidRegistry,
8539 const Guid *puuidSnapshot)
8540{
8541 HRESULT rc = S_OK;
8542
8543 /* paranoia: detect duplicate attachments */
8544 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8545 it != data.llAttachedDevices.end();
8546 ++it)
8547 {
8548 const settings::AttachedDevice &ad = *it;
8549
8550 for (settings::AttachedDevicesList::const_iterator it2 = it;
8551 it2 != data.llAttachedDevices.end();
8552 ++it2)
8553 {
8554 if (it == it2)
8555 continue;
8556
8557 const settings::AttachedDevice &ad2 = *it2;
8558
8559 if ( ad.lPort == ad2.lPort
8560 && ad.lDevice == ad2.lDevice)
8561 {
8562 return setError(E_FAIL,
8563 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
8564 aStorageController->getName().c_str(),
8565 ad.lPort,
8566 ad.lDevice,
8567 mUserData->s.strName.c_str());
8568 }
8569 }
8570 }
8571
8572 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8573 it != data.llAttachedDevices.end();
8574 ++it)
8575 {
8576 const settings::AttachedDevice &dev = *it;
8577 ComObjPtr<Medium> medium;
8578
8579 switch (dev.deviceType)
8580 {
8581 case DeviceType_Floppy:
8582 case DeviceType_DVD:
8583 if (dev.strHostDriveSrc.isNotEmpty())
8584 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
8585 else
8586 rc = mParent->findRemoveableMedium(dev.deviceType,
8587 dev.uuid,
8588 false /* fRefresh */,
8589 false /* aSetError */,
8590 medium);
8591 if (rc == VBOX_E_OBJECT_NOT_FOUND)
8592 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
8593 rc = S_OK;
8594 break;
8595
8596 case DeviceType_HardDisk:
8597 {
8598 /* find a hard disk by UUID */
8599 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
8600 if (FAILED(rc))
8601 {
8602 if (isSnapshotMachine())
8603 {
8604 // wrap another error message around the "cannot find hard disk" set by findHardDisk
8605 // so the user knows that the bad disk is in a snapshot somewhere
8606 com::ErrorInfo info;
8607 return setError(E_FAIL,
8608 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
8609 puuidSnapshot->raw(),
8610 info.getText().raw());
8611 }
8612 else
8613 return rc;
8614 }
8615
8616 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
8617
8618 if (medium->getType() == MediumType_Immutable)
8619 {
8620 if (isSnapshotMachine())
8621 return setError(E_FAIL,
8622 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8623 "of the virtual machine '%s' ('%s')"),
8624 medium->getLocationFull().c_str(),
8625 dev.uuid.raw(),
8626 puuidSnapshot->raw(),
8627 mUserData->s.strName.c_str(),
8628 mData->m_strConfigFileFull.c_str());
8629
8630 return setError(E_FAIL,
8631 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8632 medium->getLocationFull().c_str(),
8633 dev.uuid.raw(),
8634 mUserData->s.strName.c_str(),
8635 mData->m_strConfigFileFull.c_str());
8636 }
8637
8638 if (medium->getType() == MediumType_MultiAttach)
8639 {
8640 if (isSnapshotMachine())
8641 return setError(E_FAIL,
8642 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8643 "of the virtual machine '%s' ('%s')"),
8644 medium->getLocationFull().c_str(),
8645 dev.uuid.raw(),
8646 puuidSnapshot->raw(),
8647 mUserData->s.strName.c_str(),
8648 mData->m_strConfigFileFull.c_str());
8649
8650 return setError(E_FAIL,
8651 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8652 medium->getLocationFull().c_str(),
8653 dev.uuid.raw(),
8654 mUserData->s.strName.c_str(),
8655 mData->m_strConfigFileFull.c_str());
8656 }
8657
8658 if ( !isSnapshotMachine()
8659 && medium->getChildren().size() != 0
8660 )
8661 return setError(E_FAIL,
8662 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
8663 "because it has %d differencing child hard disks"),
8664 medium->getLocationFull().c_str(),
8665 dev.uuid.raw(),
8666 mUserData->s.strName.c_str(),
8667 mData->m_strConfigFileFull.c_str(),
8668 medium->getChildren().size());
8669
8670 if (findAttachment(mMediaData->mAttachments,
8671 medium))
8672 return setError(E_FAIL,
8673 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
8674 medium->getLocationFull().c_str(),
8675 dev.uuid.raw(),
8676 mUserData->s.strName.c_str(),
8677 mData->m_strConfigFileFull.c_str());
8678
8679 break;
8680 }
8681
8682 default:
8683 return setError(E_FAIL,
8684 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
8685 medium->getLocationFull().c_str(),
8686 mUserData->s.strName.c_str(),
8687 mData->m_strConfigFileFull.c_str());
8688 }
8689
8690 if (FAILED(rc))
8691 break;
8692
8693 /* Bandwidth groups are loaded at this point. */
8694 ComObjPtr<BandwidthGroup> pBwGroup;
8695
8696 if (!dev.strBwGroup.isEmpty())
8697 {
8698 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
8699 if (FAILED(rc))
8700 return setError(E_FAIL,
8701 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
8702 medium->getLocationFull().c_str(),
8703 dev.strBwGroup.c_str(),
8704 mUserData->s.strName.c_str(),
8705 mData->m_strConfigFileFull.c_str());
8706 pBwGroup->reference();
8707 }
8708
8709 const Bstr controllerName = aStorageController->getName();
8710 ComObjPtr<MediumAttachment> pAttachment;
8711 pAttachment.createObject();
8712 rc = pAttachment->init(this,
8713 medium,
8714 controllerName,
8715 dev.lPort,
8716 dev.lDevice,
8717 dev.deviceType,
8718 false,
8719 dev.fPassThrough,
8720 dev.fTempEject,
8721 dev.fNonRotational,
8722 dev.fDiscard,
8723 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
8724 if (FAILED(rc)) break;
8725
8726 /* associate the medium with this machine and snapshot */
8727 if (!medium.isNull())
8728 {
8729 AutoCaller medCaller(medium);
8730 if (FAILED(medCaller.rc())) return medCaller.rc();
8731 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
8732
8733 if (isSnapshotMachine())
8734 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
8735 else
8736 rc = medium->addBackReference(mData->mUuid);
8737 /* If the medium->addBackReference fails it sets an appropriate
8738 * error message, so no need to do any guesswork here. */
8739
8740 if (puuidRegistry)
8741 // caller wants registry ID to be set on all attached media (OVF import case)
8742 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
8743 }
8744
8745 if (FAILED(rc))
8746 break;
8747
8748 /* back up mMediaData to let registeredInit() properly rollback on failure
8749 * (= limited accessibility) */
8750 setModified(IsModified_Storage);
8751 mMediaData.backup();
8752 mMediaData->mAttachments.push_back(pAttachment);
8753 }
8754
8755 return rc;
8756}
8757
8758/**
8759 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
8760 *
8761 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
8762 * @param aSnapshot where to return the found snapshot
8763 * @param aSetError true to set extended error info on failure
8764 */
8765HRESULT Machine::findSnapshotById(const Guid &aId,
8766 ComObjPtr<Snapshot> &aSnapshot,
8767 bool aSetError /* = false */)
8768{
8769 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
8770
8771 if (!mData->mFirstSnapshot)
8772 {
8773 if (aSetError)
8774 return setError(E_FAIL, tr("This machine does not have any snapshots"));
8775 return E_FAIL;
8776 }
8777
8778 if (aId.isEmpty())
8779 aSnapshot = mData->mFirstSnapshot;
8780 else
8781 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
8782
8783 if (!aSnapshot)
8784 {
8785 if (aSetError)
8786 return setError(E_FAIL,
8787 tr("Could not find a snapshot with UUID {%s}"),
8788 aId.toString().c_str());
8789 return E_FAIL;
8790 }
8791
8792 return S_OK;
8793}
8794
8795/**
8796 * Returns the snapshot with the given name or fails of no such snapshot.
8797 *
8798 * @param aName snapshot name to find
8799 * @param aSnapshot where to return the found snapshot
8800 * @param aSetError true to set extended error info on failure
8801 */
8802HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
8803 ComObjPtr<Snapshot> &aSnapshot,
8804 bool aSetError /* = false */)
8805{
8806 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
8807
8808 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
8809
8810 if (!mData->mFirstSnapshot)
8811 {
8812 if (aSetError)
8813 return setError(VBOX_E_OBJECT_NOT_FOUND,
8814 tr("This machine does not have any snapshots"));
8815 return VBOX_E_OBJECT_NOT_FOUND;
8816 }
8817
8818 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
8819
8820 if (!aSnapshot)
8821 {
8822 if (aSetError)
8823 return setError(VBOX_E_OBJECT_NOT_FOUND,
8824 tr("Could not find a snapshot named '%s'"), strName.c_str());
8825 return VBOX_E_OBJECT_NOT_FOUND;
8826 }
8827
8828 return S_OK;
8829}
8830
8831/**
8832 * Returns a storage controller object with the given name.
8833 *
8834 * @param aName storage controller name to find
8835 * @param aStorageController where to return the found storage controller
8836 * @param aSetError true to set extended error info on failure
8837 */
8838HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
8839 ComObjPtr<StorageController> &aStorageController,
8840 bool aSetError /* = false */)
8841{
8842 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
8843
8844 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
8845 it != mStorageControllers->end();
8846 ++it)
8847 {
8848 if ((*it)->getName() == aName)
8849 {
8850 aStorageController = (*it);
8851 return S_OK;
8852 }
8853 }
8854
8855 if (aSetError)
8856 return setError(VBOX_E_OBJECT_NOT_FOUND,
8857 tr("Could not find a storage controller named '%s'"),
8858 aName.c_str());
8859 return VBOX_E_OBJECT_NOT_FOUND;
8860}
8861
8862HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
8863 MediaData::AttachmentList &atts)
8864{
8865 AutoCaller autoCaller(this);
8866 if (FAILED(autoCaller.rc())) return autoCaller.rc();
8867
8868 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8869
8870 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
8871 it != mMediaData->mAttachments.end();
8872 ++it)
8873 {
8874 const ComObjPtr<MediumAttachment> &pAtt = *it;
8875
8876 // should never happen, but deal with NULL pointers in the list.
8877 AssertStmt(!pAtt.isNull(), continue);
8878
8879 // getControllerName() needs caller+read lock
8880 AutoCaller autoAttCaller(pAtt);
8881 if (FAILED(autoAttCaller.rc()))
8882 {
8883 atts.clear();
8884 return autoAttCaller.rc();
8885 }
8886 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
8887
8888 if (pAtt->getControllerName() == aName)
8889 atts.push_back(pAtt);
8890 }
8891
8892 return S_OK;
8893}
8894
8895/**
8896 * Helper for #saveSettings. Cares about renaming the settings directory and
8897 * file if the machine name was changed and about creating a new settings file
8898 * if this is a new machine.
8899 *
8900 * @note Must be never called directly but only from #saveSettings().
8901 */
8902HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
8903{
8904 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
8905
8906 HRESULT rc = S_OK;
8907
8908 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
8909
8910 /* attempt to rename the settings file if machine name is changed */
8911 if ( mUserData->s.fNameSync
8912 && mUserData.isBackedUp()
8913 && mUserData.backedUpData()->s.strName != mUserData->s.strName
8914 )
8915 {
8916 bool dirRenamed = false;
8917 bool fileRenamed = false;
8918
8919 Utf8Str configFile, newConfigFile;
8920 Utf8Str configFilePrev, newConfigFilePrev;
8921 Utf8Str configDir, newConfigDir;
8922
8923 do
8924 {
8925 int vrc = VINF_SUCCESS;
8926
8927 Utf8Str name = mUserData.backedUpData()->s.strName;
8928 Utf8Str newName = mUserData->s.strName;
8929
8930 configFile = mData->m_strConfigFileFull;
8931
8932 /* first, rename the directory if it matches the machine name */
8933 configDir = configFile;
8934 configDir.stripFilename();
8935 newConfigDir = configDir;
8936 if (!strcmp(RTPathFilename(configDir.c_str()), name.c_str()))
8937 {
8938 newConfigDir.stripFilename();
8939 newConfigDir.append(RTPATH_DELIMITER);
8940 newConfigDir.append(newName);
8941 /* new dir and old dir cannot be equal here because of 'if'
8942 * above and because name != newName */
8943 Assert(configDir != newConfigDir);
8944 if (!fSettingsFileIsNew)
8945 {
8946 /* perform real rename only if the machine is not new */
8947 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
8948 if (RT_FAILURE(vrc))
8949 {
8950 rc = setError(E_FAIL,
8951 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
8952 configDir.c_str(),
8953 newConfigDir.c_str(),
8954 vrc);
8955 break;
8956 }
8957 dirRenamed = true;
8958 }
8959 }
8960
8961 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
8962 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
8963
8964 /* then try to rename the settings file itself */
8965 if (newConfigFile != configFile)
8966 {
8967 /* get the path to old settings file in renamed directory */
8968 configFile = Utf8StrFmt("%s%c%s",
8969 newConfigDir.c_str(),
8970 RTPATH_DELIMITER,
8971 RTPathFilename(configFile.c_str()));
8972 if (!fSettingsFileIsNew)
8973 {
8974 /* perform real rename only if the machine is not new */
8975 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
8976 if (RT_FAILURE(vrc))
8977 {
8978 rc = setError(E_FAIL,
8979 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
8980 configFile.c_str(),
8981 newConfigFile.c_str(),
8982 vrc);
8983 break;
8984 }
8985 fileRenamed = true;
8986 configFilePrev = configFile;
8987 configFilePrev += "-prev";
8988 newConfigFilePrev = newConfigFile;
8989 newConfigFilePrev += "-prev";
8990 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
8991 }
8992 }
8993
8994 // update m_strConfigFileFull amd mConfigFile
8995 mData->m_strConfigFileFull = newConfigFile;
8996 // compute the relative path too
8997 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
8998
8999 // store the old and new so that VirtualBox::saveSettings() can update
9000 // the media registry
9001 if ( mData->mRegistered
9002 && configDir != newConfigDir)
9003 {
9004 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9005
9006 if (pfNeedsGlobalSaveSettings)
9007 *pfNeedsGlobalSaveSettings = true;
9008 }
9009
9010 // in the saved state file path, replace the old directory with the new directory
9011 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9012 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9013
9014 // and do the same thing for the saved state file paths of all the online snapshots
9015 if (mData->mFirstSnapshot)
9016 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9017 newConfigDir.c_str());
9018 }
9019 while (0);
9020
9021 if (FAILED(rc))
9022 {
9023 /* silently try to rename everything back */
9024 if (fileRenamed)
9025 {
9026 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9027 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9028 }
9029 if (dirRenamed)
9030 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9031 }
9032
9033 if (FAILED(rc)) return rc;
9034 }
9035
9036 if (fSettingsFileIsNew)
9037 {
9038 /* create a virgin config file */
9039 int vrc = VINF_SUCCESS;
9040
9041 /* ensure the settings directory exists */
9042 Utf8Str path(mData->m_strConfigFileFull);
9043 path.stripFilename();
9044 if (!RTDirExists(path.c_str()))
9045 {
9046 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9047 if (RT_FAILURE(vrc))
9048 {
9049 return setError(E_FAIL,
9050 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9051 path.c_str(),
9052 vrc);
9053 }
9054 }
9055
9056 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9057 path = Utf8Str(mData->m_strConfigFileFull);
9058 RTFILE f = NIL_RTFILE;
9059 vrc = RTFileOpen(&f, path.c_str(),
9060 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9061 if (RT_FAILURE(vrc))
9062 return setError(E_FAIL,
9063 tr("Could not create the settings file '%s' (%Rrc)"),
9064 path.c_str(),
9065 vrc);
9066 RTFileClose(f);
9067 }
9068
9069 return rc;
9070}
9071
9072/**
9073 * Saves and commits machine data, user data and hardware data.
9074 *
9075 * Note that on failure, the data remains uncommitted.
9076 *
9077 * @a aFlags may combine the following flags:
9078 *
9079 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9080 * Used when saving settings after an operation that makes them 100%
9081 * correspond to the settings from the current snapshot.
9082 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9083 * #isReallyModified() returns false. This is necessary for cases when we
9084 * change machine data directly, not through the backup()/commit() mechanism.
9085 * - SaveS_Force: settings will be saved without doing a deep compare of the
9086 * settings structures. This is used when this is called because snapshots
9087 * have changed to avoid the overhead of the deep compare.
9088 *
9089 * @note Must be called from under this object's write lock. Locks children for
9090 * writing.
9091 *
9092 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9093 * initialized to false and that will be set to true by this function if
9094 * the caller must invoke VirtualBox::saveSettings() because the global
9095 * settings have changed. This will happen if a machine rename has been
9096 * saved and the global machine and media registries will therefore need
9097 * updating.
9098 */
9099HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9100 int aFlags /*= 0*/)
9101{
9102 LogFlowThisFuncEnter();
9103
9104 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9105
9106 /* make sure child objects are unable to modify the settings while we are
9107 * saving them */
9108 ensureNoStateDependencies();
9109
9110 AssertReturn(!isSnapshotMachine(),
9111 E_FAIL);
9112
9113 HRESULT rc = S_OK;
9114 bool fNeedsWrite = false;
9115
9116 /* First, prepare to save settings. It will care about renaming the
9117 * settings directory and file if the machine name was changed and about
9118 * creating a new settings file if this is a new machine. */
9119 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9120 if (FAILED(rc)) return rc;
9121
9122 // keep a pointer to the current settings structures
9123 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9124 settings::MachineConfigFile *pNewConfig = NULL;
9125
9126 try
9127 {
9128 // make a fresh one to have everyone write stuff into
9129 pNewConfig = new settings::MachineConfigFile(NULL);
9130 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9131
9132 // now go and copy all the settings data from COM to the settings structures
9133 // (this calles saveSettings() on all the COM objects in the machine)
9134 copyMachineDataToSettings(*pNewConfig);
9135
9136 if (aFlags & SaveS_ResetCurStateModified)
9137 {
9138 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9139 mData->mCurrentStateModified = FALSE;
9140 fNeedsWrite = true; // always, no need to compare
9141 }
9142 else if (aFlags & SaveS_Force)
9143 {
9144 fNeedsWrite = true; // always, no need to compare
9145 }
9146 else
9147 {
9148 if (!mData->mCurrentStateModified)
9149 {
9150 // do a deep compare of the settings that we just saved with the settings
9151 // previously stored in the config file; this invokes MachineConfigFile::operator==
9152 // which does a deep compare of all the settings, which is expensive but less expensive
9153 // than writing out XML in vain
9154 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9155
9156 // could still be modified if any settings changed
9157 mData->mCurrentStateModified = fAnySettingsChanged;
9158
9159 fNeedsWrite = fAnySettingsChanged;
9160 }
9161 else
9162 fNeedsWrite = true;
9163 }
9164
9165 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9166
9167 if (fNeedsWrite)
9168 // now spit it all out!
9169 pNewConfig->write(mData->m_strConfigFileFull);
9170
9171 mData->pMachineConfigFile = pNewConfig;
9172 delete pOldConfig;
9173 commit();
9174
9175 // after saving settings, we are no longer different from the XML on disk
9176 mData->flModifications = 0;
9177 }
9178 catch (HRESULT err)
9179 {
9180 // we assume that error info is set by the thrower
9181 rc = err;
9182
9183 // restore old config
9184 delete pNewConfig;
9185 mData->pMachineConfigFile = pOldConfig;
9186 }
9187 catch (...)
9188 {
9189 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9190 }
9191
9192 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9193 {
9194 /* Fire the data change event, even on failure (since we've already
9195 * committed all data). This is done only for SessionMachines because
9196 * mutable Machine instances are always not registered (i.e. private
9197 * to the client process that creates them) and thus don't need to
9198 * inform callbacks. */
9199 if (isSessionMachine())
9200 mParent->onMachineDataChange(mData->mUuid);
9201 }
9202
9203 LogFlowThisFunc(("rc=%08X\n", rc));
9204 LogFlowThisFuncLeave();
9205 return rc;
9206}
9207
9208/**
9209 * Implementation for saving the machine settings into the given
9210 * settings::MachineConfigFile instance. This copies machine extradata
9211 * from the previous machine config file in the instance data, if any.
9212 *
9213 * This gets called from two locations:
9214 *
9215 * -- Machine::saveSettings(), during the regular XML writing;
9216 *
9217 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9218 * exported to OVF and we write the VirtualBox proprietary XML
9219 * into a <vbox:Machine> tag.
9220 *
9221 * This routine fills all the fields in there, including snapshots, *except*
9222 * for the following:
9223 *
9224 * -- fCurrentStateModified. There is some special logic associated with that.
9225 *
9226 * The caller can then call MachineConfigFile::write() or do something else
9227 * with it.
9228 *
9229 * Caller must hold the machine lock!
9230 *
9231 * This throws XML errors and HRESULT, so the caller must have a catch block!
9232 */
9233void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
9234{
9235 // deep copy extradata
9236 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9237
9238 config.uuid = mData->mUuid;
9239
9240 // copy name, description, OS type, teleport, UTC etc.
9241 config.machineUserData = mUserData->s;
9242
9243 if ( mData->mMachineState == MachineState_Saved
9244 || mData->mMachineState == MachineState_Restoring
9245 // when deleting a snapshot we may or may not have a saved state in the current state,
9246 // so let's not assert here please
9247 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9248 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9249 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9250 && (!mSSData->strStateFilePath.isEmpty())
9251 )
9252 )
9253 {
9254 Assert(!mSSData->strStateFilePath.isEmpty());
9255 /* try to make the file name relative to the settings file dir */
9256 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9257 }
9258 else
9259 {
9260 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9261 config.strStateFile.setNull();
9262 }
9263
9264 if (mData->mCurrentSnapshot)
9265 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
9266 else
9267 config.uuidCurrentSnapshot.clear();
9268
9269 config.timeLastStateChange = mData->mLastStateChange;
9270 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9271 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9272
9273 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9274 if (FAILED(rc)) throw rc;
9275
9276 rc = saveStorageControllers(config.storageMachine);
9277 if (FAILED(rc)) throw rc;
9278
9279 // save machine's media registry if this is VirtualBox 4.0 or later
9280 if (config.canHaveOwnMediaRegistry())
9281 {
9282 // determine machine folder
9283 Utf8Str strMachineFolder = getSettingsFileFull();
9284 strMachineFolder.stripFilename();
9285 mParent->saveMediaRegistry(config.mediaRegistry,
9286 getId(), // only media with registry ID == machine UUID
9287 strMachineFolder);
9288 // this throws HRESULT
9289 }
9290
9291 // save snapshots
9292 rc = saveAllSnapshots(config);
9293 if (FAILED(rc)) throw rc;
9294}
9295
9296/**
9297 * Saves all snapshots of the machine into the given machine config file. Called
9298 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9299 * @param config
9300 * @return
9301 */
9302HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
9303{
9304 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9305
9306 HRESULT rc = S_OK;
9307
9308 try
9309 {
9310 config.llFirstSnapshot.clear();
9311
9312 if (mData->mFirstSnapshot)
9313 {
9314 settings::Snapshot snapNew;
9315 config.llFirstSnapshot.push_back(snapNew);
9316
9317 // get reference to the fresh copy of the snapshot on the list and
9318 // work on that copy directly to avoid excessive copying later
9319 settings::Snapshot &snap = config.llFirstSnapshot.front();
9320
9321 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
9322 if (FAILED(rc)) throw rc;
9323 }
9324
9325// if (mType == IsSessionMachine)
9326// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9327
9328 }
9329 catch (HRESULT err)
9330 {
9331 /* we assume that error info is set by the thrower */
9332 rc = err;
9333 }
9334 catch (...)
9335 {
9336 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9337 }
9338
9339 return rc;
9340}
9341
9342/**
9343 * Saves the VM hardware configuration. It is assumed that the
9344 * given node is empty.
9345 *
9346 * @param data Reference to the settings object for the hardware config.
9347 * @param pDbg Pointer to the settings object for the debugging config
9348 * which happens to live in mHWData.
9349 * @param pAutostart Pointer to the settings object for the autostart config
9350 * which happens to live in mHWData.
9351 */
9352HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9353 settings::Autostart *pAutostart)
9354{
9355 HRESULT rc = S_OK;
9356
9357 try
9358 {
9359 /* The hardware version attribute (optional).
9360 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9361 if ( mHWData->mHWVersion == "1"
9362 && mSSData->strStateFilePath.isEmpty()
9363 )
9364 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. */
9365
9366 data.strVersion = mHWData->mHWVersion;
9367 data.uuid = mHWData->mHardwareUUID;
9368
9369 // CPU
9370 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9371 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
9372 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9373 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9374 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9375 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9376 data.fPAE = !!mHWData->mPAEEnabled;
9377 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9378
9379 /* Standard and Extended CPUID leafs. */
9380 data.llCpuIdLeafs.clear();
9381 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
9382 {
9383 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9384 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9385 }
9386 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
9387 {
9388 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9389 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9390 }
9391
9392 data.cCPUs = mHWData->mCPUCount;
9393 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9394 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9395
9396 data.llCpus.clear();
9397 if (data.fCpuHotPlug)
9398 {
9399 for (unsigned idx = 0; idx < data.cCPUs; idx++)
9400 {
9401 if (mHWData->mCPUAttached[idx])
9402 {
9403 settings::Cpu cpu;
9404 cpu.ulId = idx;
9405 data.llCpus.push_back(cpu);
9406 }
9407 }
9408 }
9409
9410 // memory
9411 data.ulMemorySizeMB = mHWData->mMemorySize;
9412 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9413
9414 // firmware
9415 data.firmwareType = mHWData->mFirmwareType;
9416
9417 // HID
9418 data.pointingHidType = mHWData->mPointingHidType;
9419 data.keyboardHidType = mHWData->mKeyboardHidType;
9420
9421 // chipset
9422 data.chipsetType = mHWData->mChipsetType;
9423
9424 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9425
9426 // HPET
9427 data.fHpetEnabled = !!mHWData->mHpetEnabled;
9428
9429 // boot order
9430 data.mapBootOrder.clear();
9431 for (size_t i = 0;
9432 i < RT_ELEMENTS(mHWData->mBootOrder);
9433 ++i)
9434 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9435
9436 // display
9437 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9438 data.cMonitors = mHWData->mMonitorCount;
9439 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9440 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9441
9442 /* VRDEServer settings (optional) */
9443 rc = mVRDEServer->saveSettings(data.vrdeSettings);
9444 if (FAILED(rc)) throw rc;
9445
9446 /* BIOS (required) */
9447 rc = mBIOSSettings->saveSettings(data.biosSettings);
9448 if (FAILED(rc)) throw rc;
9449
9450 /* USB Controller (required) */
9451 rc = mUSBController->saveSettings(data.usbController);
9452 if (FAILED(rc)) throw rc;
9453
9454 /* Network adapters (required) */
9455 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
9456 data.llNetworkAdapters.clear();
9457 /* Write out only the nominal number of network adapters for this
9458 * chipset type. Since Machine::commit() hasn't been called there
9459 * may be extra NIC settings in the vector. */
9460 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
9461 {
9462 settings::NetworkAdapter nic;
9463 nic.ulSlot = slot;
9464 /* paranoia check... must not be NULL, but must not crash either. */
9465 if (mNetworkAdapters[slot])
9466 {
9467 rc = mNetworkAdapters[slot]->saveSettings(nic);
9468 if (FAILED(rc)) throw rc;
9469
9470 data.llNetworkAdapters.push_back(nic);
9471 }
9472 }
9473
9474 /* Serial ports */
9475 data.llSerialPorts.clear();
9476 for (ULONG slot = 0;
9477 slot < RT_ELEMENTS(mSerialPorts);
9478 ++slot)
9479 {
9480 settings::SerialPort s;
9481 s.ulSlot = slot;
9482 rc = mSerialPorts[slot]->saveSettings(s);
9483 if (FAILED(rc)) return rc;
9484
9485 data.llSerialPorts.push_back(s);
9486 }
9487
9488 /* Parallel ports */
9489 data.llParallelPorts.clear();
9490 for (ULONG slot = 0;
9491 slot < RT_ELEMENTS(mParallelPorts);
9492 ++slot)
9493 {
9494 settings::ParallelPort p;
9495 p.ulSlot = slot;
9496 rc = mParallelPorts[slot]->saveSettings(p);
9497 if (FAILED(rc)) return rc;
9498
9499 data.llParallelPorts.push_back(p);
9500 }
9501
9502 /* Audio adapter */
9503 rc = mAudioAdapter->saveSettings(data.audioAdapter);
9504 if (FAILED(rc)) return rc;
9505
9506 /* Shared folders */
9507 data.llSharedFolders.clear();
9508 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
9509 it != mHWData->mSharedFolders.end();
9510 ++it)
9511 {
9512 SharedFolder *pSF = *it;
9513 AutoCaller sfCaller(pSF);
9514 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
9515 settings::SharedFolder sf;
9516 sf.strName = pSF->getName();
9517 sf.strHostPath = pSF->getHostPath();
9518 sf.fWritable = !!pSF->isWritable();
9519 sf.fAutoMount = !!pSF->isAutoMounted();
9520
9521 data.llSharedFolders.push_back(sf);
9522 }
9523
9524 // clipboard
9525 data.clipboardMode = mHWData->mClipboardMode;
9526
9527 /* Guest */
9528 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
9529
9530 // IO settings
9531 data.ioSettings.fIoCacheEnabled = !!mHWData->mIoCacheEnabled;
9532 data.ioSettings.ulIoCacheSize = mHWData->mIoCacheSize;
9533
9534 /* BandwidthControl (required) */
9535 rc = mBandwidthControl->saveSettings(data.ioSettings);
9536 if (FAILED(rc)) throw rc;
9537
9538 /* Host PCI devices */
9539 for (HWData::PciDeviceAssignmentList::const_iterator it = mHWData->mPciDeviceAssignments.begin();
9540 it != mHWData->mPciDeviceAssignments.end();
9541 ++it)
9542 {
9543 ComObjPtr<PciDeviceAttachment> pda = *it;
9544 settings::HostPciDeviceAttachment hpda;
9545
9546 rc = pda->saveSettings(hpda);
9547 if (FAILED(rc)) throw rc;
9548
9549 data.pciAttachments.push_back(hpda);
9550 }
9551
9552
9553 // guest properties
9554 data.llGuestProperties.clear();
9555#ifdef VBOX_WITH_GUEST_PROPS
9556 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
9557 it != mHWData->mGuestProperties.end();
9558 ++it)
9559 {
9560 HWData::GuestProperty property = *it;
9561
9562 /* Remove transient guest properties at shutdown unless we
9563 * are saving state */
9564 if ( ( mData->mMachineState == MachineState_PoweredOff
9565 || mData->mMachineState == MachineState_Aborted
9566 || mData->mMachineState == MachineState_Teleported)
9567 && ( property.mFlags & guestProp::TRANSIENT
9568 || property.mFlags & guestProp::TRANSRESET))
9569 continue;
9570 settings::GuestProperty prop;
9571 prop.strName = property.strName;
9572 prop.strValue = property.strValue;
9573 prop.timestamp = property.mTimestamp;
9574 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
9575 guestProp::writeFlags(property.mFlags, szFlags);
9576 prop.strFlags = szFlags;
9577
9578 data.llGuestProperties.push_back(prop);
9579 }
9580
9581 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
9582 /* I presume this doesn't require a backup(). */
9583 mData->mGuestPropertiesModified = FALSE;
9584#endif /* VBOX_WITH_GUEST_PROPS defined */
9585
9586 *pDbg = mHWData->mDebugging;
9587 *pAutostart = mHWData->mAutostart;
9588 }
9589 catch(std::bad_alloc &)
9590 {
9591 return E_OUTOFMEMORY;
9592 }
9593
9594 AssertComRC(rc);
9595 return rc;
9596}
9597
9598/**
9599 * Saves the storage controller configuration.
9600 *
9601 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
9602 */
9603HRESULT Machine::saveStorageControllers(settings::Storage &data)
9604{
9605 data.llStorageControllers.clear();
9606
9607 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9608 it != mStorageControllers->end();
9609 ++it)
9610 {
9611 HRESULT rc;
9612 ComObjPtr<StorageController> pCtl = *it;
9613
9614 settings::StorageController ctl;
9615 ctl.strName = pCtl->getName();
9616 ctl.controllerType = pCtl->getControllerType();
9617 ctl.storageBus = pCtl->getStorageBus();
9618 ctl.ulInstance = pCtl->getInstance();
9619 ctl.fBootable = pCtl->getBootable();
9620
9621 /* Save the port count. */
9622 ULONG portCount;
9623 rc = pCtl->COMGETTER(PortCount)(&portCount);
9624 ComAssertComRCRet(rc, rc);
9625 ctl.ulPortCount = portCount;
9626
9627 /* Save fUseHostIOCache */
9628 BOOL fUseHostIOCache;
9629 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
9630 ComAssertComRCRet(rc, rc);
9631 ctl.fUseHostIOCache = !!fUseHostIOCache;
9632
9633 /* Save IDE emulation settings. */
9634 if (ctl.controllerType == StorageControllerType_IntelAhci)
9635 {
9636 if ( (FAILED(rc = pCtl->GetIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
9637 || (FAILED(rc = pCtl->GetIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
9638 || (FAILED(rc = pCtl->GetIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
9639 || (FAILED(rc = pCtl->GetIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
9640 )
9641 ComAssertComRCRet(rc, rc);
9642 }
9643
9644 /* save the devices now. */
9645 rc = saveStorageDevices(pCtl, ctl);
9646 ComAssertComRCRet(rc, rc);
9647
9648 data.llStorageControllers.push_back(ctl);
9649 }
9650
9651 return S_OK;
9652}
9653
9654/**
9655 * Saves the hard disk configuration.
9656 */
9657HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
9658 settings::StorageController &data)
9659{
9660 MediaData::AttachmentList atts;
9661
9662 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
9663 if (FAILED(rc)) return rc;
9664
9665 data.llAttachedDevices.clear();
9666 for (MediaData::AttachmentList::const_iterator it = atts.begin();
9667 it != atts.end();
9668 ++it)
9669 {
9670 settings::AttachedDevice dev;
9671
9672 MediumAttachment *pAttach = *it;
9673 Medium *pMedium = pAttach->getMedium();
9674
9675 dev.deviceType = pAttach->getType();
9676 dev.lPort = pAttach->getPort();
9677 dev.lDevice = pAttach->getDevice();
9678 if (pMedium)
9679 {
9680 if (pMedium->isHostDrive())
9681 dev.strHostDriveSrc = pMedium->getLocationFull();
9682 else
9683 dev.uuid = pMedium->getId();
9684 dev.fPassThrough = pAttach->getPassthrough();
9685 dev.fTempEject = pAttach->getTempEject();
9686 dev.fDiscard = pAttach->getDiscard();
9687 }
9688
9689 dev.strBwGroup = pAttach->getBandwidthGroup();
9690
9691 data.llAttachedDevices.push_back(dev);
9692 }
9693
9694 return S_OK;
9695}
9696
9697/**
9698 * Saves machine state settings as defined by aFlags
9699 * (SaveSTS_* values).
9700 *
9701 * @param aFlags Combination of SaveSTS_* flags.
9702 *
9703 * @note Locks objects for writing.
9704 */
9705HRESULT Machine::saveStateSettings(int aFlags)
9706{
9707 if (aFlags == 0)
9708 return S_OK;
9709
9710 AutoCaller autoCaller(this);
9711 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9712
9713 /* This object's write lock is also necessary to serialize file access
9714 * (prevent concurrent reads and writes) */
9715 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9716
9717 HRESULT rc = S_OK;
9718
9719 Assert(mData->pMachineConfigFile);
9720
9721 try
9722 {
9723 if (aFlags & SaveSTS_CurStateModified)
9724 mData->pMachineConfigFile->fCurrentStateModified = true;
9725
9726 if (aFlags & SaveSTS_StateFilePath)
9727 {
9728 if (!mSSData->strStateFilePath.isEmpty())
9729 /* try to make the file name relative to the settings file dir */
9730 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
9731 else
9732 mData->pMachineConfigFile->strStateFile.setNull();
9733 }
9734
9735 if (aFlags & SaveSTS_StateTimeStamp)
9736 {
9737 Assert( mData->mMachineState != MachineState_Aborted
9738 || mSSData->strStateFilePath.isEmpty());
9739
9740 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
9741
9742 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
9743//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
9744 }
9745
9746 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
9747 }
9748 catch (...)
9749 {
9750 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9751 }
9752
9753 return rc;
9754}
9755
9756/**
9757 * Ensures that the given medium is added to a media registry. If this machine
9758 * was created with 4.0 or later, then the machine registry is used. Otherwise
9759 * the global VirtualBox media registry is used.
9760 *
9761 * Caller must NOT hold machine lock, media tree or any medium locks!
9762 *
9763 * @param pMedium
9764 */
9765void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
9766{
9767 /* Paranoia checks: do not hold machine or media tree locks. */
9768 AssertReturnVoid(!isWriteLockOnCurrentThread());
9769 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
9770
9771 ComObjPtr<Medium> pBase;
9772 {
9773 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9774 pBase = pMedium->getBase();
9775 }
9776
9777 /* Paranoia checks: do not hold medium locks. */
9778 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
9779 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
9780
9781 // decide which medium registry to use now that the medium is attached:
9782 Guid uuid;
9783 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
9784 // machine XML is VirtualBox 4.0 or higher:
9785 uuid = getId(); // machine UUID
9786 else
9787 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
9788
9789 if (pMedium->addRegistry(uuid, false /* fRecurse */))
9790 mParent->markRegistryModified(uuid);
9791
9792 /* For more complex hard disk structures it can happen that the base
9793 * medium isn't yet associated with any medium registry. Do that now. */
9794 if (pMedium != pBase)
9795 {
9796 if (pBase->addRegistry(uuid, true /* fRecurse */))
9797 mParent->markRegistryModified(uuid);
9798 }
9799}
9800
9801/**
9802 * Creates differencing hard disks for all normal hard disks attached to this
9803 * machine and a new set of attachments to refer to created disks.
9804 *
9805 * Used when taking a snapshot or when deleting the current state. Gets called
9806 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
9807 *
9808 * This method assumes that mMediaData contains the original hard disk attachments
9809 * it needs to create diffs for. On success, these attachments will be replaced
9810 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
9811 * called to delete created diffs which will also rollback mMediaData and restore
9812 * whatever was backed up before calling this method.
9813 *
9814 * Attachments with non-normal hard disks are left as is.
9815 *
9816 * If @a aOnline is @c false then the original hard disks that require implicit
9817 * diffs will be locked for reading. Otherwise it is assumed that they are
9818 * already locked for writing (when the VM was started). Note that in the latter
9819 * case it is responsibility of the caller to lock the newly created diffs for
9820 * writing if this method succeeds.
9821 *
9822 * @param aProgress Progress object to run (must contain at least as
9823 * many operations left as the number of hard disks
9824 * attached).
9825 * @param aOnline Whether the VM was online prior to this operation.
9826 *
9827 * @note The progress object is not marked as completed, neither on success nor
9828 * on failure. This is a responsibility of the caller.
9829 *
9830 * @note Locks this object for writing.
9831 */
9832HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
9833 ULONG aWeight,
9834 bool aOnline)
9835{
9836 LogFlowThisFunc(("aOnline=%d\n", aOnline));
9837
9838 AutoCaller autoCaller(this);
9839 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9840
9841 AutoMultiWriteLock2 alock(this->lockHandle(),
9842 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9843
9844 /* must be in a protective state because we release the lock below */
9845 AssertReturn( mData->mMachineState == MachineState_Saving
9846 || mData->mMachineState == MachineState_LiveSnapshotting
9847 || mData->mMachineState == MachineState_RestoringSnapshot
9848 || mData->mMachineState == MachineState_DeletingSnapshot
9849 , E_FAIL);
9850
9851 HRESULT rc = S_OK;
9852
9853 MediumLockListMap lockedMediaOffline;
9854 MediumLockListMap *lockedMediaMap;
9855 if (aOnline)
9856 lockedMediaMap = &mData->mSession.mLockedMedia;
9857 else
9858 lockedMediaMap = &lockedMediaOffline;
9859
9860 try
9861 {
9862 if (!aOnline)
9863 {
9864 /* lock all attached hard disks early to detect "in use"
9865 * situations before creating actual diffs */
9866 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
9867 it != mMediaData->mAttachments.end();
9868 ++it)
9869 {
9870 MediumAttachment* pAtt = *it;
9871 if (pAtt->getType() == DeviceType_HardDisk)
9872 {
9873 Medium* pMedium = pAtt->getMedium();
9874 Assert(pMedium);
9875
9876 MediumLockList *pMediumLockList(new MediumLockList());
9877 alock.release();
9878 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
9879 false /* fMediumLockWrite */,
9880 NULL,
9881 *pMediumLockList);
9882 alock.acquire();
9883 if (FAILED(rc))
9884 {
9885 delete pMediumLockList;
9886 throw rc;
9887 }
9888 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
9889 if (FAILED(rc))
9890 {
9891 throw setError(rc,
9892 tr("Collecting locking information for all attached media failed"));
9893 }
9894 }
9895 }
9896
9897 /* Now lock all media. If this fails, nothing is locked. */
9898 alock.release();
9899 rc = lockedMediaMap->Lock();
9900 alock.acquire();
9901 if (FAILED(rc))
9902 {
9903 throw setError(rc,
9904 tr("Locking of attached media failed"));
9905 }
9906 }
9907
9908 /* remember the current list (note that we don't use backup() since
9909 * mMediaData may be already backed up) */
9910 MediaData::AttachmentList atts = mMediaData->mAttachments;
9911
9912 /* start from scratch */
9913 mMediaData->mAttachments.clear();
9914
9915 /* go through remembered attachments and create diffs for normal hard
9916 * disks and attach them */
9917 for (MediaData::AttachmentList::const_iterator it = atts.begin();
9918 it != atts.end();
9919 ++it)
9920 {
9921 MediumAttachment* pAtt = *it;
9922
9923 DeviceType_T devType = pAtt->getType();
9924 Medium* pMedium = pAtt->getMedium();
9925
9926 if ( devType != DeviceType_HardDisk
9927 || pMedium == NULL
9928 || pMedium->getType() != MediumType_Normal)
9929 {
9930 /* copy the attachment as is */
9931
9932 /** @todo the progress object created in Console::TakeSnaphot
9933 * only expects operations for hard disks. Later other
9934 * device types need to show up in the progress as well. */
9935 if (devType == DeviceType_HardDisk)
9936 {
9937 if (pMedium == NULL)
9938 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
9939 aWeight); // weight
9940 else
9941 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
9942 pMedium->getBase()->getName().c_str()).raw(),
9943 aWeight); // weight
9944 }
9945
9946 mMediaData->mAttachments.push_back(pAtt);
9947 continue;
9948 }
9949
9950 /* need a diff */
9951 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
9952 pMedium->getBase()->getName().c_str()).raw(),
9953 aWeight); // weight
9954
9955 Utf8Str strFullSnapshotFolder;
9956 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
9957
9958 ComObjPtr<Medium> diff;
9959 diff.createObject();
9960 // store the diff in the same registry as the parent
9961 // (this cannot fail here because we can't create implicit diffs for
9962 // unregistered images)
9963 Guid uuidRegistryParent;
9964 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
9965 Assert(fInRegistry); NOREF(fInRegistry);
9966 rc = diff->init(mParent,
9967 pMedium->getPreferredDiffFormat(),
9968 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
9969 uuidRegistryParent);
9970 if (FAILED(rc)) throw rc;
9971
9972 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
9973 * the push_back? Looks like we're going to release medium with the
9974 * wrong kind of lock (general issue with if we fail anywhere at all)
9975 * and an orphaned VDI in the snapshots folder. */
9976
9977 /* update the appropriate lock list */
9978 MediumLockList *pMediumLockList;
9979 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
9980 AssertComRCThrowRC(rc);
9981 if (aOnline)
9982 {
9983 alock.release();
9984 rc = pMediumLockList->Update(pMedium, false);
9985 alock.acquire();
9986 AssertComRCThrowRC(rc);
9987 }
9988
9989 /* release the locks before the potentially lengthy operation */
9990 alock.release();
9991 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
9992 pMediumLockList,
9993 NULL /* aProgress */,
9994 true /* aWait */);
9995 alock.acquire();
9996 if (FAILED(rc)) throw rc;
9997
9998 rc = lockedMediaMap->Unlock();
9999 AssertComRCThrowRC(rc);
10000 alock.release();
10001 rc = pMediumLockList->Append(diff, true);
10002 alock.acquire();
10003 AssertComRCThrowRC(rc);
10004 alock.release();
10005 rc = lockedMediaMap->Lock();
10006 alock.acquire();
10007 AssertComRCThrowRC(rc);
10008
10009 rc = diff->addBackReference(mData->mUuid);
10010 AssertComRCThrowRC(rc);
10011
10012 /* add a new attachment */
10013 ComObjPtr<MediumAttachment> attachment;
10014 attachment.createObject();
10015 rc = attachment->init(this,
10016 diff,
10017 pAtt->getControllerName(),
10018 pAtt->getPort(),
10019 pAtt->getDevice(),
10020 DeviceType_HardDisk,
10021 true /* aImplicit */,
10022 false /* aPassthrough */,
10023 false /* aTempEject */,
10024 pAtt->getNonRotational(),
10025 pAtt->getDiscard(),
10026 pAtt->getBandwidthGroup());
10027 if (FAILED(rc)) throw rc;
10028
10029 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10030 AssertComRCThrowRC(rc);
10031 mMediaData->mAttachments.push_back(attachment);
10032 }
10033 }
10034 catch (HRESULT aRC) { rc = aRC; }
10035
10036 /* unlock all hard disks we locked */
10037 if (!aOnline)
10038 {
10039 ErrorInfoKeeper eik;
10040
10041 HRESULT rc1 = lockedMediaMap->Clear();
10042 AssertComRC(rc1);
10043 }
10044
10045 if (FAILED(rc))
10046 {
10047 MultiResult mrc = rc;
10048
10049 alock.release();
10050 mrc = deleteImplicitDiffs();
10051 }
10052
10053 return rc;
10054}
10055
10056/**
10057 * Deletes implicit differencing hard disks created either by
10058 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10059 *
10060 * Note that to delete hard disks created by #AttachDevice() this method is
10061 * called from #fixupMedia() when the changes are rolled back.
10062 *
10063 * @note Locks this object for writing.
10064 */
10065HRESULT Machine::deleteImplicitDiffs()
10066{
10067 AutoCaller autoCaller(this);
10068 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10069
10070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10071 LogFlowThisFuncEnter();
10072
10073 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10074
10075 HRESULT rc = S_OK;
10076
10077 MediaData::AttachmentList implicitAtts;
10078
10079 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10080
10081 /* enumerate new attachments */
10082 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10083 it != mMediaData->mAttachments.end();
10084 ++it)
10085 {
10086 ComObjPtr<Medium> hd = (*it)->getMedium();
10087 if (hd.isNull())
10088 continue;
10089
10090 if ((*it)->isImplicit())
10091 {
10092 /* deassociate and mark for deletion */
10093 LogFlowThisFunc(("Detaching '%s', pending deletion\n", (*it)->getLogName()));
10094 rc = hd->removeBackReference(mData->mUuid);
10095 AssertComRC(rc);
10096 implicitAtts.push_back(*it);
10097 continue;
10098 }
10099
10100 /* was this hard disk attached before? */
10101 if (!findAttachment(oldAtts, hd))
10102 {
10103 /* no: de-associate */
10104 LogFlowThisFunc(("Detaching '%s', no deletion\n", (*it)->getLogName()));
10105 rc = hd->removeBackReference(mData->mUuid);
10106 AssertComRC(rc);
10107 continue;
10108 }
10109 LogFlowThisFunc(("Not detaching '%s'\n", (*it)->getLogName()));
10110 }
10111
10112 /* rollback hard disk changes */
10113 mMediaData.rollback();
10114
10115 MultiResult mrc(S_OK);
10116
10117 /* delete unused implicit diffs */
10118 if (implicitAtts.size() != 0)
10119 {
10120 /* will release the lock before the potentially lengthy
10121 * operation, so protect with the special state (unless already
10122 * protected) */
10123 MachineState_T oldState = mData->mMachineState;
10124 if ( oldState != MachineState_Saving
10125 && oldState != MachineState_LiveSnapshotting
10126 && oldState != MachineState_RestoringSnapshot
10127 && oldState != MachineState_DeletingSnapshot
10128 && oldState != MachineState_DeletingSnapshotOnline
10129 && oldState != MachineState_DeletingSnapshotPaused
10130 )
10131 setMachineState(MachineState_SettingUp);
10132
10133 alock.release();
10134
10135 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
10136 it != implicitAtts.end();
10137 ++it)
10138 {
10139 LogFlowThisFunc(("Deleting '%s'\n", (*it)->getLogName()));
10140 ComObjPtr<Medium> hd = (*it)->getMedium();
10141
10142 rc = hd->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10143 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, (*it)->getLogName(), hd->getLocationFull().c_str() ));
10144 mrc = rc;
10145 }
10146
10147 alock.acquire();
10148
10149 if (mData->mMachineState == MachineState_SettingUp)
10150 setMachineState(oldState);
10151 }
10152
10153 return mrc;
10154}
10155
10156/**
10157 * Looks through the given list of media attachments for one with the given parameters
10158 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10159 * can be searched as well if needed.
10160 *
10161 * @param list
10162 * @param aControllerName
10163 * @param aControllerPort
10164 * @param aDevice
10165 * @return
10166 */
10167MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10168 IN_BSTR aControllerName,
10169 LONG aControllerPort,
10170 LONG aDevice)
10171{
10172 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10173 it != ll.end();
10174 ++it)
10175 {
10176 MediumAttachment *pAttach = *it;
10177 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
10178 return pAttach;
10179 }
10180
10181 return NULL;
10182}
10183
10184/**
10185 * Looks through the given list of media attachments for one with the given parameters
10186 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10187 * can be searched as well if needed.
10188 *
10189 * @param list
10190 * @param aControllerName
10191 * @param aControllerPort
10192 * @param aDevice
10193 * @return
10194 */
10195MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10196 ComObjPtr<Medium> pMedium)
10197{
10198 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10199 it != ll.end();
10200 ++it)
10201 {
10202 MediumAttachment *pAttach = *it;
10203 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10204 if (pMediumThis == pMedium)
10205 return pAttach;
10206 }
10207
10208 return NULL;
10209}
10210
10211/**
10212 * Looks through the given list of media attachments for one with the given parameters
10213 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10214 * can be searched as well if needed.
10215 *
10216 * @param list
10217 * @param aControllerName
10218 * @param aControllerPort
10219 * @param aDevice
10220 * @return
10221 */
10222MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10223 Guid &id)
10224{
10225 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10226 it != ll.end();
10227 ++it)
10228 {
10229 MediumAttachment *pAttach = *it;
10230 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10231 if (pMediumThis->getId() == id)
10232 return pAttach;
10233 }
10234
10235 return NULL;
10236}
10237
10238/**
10239 * Main implementation for Machine::DetachDevice. This also gets called
10240 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10241 *
10242 * @param pAttach Medium attachment to detach.
10243 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10244 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
10245 * @return
10246 */
10247HRESULT Machine::detachDevice(MediumAttachment *pAttach,
10248 AutoWriteLock &writeLock,
10249 Snapshot *pSnapshot)
10250{
10251 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
10252 DeviceType_T mediumType = pAttach->getType();
10253
10254 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
10255
10256 if (pAttach->isImplicit())
10257 {
10258 /* attempt to implicitly delete the implicitly created diff */
10259
10260 /// @todo move the implicit flag from MediumAttachment to Medium
10261 /// and forbid any hard disk operation when it is implicit. Or maybe
10262 /// a special media state for it to make it even more simple.
10263
10264 Assert(mMediaData.isBackedUp());
10265
10266 /* will release the lock before the potentially lengthy operation, so
10267 * protect with the special state */
10268 MachineState_T oldState = mData->mMachineState;
10269 setMachineState(MachineState_SettingUp);
10270
10271 writeLock.release();
10272
10273 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
10274 true /*aWait*/);
10275
10276 writeLock.acquire();
10277
10278 setMachineState(oldState);
10279
10280 if (FAILED(rc)) return rc;
10281 }
10282
10283 setModified(IsModified_Storage);
10284 mMediaData.backup();
10285 mMediaData->mAttachments.remove(pAttach);
10286
10287 if (!oldmedium.isNull())
10288 {
10289 // if this is from a snapshot, do not defer detachment to commitMedia()
10290 if (pSnapshot)
10291 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
10292 // else if non-hard disk media, do not defer detachment to commitMedia() either
10293 else if (mediumType != DeviceType_HardDisk)
10294 oldmedium->removeBackReference(mData->mUuid);
10295 }
10296
10297 return S_OK;
10298}
10299
10300/**
10301 * Goes thru all media of the given list and
10302 *
10303 * 1) calls detachDevice() on each of them for this machine and
10304 * 2) adds all Medium objects found in the process to the given list,
10305 * depending on cleanupMode.
10306 *
10307 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10308 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10309 * media to the list.
10310 *
10311 * This gets called from Machine::Unregister, both for the actual Machine and
10312 * the SnapshotMachine objects that might be found in the snapshots.
10313 *
10314 * Requires caller and locking. The machine lock must be passed in because it
10315 * will be passed on to detachDevice which needs it for temporary unlocking.
10316 *
10317 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
10318 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
10319 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
10320 * otherwise no media get added.
10321 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
10322 * @return
10323 */
10324HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
10325 Snapshot *pSnapshot,
10326 CleanupMode_T cleanupMode,
10327 MediaList &llMedia)
10328{
10329 Assert(isWriteLockOnCurrentThread());
10330
10331 HRESULT rc;
10332
10333 // make a temporary list because detachDevice invalidates iterators into
10334 // mMediaData->mAttachments
10335 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
10336
10337 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
10338 it != llAttachments2.end();
10339 ++it)
10340 {
10341 ComObjPtr<MediumAttachment> &pAttach = *it;
10342 ComObjPtr<Medium> pMedium = pAttach->getMedium();
10343
10344 if (!pMedium.isNull())
10345 {
10346 AutoCaller mac(pMedium);
10347 if (FAILED(mac.rc())) return mac.rc();
10348 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
10349 DeviceType_T devType = pMedium->getDeviceType();
10350 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
10351 && devType == DeviceType_HardDisk)
10352 || (cleanupMode == CleanupMode_Full)
10353 )
10354 {
10355 llMedia.push_back(pMedium);
10356 ComObjPtr<Medium> pParent = pMedium->getParent();
10357 /*
10358 * Search for medias which are not attached to any machine, but
10359 * in the chain to an attached disk. Mediums are only consided
10360 * if they are:
10361 * - have only one child
10362 * - no references to any machines
10363 * - are of normal medium type
10364 */
10365 while (!pParent.isNull())
10366 {
10367 AutoCaller mac1(pParent);
10368 if (FAILED(mac1.rc())) return mac1.rc();
10369 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
10370 if (pParent->getChildren().size() == 1)
10371 {
10372 if ( pParent->getMachineBackRefCount() == 0
10373 && pParent->getType() == MediumType_Normal
10374 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
10375 llMedia.push_back(pParent);
10376 }else
10377 break;
10378 pParent = pParent->getParent();
10379 }
10380 }
10381 }
10382
10383 // real machine: then we need to use the proper method
10384 rc = detachDevice(pAttach, writeLock, pSnapshot);
10385
10386 if (FAILED(rc))
10387 return rc;
10388 }
10389
10390 return S_OK;
10391}
10392
10393/**
10394 * Perform deferred hard disk detachments.
10395 *
10396 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10397 * backed up).
10398 *
10399 * If @a aOnline is @c true then this method will also unlock the old hard disks
10400 * for which the new implicit diffs were created and will lock these new diffs for
10401 * writing.
10402 *
10403 * @param aOnline Whether the VM was online prior to this operation.
10404 *
10405 * @note Locks this object for writing!
10406 */
10407void Machine::commitMedia(bool aOnline /*= false*/)
10408{
10409 AutoCaller autoCaller(this);
10410 AssertComRCReturnVoid(autoCaller.rc());
10411
10412 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10413
10414 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
10415
10416 HRESULT rc = S_OK;
10417
10418 /* no attach/detach operations -- nothing to do */
10419 if (!mMediaData.isBackedUp())
10420 return;
10421
10422 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10423 bool fMediaNeedsLocking = false;
10424
10425 /* enumerate new attachments */
10426 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10427 it != mMediaData->mAttachments.end();
10428 ++it)
10429 {
10430 MediumAttachment *pAttach = *it;
10431
10432 pAttach->commit();
10433
10434 Medium* pMedium = pAttach->getMedium();
10435 bool fImplicit = pAttach->isImplicit();
10436
10437 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
10438 (pMedium) ? pMedium->getName().c_str() : "NULL",
10439 fImplicit));
10440
10441 /** @todo convert all this Machine-based voodoo to MediumAttachment
10442 * based commit logic. */
10443 if (fImplicit)
10444 {
10445 /* convert implicit attachment to normal */
10446 pAttach->setImplicit(false);
10447
10448 if ( aOnline
10449 && pMedium
10450 && pAttach->getType() == DeviceType_HardDisk
10451 )
10452 {
10453 ComObjPtr<Medium> parent = pMedium->getParent();
10454 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
10455
10456 /* update the appropriate lock list */
10457 MediumLockList *pMediumLockList;
10458 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10459 AssertComRC(rc);
10460 if (pMediumLockList)
10461 {
10462 /* unlock if there's a need to change the locking */
10463 if (!fMediaNeedsLocking)
10464 {
10465 rc = mData->mSession.mLockedMedia.Unlock();
10466 AssertComRC(rc);
10467 fMediaNeedsLocking = true;
10468 }
10469 rc = pMediumLockList->Update(parent, false);
10470 AssertComRC(rc);
10471 rc = pMediumLockList->Append(pMedium, true);
10472 AssertComRC(rc);
10473 }
10474 }
10475
10476 continue;
10477 }
10478
10479 if (pMedium)
10480 {
10481 /* was this medium attached before? */
10482 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
10483 oldIt != oldAtts.end();
10484 ++oldIt)
10485 {
10486 MediumAttachment *pOldAttach = *oldIt;
10487 if (pOldAttach->getMedium() == pMedium)
10488 {
10489 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
10490
10491 /* yes: remove from old to avoid de-association */
10492 oldAtts.erase(oldIt);
10493 break;
10494 }
10495 }
10496 }
10497 }
10498
10499 /* enumerate remaining old attachments and de-associate from the
10500 * current machine state */
10501 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
10502 it != oldAtts.end();
10503 ++it)
10504 {
10505 MediumAttachment *pAttach = *it;
10506 Medium* pMedium = pAttach->getMedium();
10507
10508 /* Detach only hard disks, since DVD/floppy media is detached
10509 * instantly in MountMedium. */
10510 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
10511 {
10512 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
10513
10514 /* now de-associate from the current machine state */
10515 rc = pMedium->removeBackReference(mData->mUuid);
10516 AssertComRC(rc);
10517
10518 if (aOnline)
10519 {
10520 /* unlock since medium is not used anymore */
10521 MediumLockList *pMediumLockList;
10522 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10523 AssertComRC(rc);
10524 if (pMediumLockList)
10525 {
10526 rc = mData->mSession.mLockedMedia.Remove(pAttach);
10527 AssertComRC(rc);
10528 }
10529 }
10530 }
10531 }
10532
10533 /* take media locks again so that the locking state is consistent */
10534 if (fMediaNeedsLocking)
10535 {
10536 Assert(aOnline);
10537 rc = mData->mSession.mLockedMedia.Lock();
10538 AssertComRC(rc);
10539 }
10540
10541 /* commit the hard disk changes */
10542 mMediaData.commit();
10543
10544 if (isSessionMachine())
10545 {
10546 /*
10547 * Update the parent machine to point to the new owner.
10548 * This is necessary because the stored parent will point to the
10549 * session machine otherwise and cause crashes or errors later
10550 * when the session machine gets invalid.
10551 */
10552 /** @todo Change the MediumAttachment class to behave like any other
10553 * class in this regard by creating peer MediumAttachment
10554 * objects for session machines and share the data with the peer
10555 * machine.
10556 */
10557 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10558 it != mMediaData->mAttachments.end();
10559 ++it)
10560 {
10561 (*it)->updateParentMachine(mPeer);
10562 }
10563
10564 /* attach new data to the primary machine and reshare it */
10565 mPeer->mMediaData.attach(mMediaData);
10566 }
10567
10568 return;
10569}
10570
10571/**
10572 * Perform deferred deletion of implicitly created diffs.
10573 *
10574 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10575 * backed up).
10576 *
10577 * @note Locks this object for writing!
10578 */
10579void Machine::rollbackMedia()
10580{
10581 AutoCaller autoCaller(this);
10582 AssertComRCReturnVoid (autoCaller.rc());
10583
10584 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10585
10586 LogFlowThisFunc(("Entering\n"));
10587
10588 HRESULT rc = S_OK;
10589
10590 /* no attach/detach operations -- nothing to do */
10591 if (!mMediaData.isBackedUp())
10592 return;
10593
10594 /* enumerate new attachments */
10595 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10596 it != mMediaData->mAttachments.end();
10597 ++it)
10598 {
10599 MediumAttachment *pAttach = *it;
10600 /* Fix up the backrefs for DVD/floppy media. */
10601 if (pAttach->getType() != DeviceType_HardDisk)
10602 {
10603 Medium* pMedium = pAttach->getMedium();
10604 if (pMedium)
10605 {
10606 rc = pMedium->removeBackReference(mData->mUuid);
10607 AssertComRC(rc);
10608 }
10609 }
10610
10611 (*it)->rollback();
10612
10613 pAttach = *it;
10614 /* Fix up the backrefs for DVD/floppy media. */
10615 if (pAttach->getType() != DeviceType_HardDisk)
10616 {
10617 Medium* pMedium = pAttach->getMedium();
10618 if (pMedium)
10619 {
10620 rc = pMedium->addBackReference(mData->mUuid);
10621 AssertComRC(rc);
10622 }
10623 }
10624 }
10625
10626 /** @todo convert all this Machine-based voodoo to MediumAttachment
10627 * based rollback logic. */
10628 deleteImplicitDiffs();
10629
10630 return;
10631}
10632
10633/**
10634 * Returns true if the settings file is located in the directory named exactly
10635 * as the machine; this means, among other things, that the machine directory
10636 * should be auto-renamed.
10637 *
10638 * @param aSettingsDir if not NULL, the full machine settings file directory
10639 * name will be assigned there.
10640 *
10641 * @note Doesn't lock anything.
10642 * @note Not thread safe (must be called from this object's lock).
10643 */
10644bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
10645{
10646 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
10647 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
10648 if (aSettingsDir)
10649 *aSettingsDir = strMachineDirName;
10650 strMachineDirName.stripPath(); // vmname
10651 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
10652 strConfigFileOnly.stripPath() // vmname.vbox
10653 .stripExt(); // vmname
10654
10655 AssertReturn(!strMachineDirName.isEmpty(), false);
10656 AssertReturn(!strConfigFileOnly.isEmpty(), false);
10657
10658 return strMachineDirName == strConfigFileOnly;
10659}
10660
10661/**
10662 * Discards all changes to machine settings.
10663 *
10664 * @param aNotify Whether to notify the direct session about changes or not.
10665 *
10666 * @note Locks objects for writing!
10667 */
10668void Machine::rollback(bool aNotify)
10669{
10670 AutoCaller autoCaller(this);
10671 AssertComRCReturn(autoCaller.rc(), (void)0);
10672
10673 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10674
10675 if (!mStorageControllers.isNull())
10676 {
10677 if (mStorageControllers.isBackedUp())
10678 {
10679 /* unitialize all new devices (absent in the backed up list). */
10680 StorageControllerList::const_iterator it = mStorageControllers->begin();
10681 StorageControllerList *backedList = mStorageControllers.backedUpData();
10682 while (it != mStorageControllers->end())
10683 {
10684 if ( std::find(backedList->begin(), backedList->end(), *it)
10685 == backedList->end()
10686 )
10687 {
10688 (*it)->uninit();
10689 }
10690 ++it;
10691 }
10692
10693 /* restore the list */
10694 mStorageControllers.rollback();
10695 }
10696
10697 /* rollback any changes to devices after restoring the list */
10698 if (mData->flModifications & IsModified_Storage)
10699 {
10700 StorageControllerList::const_iterator it = mStorageControllers->begin();
10701 while (it != mStorageControllers->end())
10702 {
10703 (*it)->rollback();
10704 ++it;
10705 }
10706 }
10707 }
10708
10709 mUserData.rollback();
10710
10711 mHWData.rollback();
10712
10713 if (mData->flModifications & IsModified_Storage)
10714 rollbackMedia();
10715
10716 if (mBIOSSettings)
10717 mBIOSSettings->rollback();
10718
10719 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
10720 mVRDEServer->rollback();
10721
10722 if (mAudioAdapter)
10723 mAudioAdapter->rollback();
10724
10725 if (mUSBController && (mData->flModifications & IsModified_USB))
10726 mUSBController->rollback();
10727
10728 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
10729 mBandwidthControl->rollback();
10730
10731 if (!mHWData.isNull())
10732 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
10733 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
10734 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
10735 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
10736
10737 if (mData->flModifications & IsModified_NetworkAdapters)
10738 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
10739 if ( mNetworkAdapters[slot]
10740 && mNetworkAdapters[slot]->isModified())
10741 {
10742 mNetworkAdapters[slot]->rollback();
10743 networkAdapters[slot] = mNetworkAdapters[slot];
10744 }
10745
10746 if (mData->flModifications & IsModified_SerialPorts)
10747 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
10748 if ( mSerialPorts[slot]
10749 && mSerialPorts[slot]->isModified())
10750 {
10751 mSerialPorts[slot]->rollback();
10752 serialPorts[slot] = mSerialPorts[slot];
10753 }
10754
10755 if (mData->flModifications & IsModified_ParallelPorts)
10756 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
10757 if ( mParallelPorts[slot]
10758 && mParallelPorts[slot]->isModified())
10759 {
10760 mParallelPorts[slot]->rollback();
10761 parallelPorts[slot] = mParallelPorts[slot];
10762 }
10763
10764 if (aNotify)
10765 {
10766 /* inform the direct session about changes */
10767
10768 ComObjPtr<Machine> that = this;
10769 uint32_t flModifications = mData->flModifications;
10770 alock.release();
10771
10772 if (flModifications & IsModified_SharedFolders)
10773 that->onSharedFolderChange();
10774
10775 if (flModifications & IsModified_VRDEServer)
10776 that->onVRDEServerChange(/* aRestart */ TRUE);
10777 if (flModifications & IsModified_USB)
10778 that->onUSBControllerChange();
10779
10780 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
10781 if (networkAdapters[slot])
10782 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
10783 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
10784 if (serialPorts[slot])
10785 that->onSerialPortChange(serialPorts[slot]);
10786 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
10787 if (parallelPorts[slot])
10788 that->onParallelPortChange(parallelPorts[slot]);
10789
10790 if (flModifications & IsModified_Storage)
10791 that->onStorageControllerChange();
10792
10793#if 0
10794 if (flModifications & IsModified_BandwidthControl)
10795 that->onBandwidthControlChange();
10796#endif
10797 }
10798}
10799
10800/**
10801 * Commits all the changes to machine settings.
10802 *
10803 * Note that this operation is supposed to never fail.
10804 *
10805 * @note Locks this object and children for writing.
10806 */
10807void Machine::commit()
10808{
10809 AutoCaller autoCaller(this);
10810 AssertComRCReturnVoid(autoCaller.rc());
10811
10812 AutoCaller peerCaller(mPeer);
10813 AssertComRCReturnVoid(peerCaller.rc());
10814
10815 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
10816
10817 /*
10818 * use safe commit to ensure Snapshot machines (that share mUserData)
10819 * will still refer to a valid memory location
10820 */
10821 mUserData.commitCopy();
10822
10823 mHWData.commit();
10824
10825 if (mMediaData.isBackedUp())
10826 commitMedia();
10827
10828 mBIOSSettings->commit();
10829 mVRDEServer->commit();
10830 mAudioAdapter->commit();
10831 mUSBController->commit();
10832 mBandwidthControl->commit();
10833
10834 /* Keep the original network adapter count until this point, so that
10835 * discarding a chipset type change will not lose settings. */
10836 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
10837 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
10838 mNetworkAdapters[slot]->commit();
10839 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
10840 mSerialPorts[slot]->commit();
10841 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
10842 mParallelPorts[slot]->commit();
10843
10844 bool commitStorageControllers = false;
10845
10846 if (mStorageControllers.isBackedUp())
10847 {
10848 mStorageControllers.commit();
10849
10850 if (mPeer)
10851 {
10852 AutoWriteLock peerlock(mPeer COMMA_LOCKVAL_SRC_POS);
10853
10854 /* Commit all changes to new controllers (this will reshare data with
10855 * peers for those who have peers) */
10856 StorageControllerList *newList = new StorageControllerList();
10857 StorageControllerList::const_iterator it = mStorageControllers->begin();
10858 while (it != mStorageControllers->end())
10859 {
10860 (*it)->commit();
10861
10862 /* look if this controller has a peer device */
10863 ComObjPtr<StorageController> peer = (*it)->getPeer();
10864 if (!peer)
10865 {
10866 /* no peer means the device is a newly created one;
10867 * create a peer owning data this device share it with */
10868 peer.createObject();
10869 peer->init(mPeer, *it, true /* aReshare */);
10870 }
10871 else
10872 {
10873 /* remove peer from the old list */
10874 mPeer->mStorageControllers->remove(peer);
10875 }
10876 /* and add it to the new list */
10877 newList->push_back(peer);
10878
10879 ++it;
10880 }
10881
10882 /* uninit old peer's controllers that are left */
10883 it = mPeer->mStorageControllers->begin();
10884 while (it != mPeer->mStorageControllers->end())
10885 {
10886 (*it)->uninit();
10887 ++it;
10888 }
10889
10890 /* attach new list of controllers to our peer */
10891 mPeer->mStorageControllers.attach(newList);
10892 }
10893 else
10894 {
10895 /* we have no peer (our parent is the newly created machine);
10896 * just commit changes to devices */
10897 commitStorageControllers = true;
10898 }
10899 }
10900 else
10901 {
10902 /* the list of controllers itself is not changed,
10903 * just commit changes to controllers themselves */
10904 commitStorageControllers = true;
10905 }
10906
10907 if (commitStorageControllers)
10908 {
10909 StorageControllerList::const_iterator it = mStorageControllers->begin();
10910 while (it != mStorageControllers->end())
10911 {
10912 (*it)->commit();
10913 ++it;
10914 }
10915 }
10916
10917 if (isSessionMachine())
10918 {
10919 /* attach new data to the primary machine and reshare it */
10920 mPeer->mUserData.attach(mUserData);
10921 mPeer->mHWData.attach(mHWData);
10922 /* mMediaData is reshared by fixupMedia */
10923 // mPeer->mMediaData.attach(mMediaData);
10924 Assert(mPeer->mMediaData.data() == mMediaData.data());
10925 }
10926}
10927
10928/**
10929 * Copies all the hardware data from the given machine.
10930 *
10931 * Currently, only called when the VM is being restored from a snapshot. In
10932 * particular, this implies that the VM is not running during this method's
10933 * call.
10934 *
10935 * @note This method must be called from under this object's lock.
10936 *
10937 * @note This method doesn't call #commit(), so all data remains backed up and
10938 * unsaved.
10939 */
10940void Machine::copyFrom(Machine *aThat)
10941{
10942 AssertReturnVoid(!isSnapshotMachine());
10943 AssertReturnVoid(aThat->isSnapshotMachine());
10944
10945 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
10946
10947 mHWData.assignCopy(aThat->mHWData);
10948
10949 // create copies of all shared folders (mHWData after attaching a copy
10950 // contains just references to original objects)
10951 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
10952 it != mHWData->mSharedFolders.end();
10953 ++it)
10954 {
10955 ComObjPtr<SharedFolder> folder;
10956 folder.createObject();
10957 HRESULT rc = folder->initCopy(getMachine(), *it);
10958 AssertComRC(rc);
10959 *it = folder;
10960 }
10961
10962 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
10963 mVRDEServer->copyFrom(aThat->mVRDEServer);
10964 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
10965 mUSBController->copyFrom(aThat->mUSBController);
10966 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
10967
10968 /* create private copies of all controllers */
10969 mStorageControllers.backup();
10970 mStorageControllers->clear();
10971 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
10972 it != aThat->mStorageControllers->end();
10973 ++it)
10974 {
10975 ComObjPtr<StorageController> ctrl;
10976 ctrl.createObject();
10977 ctrl->initCopy(this, *it);
10978 mStorageControllers->push_back(ctrl);
10979 }
10980
10981 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
10982 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
10983 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
10984 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
10985 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
10986 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
10987}
10988
10989/**
10990 * Returns whether the given storage controller is hotplug capable.
10991 *
10992 * @returns true if the controller supports hotplugging
10993 * false otherwise.
10994 * @param enmCtrlType The controller type to check for.
10995 */
10996bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
10997{
10998 switch (enmCtrlType)
10999 {
11000 case StorageControllerType_IntelAhci:
11001 return true;
11002 case StorageControllerType_LsiLogic:
11003 case StorageControllerType_LsiLogicSas:
11004 case StorageControllerType_BusLogic:
11005 case StorageControllerType_PIIX3:
11006 case StorageControllerType_PIIX4:
11007 case StorageControllerType_ICH6:
11008 case StorageControllerType_I82078:
11009 default:
11010 return false;
11011 }
11012}
11013
11014#ifdef VBOX_WITH_RESOURCE_USAGE_API
11015
11016void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11017{
11018 AssertReturnVoid(isWriteLockOnCurrentThread());
11019 AssertPtrReturnVoid(aCollector);
11020
11021 pm::CollectorHAL *hal = aCollector->getHAL();
11022 /* Create sub metrics */
11023 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11024 "Percentage of processor time spent in user mode by the VM process.");
11025 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11026 "Percentage of processor time spent in kernel mode by the VM process.");
11027 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11028 "Size of resident portion of VM process in memory.");
11029 /* Create and register base metrics */
11030 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11031 cpuLoadUser, cpuLoadKernel);
11032 aCollector->registerBaseMetric(cpuLoad);
11033 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11034 ramUsageUsed);
11035 aCollector->registerBaseMetric(ramUsage);
11036
11037 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11038 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11039 new pm::AggregateAvg()));
11040 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11041 new pm::AggregateMin()));
11042 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11043 new pm::AggregateMax()));
11044 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11045 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11046 new pm::AggregateAvg()));
11047 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11048 new pm::AggregateMin()));
11049 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11050 new pm::AggregateMax()));
11051
11052 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11053 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11054 new pm::AggregateAvg()));
11055 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11056 new pm::AggregateMin()));
11057 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11058 new pm::AggregateMax()));
11059
11060
11061 /* Guest metrics collector */
11062 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11063 aCollector->registerGuest(mCollectorGuest);
11064 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11065 this, __PRETTY_FUNCTION__, mCollectorGuest));
11066
11067 /* Create sub metrics */
11068 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11069 "Percentage of processor time spent in user mode as seen by the guest.");
11070 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11071 "Percentage of processor time spent in kernel mode as seen by the guest.");
11072 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11073 "Percentage of processor time spent idling as seen by the guest.");
11074
11075 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11076 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11077 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11078 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11079 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11080 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11081
11082 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11083
11084 /* Create and register base metrics */
11085 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11086 guestLoadUser, guestLoadKernel, guestLoadIdle);
11087 aCollector->registerBaseMetric(guestCpuLoad);
11088
11089 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11090 guestMemTotal, guestMemFree,
11091 guestMemBalloon, guestMemShared,
11092 guestMemCache, guestPagedTotal);
11093 aCollector->registerBaseMetric(guestCpuMem);
11094
11095 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11096 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11097 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11098 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11099
11100 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11101 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11102 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11103 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11104
11105 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11106 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11107 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11108 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11109
11110 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11111 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11112 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11113 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11114
11115 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11116 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11117 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11118 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11119
11120 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11121 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11122 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11123 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11124
11125 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11126 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11127 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11128 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11129
11130 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11131 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11132 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11133 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11134
11135 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11136 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11137 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11138 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11139}
11140
11141void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11142{
11143 AssertReturnVoid(isWriteLockOnCurrentThread());
11144
11145 if (aCollector)
11146 {
11147 aCollector->unregisterMetricsFor(aMachine);
11148 aCollector->unregisterBaseMetricsFor(aMachine);
11149 }
11150}
11151
11152#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11153
11154
11155////////////////////////////////////////////////////////////////////////////////
11156
11157DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11158
11159HRESULT SessionMachine::FinalConstruct()
11160{
11161 LogFlowThisFunc(("\n"));
11162
11163#if defined(RT_OS_WINDOWS)
11164 mIPCSem = NULL;
11165#elif defined(RT_OS_OS2)
11166 mIPCSem = NULLHANDLE;
11167#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11168 mIPCSem = -1;
11169#else
11170# error "Port me!"
11171#endif
11172
11173 return BaseFinalConstruct();
11174}
11175
11176void SessionMachine::FinalRelease()
11177{
11178 LogFlowThisFunc(("\n"));
11179
11180 uninit(Uninit::Unexpected);
11181
11182 BaseFinalRelease();
11183}
11184
11185/**
11186 * @note Must be called only by Machine::openSession() from its own write lock.
11187 */
11188HRESULT SessionMachine::init(Machine *aMachine)
11189{
11190 LogFlowThisFuncEnter();
11191 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
11192
11193 AssertReturn(aMachine, E_INVALIDARG);
11194
11195 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
11196
11197 /* Enclose the state transition NotReady->InInit->Ready */
11198 AutoInitSpan autoInitSpan(this);
11199 AssertReturn(autoInitSpan.isOk(), E_FAIL);
11200
11201 /* create the interprocess semaphore */
11202#if defined(RT_OS_WINDOWS)
11203 mIPCSemName = aMachine->mData->m_strConfigFileFull;
11204 for (size_t i = 0; i < mIPCSemName.length(); i++)
11205 if (mIPCSemName.raw()[i] == '\\')
11206 mIPCSemName.raw()[i] = '/';
11207 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
11208 ComAssertMsgRet(mIPCSem,
11209 ("Cannot create IPC mutex '%ls', err=%d",
11210 mIPCSemName.raw(), ::GetLastError()),
11211 E_FAIL);
11212#elif defined(RT_OS_OS2)
11213 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
11214 aMachine->mData->mUuid.raw());
11215 mIPCSemName = ipcSem;
11216 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
11217 ComAssertMsgRet(arc == NO_ERROR,
11218 ("Cannot create IPC mutex '%s', arc=%ld",
11219 ipcSem.c_str(), arc),
11220 E_FAIL);
11221#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11222# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11223# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
11224 /** @todo Check that this still works correctly. */
11225 AssertCompileSize(key_t, 8);
11226# else
11227 AssertCompileSize(key_t, 4);
11228# endif
11229 key_t key;
11230 mIPCSem = -1;
11231 mIPCKey = "0";
11232 for (uint32_t i = 0; i < 1 << 24; i++)
11233 {
11234 key = ((uint32_t)'V' << 24) | i;
11235 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
11236 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
11237 {
11238 mIPCSem = sem;
11239 if (sem >= 0)
11240 mIPCKey = BstrFmt("%u", key);
11241 break;
11242 }
11243 }
11244# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11245 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
11246 char *pszSemName = NULL;
11247 RTStrUtf8ToCurrentCP(&pszSemName, semName);
11248 key_t key = ::ftok(pszSemName, 'V');
11249 RTStrFree(pszSemName);
11250
11251 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
11252# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11253
11254 int errnoSave = errno;
11255 if (mIPCSem < 0 && errnoSave == ENOSYS)
11256 {
11257 setError(E_FAIL,
11258 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
11259 "support for SysV IPC. Check the host kernel configuration for "
11260 "CONFIG_SYSVIPC=y"));
11261 return E_FAIL;
11262 }
11263 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
11264 * the IPC semaphores */
11265 if (mIPCSem < 0 && errnoSave == ENOSPC)
11266 {
11267#ifdef RT_OS_LINUX
11268 setError(E_FAIL,
11269 tr("Cannot create IPC semaphore because the system limit for the "
11270 "maximum number of semaphore sets (SEMMNI), or the system wide "
11271 "maximum number of semaphores (SEMMNS) would be exceeded. The "
11272 "current set of SysV IPC semaphores can be determined from "
11273 "the file /proc/sysvipc/sem"));
11274#else
11275 setError(E_FAIL,
11276 tr("Cannot create IPC semaphore because the system-imposed limit "
11277 "on the maximum number of allowed semaphores or semaphore "
11278 "identifiers system-wide would be exceeded"));
11279#endif
11280 return E_FAIL;
11281 }
11282 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
11283 E_FAIL);
11284 /* set the initial value to 1 */
11285 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
11286 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
11287 E_FAIL);
11288#else
11289# error "Port me!"
11290#endif
11291
11292 /* memorize the peer Machine */
11293 unconst(mPeer) = aMachine;
11294 /* share the parent pointer */
11295 unconst(mParent) = aMachine->mParent;
11296
11297 /* take the pointers to data to share */
11298 mData.share(aMachine->mData);
11299 mSSData.share(aMachine->mSSData);
11300
11301 mUserData.share(aMachine->mUserData);
11302 mHWData.share(aMachine->mHWData);
11303 mMediaData.share(aMachine->mMediaData);
11304
11305 mStorageControllers.allocate();
11306 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
11307 it != aMachine->mStorageControllers->end();
11308 ++it)
11309 {
11310 ComObjPtr<StorageController> ctl;
11311 ctl.createObject();
11312 ctl->init(this, *it);
11313 mStorageControllers->push_back(ctl);
11314 }
11315
11316 unconst(mBIOSSettings).createObject();
11317 mBIOSSettings->init(this, aMachine->mBIOSSettings);
11318 /* create another VRDEServer object that will be mutable */
11319 unconst(mVRDEServer).createObject();
11320 mVRDEServer->init(this, aMachine->mVRDEServer);
11321 /* create another audio adapter object that will be mutable */
11322 unconst(mAudioAdapter).createObject();
11323 mAudioAdapter->init(this, aMachine->mAudioAdapter);
11324 /* create a list of serial ports that will be mutable */
11325 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11326 {
11327 unconst(mSerialPorts[slot]).createObject();
11328 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
11329 }
11330 /* create a list of parallel ports that will be mutable */
11331 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11332 {
11333 unconst(mParallelPorts[slot]).createObject();
11334 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
11335 }
11336 /* create another USB controller object that will be mutable */
11337 unconst(mUSBController).createObject();
11338 mUSBController->init(this, aMachine->mUSBController);
11339
11340 /* create a list of network adapters that will be mutable */
11341 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
11342 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11343 {
11344 unconst(mNetworkAdapters[slot]).createObject();
11345 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
11346 }
11347
11348 /* create another bandwidth control object that will be mutable */
11349 unconst(mBandwidthControl).createObject();
11350 mBandwidthControl->init(this, aMachine->mBandwidthControl);
11351
11352 /* default is to delete saved state on Saved -> PoweredOff transition */
11353 mRemoveSavedState = true;
11354
11355 /* Confirm a successful initialization when it's the case */
11356 autoInitSpan.setSucceeded();
11357
11358 LogFlowThisFuncLeave();
11359 return S_OK;
11360}
11361
11362/**
11363 * Uninitializes this session object. If the reason is other than
11364 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
11365 *
11366 * @param aReason uninitialization reason
11367 *
11368 * @note Locks mParent + this object for writing.
11369 */
11370void SessionMachine::uninit(Uninit::Reason aReason)
11371{
11372 LogFlowThisFuncEnter();
11373 LogFlowThisFunc(("reason=%d\n", aReason));
11374
11375 /*
11376 * Strongly reference ourselves to prevent this object deletion after
11377 * mData->mSession.mMachine.setNull() below (which can release the last
11378 * reference and call the destructor). Important: this must be done before
11379 * accessing any members (and before AutoUninitSpan that does it as well).
11380 * This self reference will be released as the very last step on return.
11381 */
11382 ComObjPtr<SessionMachine> selfRef = this;
11383
11384 /* Enclose the state transition Ready->InUninit->NotReady */
11385 AutoUninitSpan autoUninitSpan(this);
11386 if (autoUninitSpan.uninitDone())
11387 {
11388 LogFlowThisFunc(("Already uninitialized\n"));
11389 LogFlowThisFuncLeave();
11390 return;
11391 }
11392
11393 if (autoUninitSpan.initFailed())
11394 {
11395 /* We've been called by init() because it's failed. It's not really
11396 * necessary (nor it's safe) to perform the regular uninit sequence
11397 * below, the following is enough.
11398 */
11399 LogFlowThisFunc(("Initialization failed.\n"));
11400#if defined(RT_OS_WINDOWS)
11401 if (mIPCSem)
11402 ::CloseHandle(mIPCSem);
11403 mIPCSem = NULL;
11404#elif defined(RT_OS_OS2)
11405 if (mIPCSem != NULLHANDLE)
11406 ::DosCloseMutexSem(mIPCSem);
11407 mIPCSem = NULLHANDLE;
11408#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11409 if (mIPCSem >= 0)
11410 ::semctl(mIPCSem, 0, IPC_RMID);
11411 mIPCSem = -1;
11412# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11413 mIPCKey = "0";
11414# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
11415#else
11416# error "Port me!"
11417#endif
11418 uninitDataAndChildObjects();
11419 mData.free();
11420 unconst(mParent) = NULL;
11421 unconst(mPeer) = NULL;
11422 LogFlowThisFuncLeave();
11423 return;
11424 }
11425
11426 MachineState_T lastState;
11427 {
11428 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
11429 lastState = mData->mMachineState;
11430 }
11431 NOREF(lastState);
11432
11433#ifdef VBOX_WITH_USB
11434 // release all captured USB devices, but do this before requesting the locks below
11435 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
11436 {
11437 /* Console::captureUSBDevices() is called in the VM process only after
11438 * setting the machine state to Starting or Restoring.
11439 * Console::detachAllUSBDevices() will be called upon successful
11440 * termination. So, we need to release USB devices only if there was
11441 * an abnormal termination of a running VM.
11442 *
11443 * This is identical to SessionMachine::DetachAllUSBDevices except
11444 * for the aAbnormal argument. */
11445 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
11446 AssertComRC(rc);
11447 NOREF(rc);
11448
11449 USBProxyService *service = mParent->host()->usbProxyService();
11450 if (service)
11451 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
11452 }
11453#endif /* VBOX_WITH_USB */
11454
11455 // we need to lock this object in uninit() because the lock is shared
11456 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
11457 // and others need mParent lock, and USB needs host lock.
11458 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
11459
11460#if 0
11461 // Trigger async cleanup tasks, avoid doing things here which are not
11462 // vital to be done immediately and maybe need more locks. This calls
11463 // Machine::unregisterMetrics().
11464 mParent->onMachineUninit(mPeer);
11465#else
11466 /*
11467 * It is safe to call Machine::unregisterMetrics() here because
11468 * PerformanceCollector::samplerCallback no longer accesses guest methods
11469 * holding the lock.
11470 */
11471 unregisterMetrics(mParent->performanceCollector(), mPeer);
11472#endif
11473 /* The guest must be unregistered after its metrics (@bugref{5949}). */
11474 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11475 this, __PRETTY_FUNCTION__, mCollectorGuest));
11476 if (mCollectorGuest)
11477 {
11478 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
11479 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
11480 mCollectorGuest = NULL;
11481 }
11482
11483 if (aReason == Uninit::Abnormal)
11484 {
11485 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
11486 Global::IsOnlineOrTransient(lastState)));
11487
11488 /* reset the state to Aborted */
11489 if (mData->mMachineState != MachineState_Aborted)
11490 setMachineState(MachineState_Aborted);
11491 }
11492
11493 // any machine settings modified?
11494 if (mData->flModifications)
11495 {
11496 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
11497 rollback(false /* aNotify */);
11498 }
11499
11500 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
11501 || !mConsoleTaskData.mSnapshot);
11502 if (!mConsoleTaskData.strStateFilePath.isEmpty())
11503 {
11504 LogWarningThisFunc(("canceling failed save state request!\n"));
11505 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
11506 }
11507 else if (!mConsoleTaskData.mSnapshot.isNull())
11508 {
11509 LogWarningThisFunc(("canceling untaken snapshot!\n"));
11510
11511 /* delete all differencing hard disks created (this will also attach
11512 * their parents back by rolling back mMediaData) */
11513 rollbackMedia();
11514
11515 // delete the saved state file (it might have been already created)
11516 // AFTER killing the snapshot so that releaseSavedStateFile() won't
11517 // think it's still in use
11518 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
11519 mConsoleTaskData.mSnapshot->uninit();
11520 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
11521 }
11522
11523 if (!mData->mSession.mType.isEmpty())
11524 {
11525 /* mType is not null when this machine's process has been started by
11526 * Machine::LaunchVMProcess(), therefore it is our child. We
11527 * need to queue the PID to reap the process (and avoid zombies on
11528 * Linux). */
11529 Assert(mData->mSession.mPid != NIL_RTPROCESS);
11530 mParent->addProcessToReap(mData->mSession.mPid);
11531 }
11532
11533 mData->mSession.mPid = NIL_RTPROCESS;
11534
11535 if (aReason == Uninit::Unexpected)
11536 {
11537 /* Uninitialization didn't come from #checkForDeath(), so tell the
11538 * client watcher thread to update the set of machines that have open
11539 * sessions. */
11540 mParent->updateClientWatcher();
11541 }
11542
11543 /* uninitialize all remote controls */
11544 if (mData->mSession.mRemoteControls.size())
11545 {
11546 LogFlowThisFunc(("Closing remote sessions (%d):\n",
11547 mData->mSession.mRemoteControls.size()));
11548
11549 Data::Session::RemoteControlList::iterator it =
11550 mData->mSession.mRemoteControls.begin();
11551 while (it != mData->mSession.mRemoteControls.end())
11552 {
11553 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
11554 HRESULT rc = (*it)->Uninitialize();
11555 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
11556 if (FAILED(rc))
11557 LogWarningThisFunc(("Forgot to close the remote session?\n"));
11558 ++it;
11559 }
11560 mData->mSession.mRemoteControls.clear();
11561 }
11562
11563 /*
11564 * An expected uninitialization can come only from #checkForDeath().
11565 * Otherwise it means that something's gone really wrong (for example,
11566 * the Session implementation has released the VirtualBox reference
11567 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
11568 * etc). However, it's also possible, that the client releases the IPC
11569 * semaphore correctly (i.e. before it releases the VirtualBox reference),
11570 * but the VirtualBox release event comes first to the server process.
11571 * This case is practically possible, so we should not assert on an
11572 * unexpected uninit, just log a warning.
11573 */
11574
11575 if ((aReason == Uninit::Unexpected))
11576 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
11577
11578 if (aReason != Uninit::Normal)
11579 {
11580 mData->mSession.mDirectControl.setNull();
11581 }
11582 else
11583 {
11584 /* this must be null here (see #OnSessionEnd()) */
11585 Assert(mData->mSession.mDirectControl.isNull());
11586 Assert(mData->mSession.mState == SessionState_Unlocking);
11587 Assert(!mData->mSession.mProgress.isNull());
11588 }
11589 if (mData->mSession.mProgress)
11590 {
11591 if (aReason == Uninit::Normal)
11592 mData->mSession.mProgress->notifyComplete(S_OK);
11593 else
11594 mData->mSession.mProgress->notifyComplete(E_FAIL,
11595 COM_IIDOF(ISession),
11596 getComponentName(),
11597 tr("The VM session was aborted"));
11598 mData->mSession.mProgress.setNull();
11599 }
11600
11601 /* remove the association between the peer machine and this session machine */
11602 Assert( (SessionMachine*)mData->mSession.mMachine == this
11603 || aReason == Uninit::Unexpected);
11604
11605 /* reset the rest of session data */
11606 mData->mSession.mMachine.setNull();
11607 mData->mSession.mState = SessionState_Unlocked;
11608 mData->mSession.mType.setNull();
11609
11610 /* close the interprocess semaphore before leaving the exclusive lock */
11611#if defined(RT_OS_WINDOWS)
11612 if (mIPCSem)
11613 ::CloseHandle(mIPCSem);
11614 mIPCSem = NULL;
11615#elif defined(RT_OS_OS2)
11616 if (mIPCSem != NULLHANDLE)
11617 ::DosCloseMutexSem(mIPCSem);
11618 mIPCSem = NULLHANDLE;
11619#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11620 if (mIPCSem >= 0)
11621 ::semctl(mIPCSem, 0, IPC_RMID);
11622 mIPCSem = -1;
11623# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11624 mIPCKey = "0";
11625# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
11626#else
11627# error "Port me!"
11628#endif
11629
11630 /* fire an event */
11631 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
11632
11633 uninitDataAndChildObjects();
11634
11635 /* free the essential data structure last */
11636 mData.free();
11637
11638 /* release the exclusive lock before setting the below two to NULL */
11639 multilock.release();
11640
11641 unconst(mParent) = NULL;
11642 unconst(mPeer) = NULL;
11643
11644 LogFlowThisFuncLeave();
11645}
11646
11647// util::Lockable interface
11648////////////////////////////////////////////////////////////////////////////////
11649
11650/**
11651 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
11652 * with the primary Machine instance (mPeer).
11653 */
11654RWLockHandle *SessionMachine::lockHandle() const
11655{
11656 AssertReturn(mPeer != NULL, NULL);
11657 return mPeer->lockHandle();
11658}
11659
11660// IInternalMachineControl methods
11661////////////////////////////////////////////////////////////////////////////////
11662
11663/**
11664 * Passes collected guest statistics to performance collector object
11665 */
11666STDMETHODIMP SessionMachine::ReportGuestStatistics(ULONG aValidStats, ULONG aCpuUser,
11667 ULONG aCpuKernel, ULONG aCpuIdle,
11668 ULONG aMemTotal, ULONG aMemFree,
11669 ULONG aMemBalloon, ULONG aMemShared,
11670 ULONG aMemCache, ULONG aPageTotal,
11671 ULONG aAllocVMM, ULONG aFreeVMM,
11672 ULONG aBalloonedVMM, ULONG aSharedVMM)
11673{
11674 if (mCollectorGuest)
11675 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
11676 aMemTotal, aMemFree, aMemBalloon, aMemShared,
11677 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
11678 aBalloonedVMM, aSharedVMM);
11679
11680 return S_OK;
11681}
11682
11683/**
11684 * @note Locks this object for writing.
11685 */
11686STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
11687{
11688 AutoCaller autoCaller(this);
11689 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11690
11691 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11692
11693 mRemoveSavedState = aRemove;
11694
11695 return S_OK;
11696}
11697
11698/**
11699 * @note Locks the same as #setMachineState() does.
11700 */
11701STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
11702{
11703 return setMachineState(aMachineState);
11704}
11705
11706/**
11707 * @note Locks this object for reading.
11708 */
11709STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
11710{
11711 AutoCaller autoCaller(this);
11712 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11713
11714 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11715
11716#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
11717 mIPCSemName.cloneTo(aId);
11718 return S_OK;
11719#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11720# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11721 mIPCKey.cloneTo(aId);
11722# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11723 mData->m_strConfigFileFull.cloneTo(aId);
11724# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11725 return S_OK;
11726#else
11727# error "Port me!"
11728#endif
11729}
11730
11731/**
11732 * @note Locks this object for writing.
11733 */
11734STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
11735{
11736 LogFlowThisFunc(("aProgress=%p\n", aProgress));
11737 AutoCaller autoCaller(this);
11738 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11739
11740 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11741
11742 if (mData->mSession.mState != SessionState_Locked)
11743 return VBOX_E_INVALID_OBJECT_STATE;
11744
11745 if (!mData->mSession.mProgress.isNull())
11746 mData->mSession.mProgress->setOtherProgressObject(aProgress);
11747
11748 LogFlowThisFunc(("returns S_OK.\n"));
11749 return S_OK;
11750}
11751
11752/**
11753 * @note Locks this object for writing.
11754 */
11755STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
11756{
11757 AutoCaller autoCaller(this);
11758 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11759
11760 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11761
11762 if (mData->mSession.mState != SessionState_Locked)
11763 return VBOX_E_INVALID_OBJECT_STATE;
11764
11765 /* Finalize the LaunchVMProcess progress object. */
11766 if (mData->mSession.mProgress)
11767 {
11768 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
11769 mData->mSession.mProgress.setNull();
11770 }
11771
11772 if (SUCCEEDED((HRESULT)iResult))
11773 {
11774#ifdef VBOX_WITH_RESOURCE_USAGE_API
11775 /* The VM has been powered up successfully, so it makes sense
11776 * now to offer the performance metrics for a running machine
11777 * object. Doing it earlier wouldn't be safe. */
11778 registerMetrics(mParent->performanceCollector(), mPeer,
11779 mData->mSession.mPid);
11780#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11781 }
11782
11783 return S_OK;
11784}
11785
11786/**
11787 * @note Locks this object for writing.
11788 */
11789STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
11790{
11791 LogFlowThisFuncEnter();
11792
11793 CheckComArgOutPointerValid(aProgress);
11794
11795 AutoCaller autoCaller(this);
11796 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11797
11798 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11799
11800 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
11801 E_FAIL);
11802
11803 /* create a progress object to track operation completion */
11804 ComObjPtr<Progress> pProgress;
11805 pProgress.createObject();
11806 pProgress->init(getVirtualBox(),
11807 static_cast<IMachine *>(this) /* aInitiator */,
11808 Bstr(tr("Stopping the virtual machine")).raw(),
11809 FALSE /* aCancelable */);
11810
11811 /* fill in the console task data */
11812 mConsoleTaskData.mLastState = mData->mMachineState;
11813 mConsoleTaskData.mProgress = pProgress;
11814
11815 /* set the state to Stopping (this is expected by Console::PowerDown()) */
11816 setMachineState(MachineState_Stopping);
11817
11818 pProgress.queryInterfaceTo(aProgress);
11819
11820 return S_OK;
11821}
11822
11823/**
11824 * @note Locks this object for writing.
11825 */
11826STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
11827{
11828 LogFlowThisFuncEnter();
11829
11830 AutoCaller autoCaller(this);
11831 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11832
11833 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11834
11835 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
11836 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
11837 && mConsoleTaskData.mLastState != MachineState_Null,
11838 E_FAIL);
11839
11840 /*
11841 * On failure, set the state to the state we had when BeginPoweringDown()
11842 * was called (this is expected by Console::PowerDown() and the associated
11843 * task). On success the VM process already changed the state to
11844 * MachineState_PoweredOff, so no need to do anything.
11845 */
11846 if (FAILED(iResult))
11847 setMachineState(mConsoleTaskData.mLastState);
11848
11849 /* notify the progress object about operation completion */
11850 Assert(mConsoleTaskData.mProgress);
11851 if (SUCCEEDED(iResult))
11852 mConsoleTaskData.mProgress->notifyComplete(S_OK);
11853 else
11854 {
11855 Utf8Str strErrMsg(aErrMsg);
11856 if (strErrMsg.length())
11857 mConsoleTaskData.mProgress->notifyComplete(iResult,
11858 COM_IIDOF(ISession),
11859 getComponentName(),
11860 strErrMsg.c_str());
11861 else
11862 mConsoleTaskData.mProgress->notifyComplete(iResult);
11863 }
11864
11865 /* clear out the temporary saved state data */
11866 mConsoleTaskData.mLastState = MachineState_Null;
11867 mConsoleTaskData.mProgress.setNull();
11868
11869 LogFlowThisFuncLeave();
11870 return S_OK;
11871}
11872
11873
11874/**
11875 * Goes through the USB filters of the given machine to see if the given
11876 * device matches any filter or not.
11877 *
11878 * @note Locks the same as USBController::hasMatchingFilter() does.
11879 */
11880STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
11881 BOOL *aMatched,
11882 ULONG *aMaskedIfs)
11883{
11884 LogFlowThisFunc(("\n"));
11885
11886 CheckComArgNotNull(aUSBDevice);
11887 CheckComArgOutPointerValid(aMatched);
11888
11889 AutoCaller autoCaller(this);
11890 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11891
11892#ifdef VBOX_WITH_USB
11893 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
11894#else
11895 NOREF(aUSBDevice);
11896 NOREF(aMaskedIfs);
11897 *aMatched = FALSE;
11898#endif
11899
11900 return S_OK;
11901}
11902
11903/**
11904 * @note Locks the same as Host::captureUSBDevice() does.
11905 */
11906STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
11907{
11908 LogFlowThisFunc(("\n"));
11909
11910 AutoCaller autoCaller(this);
11911 AssertComRCReturnRC(autoCaller.rc());
11912
11913#ifdef VBOX_WITH_USB
11914 /* if captureDeviceForVM() fails, it must have set extended error info */
11915 clearError();
11916 MultiResult rc = mParent->host()->checkUSBProxyService();
11917 if (FAILED(rc)) return rc;
11918
11919 USBProxyService *service = mParent->host()->usbProxyService();
11920 AssertReturn(service, E_FAIL);
11921 return service->captureDeviceForVM(this, Guid(aId).ref());
11922#else
11923 NOREF(aId);
11924 return E_NOTIMPL;
11925#endif
11926}
11927
11928/**
11929 * @note Locks the same as Host::detachUSBDevice() does.
11930 */
11931STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
11932{
11933 LogFlowThisFunc(("\n"));
11934
11935 AutoCaller autoCaller(this);
11936 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11937
11938#ifdef VBOX_WITH_USB
11939 USBProxyService *service = mParent->host()->usbProxyService();
11940 AssertReturn(service, E_FAIL);
11941 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
11942#else
11943 NOREF(aId);
11944 NOREF(aDone);
11945 return E_NOTIMPL;
11946#endif
11947}
11948
11949/**
11950 * Inserts all machine filters to the USB proxy service and then calls
11951 * Host::autoCaptureUSBDevices().
11952 *
11953 * Called by Console from the VM process upon VM startup.
11954 *
11955 * @note Locks what called methods lock.
11956 */
11957STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
11958{
11959 LogFlowThisFunc(("\n"));
11960
11961 AutoCaller autoCaller(this);
11962 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11963
11964#ifdef VBOX_WITH_USB
11965 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
11966 AssertComRC(rc);
11967 NOREF(rc);
11968
11969 USBProxyService *service = mParent->host()->usbProxyService();
11970 AssertReturn(service, E_FAIL);
11971 return service->autoCaptureDevicesForVM(this);
11972#else
11973 return S_OK;
11974#endif
11975}
11976
11977/**
11978 * Removes all machine filters from the USB proxy service and then calls
11979 * Host::detachAllUSBDevices().
11980 *
11981 * Called by Console from the VM process upon normal VM termination or by
11982 * SessionMachine::uninit() upon abnormal VM termination (from under the
11983 * Machine/SessionMachine lock).
11984 *
11985 * @note Locks what called methods lock.
11986 */
11987STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
11988{
11989 LogFlowThisFunc(("\n"));
11990
11991 AutoCaller autoCaller(this);
11992 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11993
11994#ifdef VBOX_WITH_USB
11995 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
11996 AssertComRC(rc);
11997 NOREF(rc);
11998
11999 USBProxyService *service = mParent->host()->usbProxyService();
12000 AssertReturn(service, E_FAIL);
12001 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12002#else
12003 NOREF(aDone);
12004 return S_OK;
12005#endif
12006}
12007
12008/**
12009 * @note Locks this object for writing.
12010 */
12011STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12012 IProgress **aProgress)
12013{
12014 LogFlowThisFuncEnter();
12015
12016 AssertReturn(aSession, E_INVALIDARG);
12017 AssertReturn(aProgress, E_INVALIDARG);
12018
12019 AutoCaller autoCaller(this);
12020
12021 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
12022 /*
12023 * We don't assert below because it might happen that a non-direct session
12024 * informs us it is closed right after we've been uninitialized -- it's ok.
12025 */
12026 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12027
12028 /* get IInternalSessionControl interface */
12029 ComPtr<IInternalSessionControl> control(aSession);
12030
12031 ComAssertRet(!control.isNull(), E_INVALIDARG);
12032
12033 /* Creating a Progress object requires the VirtualBox lock, and
12034 * thus locking it here is required by the lock order rules. */
12035 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12036
12037 if (control == mData->mSession.mDirectControl)
12038 {
12039 ComAssertRet(aProgress, E_POINTER);
12040
12041 /* The direct session is being normally closed by the client process
12042 * ----------------------------------------------------------------- */
12043
12044 /* go to the closing state (essential for all open*Session() calls and
12045 * for #checkForDeath()) */
12046 Assert(mData->mSession.mState == SessionState_Locked);
12047 mData->mSession.mState = SessionState_Unlocking;
12048
12049 /* set direct control to NULL to release the remote instance */
12050 mData->mSession.mDirectControl.setNull();
12051 LogFlowThisFunc(("Direct control is set to NULL\n"));
12052
12053 if (mData->mSession.mProgress)
12054 {
12055 /* finalize the progress, someone might wait if a frontend
12056 * closes the session before powering on the VM. */
12057 mData->mSession.mProgress->notifyComplete(E_FAIL,
12058 COM_IIDOF(ISession),
12059 getComponentName(),
12060 tr("The VM session was closed before any attempt to power it on"));
12061 mData->mSession.mProgress.setNull();
12062 }
12063
12064 /* Create the progress object the client will use to wait until
12065 * #checkForDeath() is called to uninitialize this session object after
12066 * it releases the IPC semaphore.
12067 * Note! Because we're "reusing" mProgress here, this must be a proxy
12068 * object just like for LaunchVMProcess. */
12069 Assert(mData->mSession.mProgress.isNull());
12070 ComObjPtr<ProgressProxy> progress;
12071 progress.createObject();
12072 ComPtr<IUnknown> pPeer(mPeer);
12073 progress->init(mParent, pPeer,
12074 Bstr(tr("Closing session")).raw(),
12075 FALSE /* aCancelable */);
12076 progress.queryInterfaceTo(aProgress);
12077 mData->mSession.mProgress = progress;
12078 }
12079 else
12080 {
12081 /* the remote session is being normally closed */
12082 Data::Session::RemoteControlList::iterator it =
12083 mData->mSession.mRemoteControls.begin();
12084 while (it != mData->mSession.mRemoteControls.end())
12085 {
12086 if (control == *it)
12087 break;
12088 ++it;
12089 }
12090 BOOL found = it != mData->mSession.mRemoteControls.end();
12091 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12092 E_INVALIDARG);
12093 // This MUST be erase(it), not remove(*it) as the latter triggers a
12094 // very nasty use after free due to the place where the value "lives".
12095 mData->mSession.mRemoteControls.erase(it);
12096 }
12097
12098 LogFlowThisFuncLeave();
12099 return S_OK;
12100}
12101
12102/**
12103 * @note Locks this object for writing.
12104 */
12105STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12106{
12107 LogFlowThisFuncEnter();
12108
12109 CheckComArgOutPointerValid(aProgress);
12110 CheckComArgOutPointerValid(aStateFilePath);
12111
12112 AutoCaller autoCaller(this);
12113 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12114
12115 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12116
12117 AssertReturn( mData->mMachineState == MachineState_Paused
12118 && mConsoleTaskData.mLastState == MachineState_Null
12119 && mConsoleTaskData.strStateFilePath.isEmpty(),
12120 E_FAIL);
12121
12122 /* create a progress object to track operation completion */
12123 ComObjPtr<Progress> pProgress;
12124 pProgress.createObject();
12125 pProgress->init(getVirtualBox(),
12126 static_cast<IMachine *>(this) /* aInitiator */,
12127 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12128 FALSE /* aCancelable */);
12129
12130 Utf8Str strStateFilePath;
12131 /* stateFilePath is null when the machine is not running */
12132 if (mData->mMachineState == MachineState_Paused)
12133 composeSavedStateFilename(strStateFilePath);
12134
12135 /* fill in the console task data */
12136 mConsoleTaskData.mLastState = mData->mMachineState;
12137 mConsoleTaskData.strStateFilePath = strStateFilePath;
12138 mConsoleTaskData.mProgress = pProgress;
12139
12140 /* set the state to Saving (this is expected by Console::SaveState()) */
12141 setMachineState(MachineState_Saving);
12142
12143 strStateFilePath.cloneTo(aStateFilePath);
12144 pProgress.queryInterfaceTo(aProgress);
12145
12146 return S_OK;
12147}
12148
12149/**
12150 * @note Locks mParent + this object for writing.
12151 */
12152STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12153{
12154 LogFlowThisFunc(("\n"));
12155
12156 AutoCaller autoCaller(this);
12157 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12158
12159 /* endSavingState() need mParent lock */
12160 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12161
12162 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12163 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12164 && mConsoleTaskData.mLastState != MachineState_Null
12165 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12166 E_FAIL);
12167
12168 /*
12169 * On failure, set the state to the state we had when BeginSavingState()
12170 * was called (this is expected by Console::SaveState() and the associated
12171 * task). On success the VM process already changed the state to
12172 * MachineState_Saved, so no need to do anything.
12173 */
12174 if (FAILED(iResult))
12175 setMachineState(mConsoleTaskData.mLastState);
12176
12177 return endSavingState(iResult, aErrMsg);
12178}
12179
12180/**
12181 * @note Locks this object for writing.
12182 */
12183STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12184{
12185 LogFlowThisFunc(("\n"));
12186
12187 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12188
12189 AutoCaller autoCaller(this);
12190 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12191
12192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12193
12194 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12195 || mData->mMachineState == MachineState_Teleported
12196 || mData->mMachineState == MachineState_Aborted
12197 , E_FAIL); /** @todo setError. */
12198
12199 Utf8Str stateFilePathFull = aSavedStateFile;
12200 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
12201 if (RT_FAILURE(vrc))
12202 return setError(VBOX_E_FILE_ERROR,
12203 tr("Invalid saved state file path '%ls' (%Rrc)"),
12204 aSavedStateFile,
12205 vrc);
12206
12207 mSSData->strStateFilePath = stateFilePathFull;
12208
12209 /* The below setMachineState() will detect the state transition and will
12210 * update the settings file */
12211
12212 return setMachineState(MachineState_Saved);
12213}
12214
12215STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
12216 ComSafeArrayOut(BSTR, aValues),
12217 ComSafeArrayOut(LONG64, aTimestamps),
12218 ComSafeArrayOut(BSTR, aFlags))
12219{
12220 LogFlowThisFunc(("\n"));
12221
12222#ifdef VBOX_WITH_GUEST_PROPS
12223 using namespace guestProp;
12224
12225 AutoCaller autoCaller(this);
12226 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12227
12228 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12229
12230 AssertReturn(!ComSafeArrayOutIsNull(aNames), E_POINTER);
12231 AssertReturn(!ComSafeArrayOutIsNull(aValues), E_POINTER);
12232 AssertReturn(!ComSafeArrayOutIsNull(aTimestamps), E_POINTER);
12233 AssertReturn(!ComSafeArrayOutIsNull(aFlags), E_POINTER);
12234
12235 size_t cEntries = mHWData->mGuestProperties.size();
12236 com::SafeArray<BSTR> names(cEntries);
12237 com::SafeArray<BSTR> values(cEntries);
12238 com::SafeArray<LONG64> timestamps(cEntries);
12239 com::SafeArray<BSTR> flags(cEntries);
12240 unsigned i = 0;
12241 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
12242 it != mHWData->mGuestProperties.end();
12243 ++it)
12244 {
12245 char szFlags[MAX_FLAGS_LEN + 1];
12246 it->strName.cloneTo(&names[i]);
12247 it->strValue.cloneTo(&values[i]);
12248 timestamps[i] = it->mTimestamp;
12249 /* If it is NULL, keep it NULL. */
12250 if (it->mFlags)
12251 {
12252 writeFlags(it->mFlags, szFlags);
12253 Bstr(szFlags).cloneTo(&flags[i]);
12254 }
12255 else
12256 flags[i] = NULL;
12257 ++i;
12258 }
12259 names.detachTo(ComSafeArrayOutArg(aNames));
12260 values.detachTo(ComSafeArrayOutArg(aValues));
12261 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
12262 flags.detachTo(ComSafeArrayOutArg(aFlags));
12263 return S_OK;
12264#else
12265 ReturnComNotImplemented();
12266#endif
12267}
12268
12269STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
12270 IN_BSTR aValue,
12271 LONG64 aTimestamp,
12272 IN_BSTR aFlags)
12273{
12274 LogFlowThisFunc(("\n"));
12275
12276#ifdef VBOX_WITH_GUEST_PROPS
12277 using namespace guestProp;
12278
12279 CheckComArgStrNotEmptyOrNull(aName);
12280 CheckComArgNotNull(aValue);
12281 CheckComArgNotNull(aFlags);
12282
12283 try
12284 {
12285 /*
12286 * Convert input up front.
12287 */
12288 Utf8Str utf8Name(aName);
12289 uint32_t fFlags = NILFLAG;
12290 if (aFlags)
12291 {
12292 Utf8Str utf8Flags(aFlags);
12293 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
12294 AssertRCReturn(vrc, E_INVALIDARG);
12295 }
12296
12297 /*
12298 * Now grab the object lock, validate the state and do the update.
12299 */
12300 AutoCaller autoCaller(this);
12301 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12302
12303 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12304
12305 switch (mData->mMachineState)
12306 {
12307 case MachineState_Paused:
12308 case MachineState_Running:
12309 case MachineState_Teleporting:
12310 case MachineState_TeleportingPausedVM:
12311 case MachineState_LiveSnapshotting:
12312 case MachineState_DeletingSnapshotOnline:
12313 case MachineState_DeletingSnapshotPaused:
12314 case MachineState_Saving:
12315 break;
12316
12317 default:
12318#ifndef DEBUG_sunlover
12319 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
12320 VBOX_E_INVALID_VM_STATE);
12321#else
12322 return VBOX_E_INVALID_VM_STATE;
12323#endif
12324 }
12325
12326 setModified(IsModified_MachineData);
12327 mHWData.backup();
12328
12329 /** @todo r=bird: The careful memory handling doesn't work out here because
12330 * the catch block won't undo any damage we've done. So, if push_back throws
12331 * bad_alloc then you've lost the value.
12332 *
12333 * Another thing. Doing a linear search here isn't extremely efficient, esp.
12334 * since values that changes actually bubbles to the end of the list. Using
12335 * something that has an efficient lookup and can tolerate a bit of updates
12336 * would be nice. RTStrSpace is one suggestion (it's not perfect). Some
12337 * combination of RTStrCache (for sharing names and getting uniqueness into
12338 * the bargain) and hash/tree is another. */
12339 for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();
12340 iter != mHWData->mGuestProperties.end();
12341 ++iter)
12342 if (utf8Name == iter->strName)
12343 {
12344 mHWData->mGuestProperties.erase(iter);
12345 mData->mGuestPropertiesModified = TRUE;
12346 break;
12347 }
12348 if (aValue != NULL)
12349 {
12350 HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };
12351 mHWData->mGuestProperties.push_back(property);
12352 mData->mGuestPropertiesModified = TRUE;
12353 }
12354
12355 /*
12356 * Send a callback notification if appropriate
12357 */
12358 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
12359 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
12360 RTSTR_MAX,
12361 utf8Name.c_str(),
12362 RTSTR_MAX, NULL)
12363 )
12364 {
12365 alock.release();
12366
12367 mParent->onGuestPropertyChange(mData->mUuid,
12368 aName,
12369 aValue,
12370 aFlags);
12371 }
12372 }
12373 catch (...)
12374 {
12375 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
12376 }
12377 return S_OK;
12378#else
12379 ReturnComNotImplemented();
12380#endif
12381}
12382
12383STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
12384 IMediumAttachment **aNewAttachment)
12385{
12386 CheckComArgNotNull(aAttachment);
12387 CheckComArgOutPointerValid(aNewAttachment);
12388
12389 AutoCaller autoCaller(this);
12390 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12391
12392 // request the host lock first, since might be calling Host methods for getting host drives;
12393 // next, protect the media tree all the while we're in here, as well as our member variables
12394 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
12395 this->lockHandle(),
12396 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
12397
12398 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
12399
12400 Bstr ctrlName;
12401 LONG lPort;
12402 LONG lDevice;
12403 bool fTempEject;
12404 {
12405 AutoCaller autoAttachCaller(this);
12406 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12407
12408 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12409
12410 /* Need to query the details first, as the IMediumAttachment reference
12411 * might be to the original settings, which we are going to change. */
12412 ctrlName = pAttach->getControllerName();
12413 lPort = pAttach->getPort();
12414 lDevice = pAttach->getDevice();
12415 fTempEject = pAttach->getTempEject();
12416 }
12417
12418 if (!fTempEject)
12419 {
12420 /* Remember previously mounted medium. The medium before taking the
12421 * backup is not necessarily the same thing. */
12422 ComObjPtr<Medium> oldmedium;
12423 oldmedium = pAttach->getMedium();
12424
12425 setModified(IsModified_Storage);
12426 mMediaData.backup();
12427
12428 // The backup operation makes the pAttach reference point to the
12429 // old settings. Re-get the correct reference.
12430 pAttach = findAttachment(mMediaData->mAttachments,
12431 ctrlName.raw(),
12432 lPort,
12433 lDevice);
12434
12435 {
12436 AutoCaller autoAttachCaller(this);
12437 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12438
12439 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12440 if (!oldmedium.isNull())
12441 oldmedium->removeBackReference(mData->mUuid);
12442
12443 pAttach->updateMedium(NULL);
12444 pAttach->updateEjected();
12445 }
12446
12447 setModified(IsModified_Storage);
12448 }
12449 else
12450 {
12451 {
12452 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12453 pAttach->updateEjected();
12454 }
12455 }
12456
12457 pAttach.queryInterfaceTo(aNewAttachment);
12458
12459 return S_OK;
12460}
12461
12462// public methods only for internal purposes
12463/////////////////////////////////////////////////////////////////////////////
12464
12465/**
12466 * Called from the client watcher thread to check for expected or unexpected
12467 * death of the client process that has a direct session to this machine.
12468 *
12469 * On Win32 and on OS/2, this method is called only when we've got the
12470 * mutex (i.e. the client has either died or terminated normally) so it always
12471 * returns @c true (the client is terminated, the session machine is
12472 * uninitialized).
12473 *
12474 * On other platforms, the method returns @c true if the client process has
12475 * terminated normally or abnormally and the session machine was uninitialized,
12476 * and @c false if the client process is still alive.
12477 *
12478 * @note Locks this object for writing.
12479 */
12480bool SessionMachine::checkForDeath()
12481{
12482 Uninit::Reason reason;
12483 bool terminated = false;
12484
12485 /* Enclose autoCaller with a block because calling uninit() from under it
12486 * will deadlock. */
12487 {
12488 AutoCaller autoCaller(this);
12489 if (!autoCaller.isOk())
12490 {
12491 /* return true if not ready, to cause the client watcher to exclude
12492 * the corresponding session from watching */
12493 LogFlowThisFunc(("Already uninitialized!\n"));
12494 return true;
12495 }
12496
12497 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12498
12499 /* Determine the reason of death: if the session state is Closing here,
12500 * everything is fine. Otherwise it means that the client did not call
12501 * OnSessionEnd() before it released the IPC semaphore. This may happen
12502 * either because the client process has abnormally terminated, or
12503 * because it simply forgot to call ISession::Close() before exiting. We
12504 * threat the latter also as an abnormal termination (see
12505 * Session::uninit() for details). */
12506 reason = mData->mSession.mState == SessionState_Unlocking ?
12507 Uninit::Normal :
12508 Uninit::Abnormal;
12509
12510#if defined(RT_OS_WINDOWS)
12511
12512 AssertMsg(mIPCSem, ("semaphore must be created"));
12513
12514 /* release the IPC mutex */
12515 ::ReleaseMutex(mIPCSem);
12516
12517 terminated = true;
12518
12519#elif defined(RT_OS_OS2)
12520
12521 AssertMsg(mIPCSem, ("semaphore must be created"));
12522
12523 /* release the IPC mutex */
12524 ::DosReleaseMutexSem(mIPCSem);
12525
12526 terminated = true;
12527
12528#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12529
12530 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
12531
12532 int val = ::semctl(mIPCSem, 0, GETVAL);
12533 if (val > 0)
12534 {
12535 /* the semaphore is signaled, meaning the session is terminated */
12536 terminated = true;
12537 }
12538
12539#else
12540# error "Port me!"
12541#endif
12542
12543 } /* AutoCaller block */
12544
12545 if (terminated)
12546 uninit(reason);
12547
12548 return terminated;
12549}
12550
12551/**
12552 * @note Locks this object for reading.
12553 */
12554HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
12555{
12556 LogFlowThisFunc(("\n"));
12557
12558 AutoCaller autoCaller(this);
12559 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12560
12561 ComPtr<IInternalSessionControl> directControl;
12562 {
12563 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12564 directControl = mData->mSession.mDirectControl;
12565 }
12566
12567 /* ignore notifications sent after #OnSessionEnd() is called */
12568 if (!directControl)
12569 return S_OK;
12570
12571 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
12572}
12573
12574/**
12575 * @note Locks this object for reading.
12576 */
12577HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
12578 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
12579{
12580 LogFlowThisFunc(("\n"));
12581
12582 AutoCaller autoCaller(this);
12583 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12584
12585 ComPtr<IInternalSessionControl> directControl;
12586 {
12587 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12588 directControl = mData->mSession.mDirectControl;
12589 }
12590
12591 /* ignore notifications sent after #OnSessionEnd() is called */
12592 if (!directControl)
12593 return S_OK;
12594 /*
12595 * instead acting like callback we ask IVirtualBox deliver corresponding event
12596 */
12597
12598 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, aHostPort, aGuestIp, aGuestPort);
12599 return S_OK;
12600}
12601
12602/**
12603 * @note Locks this object for reading.
12604 */
12605HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
12606{
12607 LogFlowThisFunc(("\n"));
12608
12609 AutoCaller autoCaller(this);
12610 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12611
12612 ComPtr<IInternalSessionControl> directControl;
12613 {
12614 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12615 directControl = mData->mSession.mDirectControl;
12616 }
12617
12618 /* ignore notifications sent after #OnSessionEnd() is called */
12619 if (!directControl)
12620 return S_OK;
12621
12622 return directControl->OnSerialPortChange(serialPort);
12623}
12624
12625/**
12626 * @note Locks this object for reading.
12627 */
12628HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
12629{
12630 LogFlowThisFunc(("\n"));
12631
12632 AutoCaller autoCaller(this);
12633 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12634
12635 ComPtr<IInternalSessionControl> directControl;
12636 {
12637 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12638 directControl = mData->mSession.mDirectControl;
12639 }
12640
12641 /* ignore notifications sent after #OnSessionEnd() is called */
12642 if (!directControl)
12643 return S_OK;
12644
12645 return directControl->OnParallelPortChange(parallelPort);
12646}
12647
12648/**
12649 * @note Locks this object for reading.
12650 */
12651HRESULT SessionMachine::onStorageControllerChange()
12652{
12653 LogFlowThisFunc(("\n"));
12654
12655 AutoCaller autoCaller(this);
12656 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12657
12658 ComPtr<IInternalSessionControl> directControl;
12659 {
12660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12661 directControl = mData->mSession.mDirectControl;
12662 }
12663
12664 /* ignore notifications sent after #OnSessionEnd() is called */
12665 if (!directControl)
12666 return S_OK;
12667
12668 return directControl->OnStorageControllerChange();
12669}
12670
12671/**
12672 * @note Locks this object for reading.
12673 */
12674HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
12675{
12676 LogFlowThisFunc(("\n"));
12677
12678 AutoCaller autoCaller(this);
12679 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12680
12681 ComPtr<IInternalSessionControl> directControl;
12682 {
12683 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12684 directControl = mData->mSession.mDirectControl;
12685 }
12686
12687 /* ignore notifications sent after #OnSessionEnd() is called */
12688 if (!directControl)
12689 return S_OK;
12690
12691 return directControl->OnMediumChange(aAttachment, aForce);
12692}
12693
12694/**
12695 * @note Locks this object for reading.
12696 */
12697HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
12698{
12699 LogFlowThisFunc(("\n"));
12700
12701 AutoCaller autoCaller(this);
12702 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
12703
12704 ComPtr<IInternalSessionControl> directControl;
12705 {
12706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12707 directControl = mData->mSession.mDirectControl;
12708 }
12709
12710 /* ignore notifications sent after #OnSessionEnd() is called */
12711 if (!directControl)
12712 return S_OK;
12713
12714 return directControl->OnCPUChange(aCPU, aRemove);
12715}
12716
12717HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
12718{
12719 LogFlowThisFunc(("\n"));
12720
12721 AutoCaller autoCaller(this);
12722 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
12723
12724 ComPtr<IInternalSessionControl> directControl;
12725 {
12726 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12727 directControl = mData->mSession.mDirectControl;
12728 }
12729
12730 /* ignore notifications sent after #OnSessionEnd() is called */
12731 if (!directControl)
12732 return S_OK;
12733
12734 return directControl->OnCPUExecutionCapChange(aExecutionCap);
12735}
12736
12737/**
12738 * @note Locks this object for reading.
12739 */
12740HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
12741{
12742 LogFlowThisFunc(("\n"));
12743
12744 AutoCaller autoCaller(this);
12745 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12746
12747 ComPtr<IInternalSessionControl> directControl;
12748 {
12749 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12750 directControl = mData->mSession.mDirectControl;
12751 }
12752
12753 /* ignore notifications sent after #OnSessionEnd() is called */
12754 if (!directControl)
12755 return S_OK;
12756
12757 return directControl->OnVRDEServerChange(aRestart);
12758}
12759
12760/**
12761 * @note Locks this object for reading.
12762 */
12763HRESULT SessionMachine::onUSBControllerChange()
12764{
12765 LogFlowThisFunc(("\n"));
12766
12767 AutoCaller autoCaller(this);
12768 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12769
12770 ComPtr<IInternalSessionControl> directControl;
12771 {
12772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12773 directControl = mData->mSession.mDirectControl;
12774 }
12775
12776 /* ignore notifications sent after #OnSessionEnd() is called */
12777 if (!directControl)
12778 return S_OK;
12779
12780 return directControl->OnUSBControllerChange();
12781}
12782
12783/**
12784 * @note Locks this object for reading.
12785 */
12786HRESULT SessionMachine::onSharedFolderChange()
12787{
12788 LogFlowThisFunc(("\n"));
12789
12790 AutoCaller autoCaller(this);
12791 AssertComRCReturnRC(autoCaller.rc());
12792
12793 ComPtr<IInternalSessionControl> directControl;
12794 {
12795 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12796 directControl = mData->mSession.mDirectControl;
12797 }
12798
12799 /* ignore notifications sent after #OnSessionEnd() is called */
12800 if (!directControl)
12801 return S_OK;
12802
12803 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
12804}
12805
12806/**
12807 * @note Locks this object for reading.
12808 */
12809HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
12810{
12811 LogFlowThisFunc(("\n"));
12812
12813 AutoCaller autoCaller(this);
12814 AssertComRCReturnRC(autoCaller.rc());
12815
12816 ComPtr<IInternalSessionControl> directControl;
12817 {
12818 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12819 directControl = mData->mSession.mDirectControl;
12820 }
12821
12822 /* ignore notifications sent after #OnSessionEnd() is called */
12823 if (!directControl)
12824 return S_OK;
12825
12826 return directControl->OnClipboardModeChange(aClipboardMode);
12827}
12828
12829/**
12830 * @note Locks this object for reading.
12831 */
12832HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
12833{
12834 LogFlowThisFunc(("\n"));
12835
12836 AutoCaller autoCaller(this);
12837 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
12838
12839 ComPtr<IInternalSessionControl> directControl;
12840 {
12841 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12842 directControl = mData->mSession.mDirectControl;
12843 }
12844
12845 /* ignore notifications sent after #OnSessionEnd() is called */
12846 if (!directControl)
12847 return S_OK;
12848
12849 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
12850}
12851
12852/**
12853 * @note Locks this object for reading.
12854 */
12855HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove)
12856{
12857 LogFlowThisFunc(("\n"));
12858
12859 AutoCaller autoCaller(this);
12860 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12861
12862 ComPtr<IInternalSessionControl> directControl;
12863 {
12864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12865 directControl = mData->mSession.mDirectControl;
12866 }
12867
12868 /* ignore notifications sent after #OnSessionEnd() is called */
12869 if (!directControl)
12870 return S_OK;
12871
12872 return directControl->OnStorageDeviceChange(aAttachment, aRemove);
12873}
12874
12875/**
12876 * Returns @c true if this machine's USB controller reports it has a matching
12877 * filter for the given USB device and @c false otherwise.
12878 *
12879 * @note locks this object for reading.
12880 */
12881bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
12882{
12883 AutoCaller autoCaller(this);
12884 /* silently return if not ready -- this method may be called after the
12885 * direct machine session has been called */
12886 if (!autoCaller.isOk())
12887 return false;
12888
12889#ifdef VBOX_WITH_USB
12890 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12891
12892 switch (mData->mMachineState)
12893 {
12894 case MachineState_Starting:
12895 case MachineState_Restoring:
12896 case MachineState_TeleportingIn:
12897 case MachineState_Paused:
12898 case MachineState_Running:
12899 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
12900 * elsewhere... */
12901 alock.release();
12902 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
12903 default: break;
12904 }
12905#else
12906 NOREF(aDevice);
12907 NOREF(aMaskedIfs);
12908#endif
12909 return false;
12910}
12911
12912/**
12913 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
12914 */
12915HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
12916 IVirtualBoxErrorInfo *aError,
12917 ULONG aMaskedIfs)
12918{
12919 LogFlowThisFunc(("\n"));
12920
12921 AutoCaller autoCaller(this);
12922
12923 /* This notification may happen after the machine object has been
12924 * uninitialized (the session was closed), so don't assert. */
12925 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12926
12927 ComPtr<IInternalSessionControl> directControl;
12928 {
12929 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12930 directControl = mData->mSession.mDirectControl;
12931 }
12932
12933 /* fail on notifications sent after #OnSessionEnd() is called, it is
12934 * expected by the caller */
12935 if (!directControl)
12936 return E_FAIL;
12937
12938 /* No locks should be held at this point. */
12939 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
12940 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
12941
12942 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
12943}
12944
12945/**
12946 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
12947 */
12948HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
12949 IVirtualBoxErrorInfo *aError)
12950{
12951 LogFlowThisFunc(("\n"));
12952
12953 AutoCaller autoCaller(this);
12954
12955 /* This notification may happen after the machine object has been
12956 * uninitialized (the session was closed), so don't assert. */
12957 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12958
12959 ComPtr<IInternalSessionControl> directControl;
12960 {
12961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12962 directControl = mData->mSession.mDirectControl;
12963 }
12964
12965 /* fail on notifications sent after #OnSessionEnd() is called, it is
12966 * expected by the caller */
12967 if (!directControl)
12968 return E_FAIL;
12969
12970 /* No locks should be held at this point. */
12971 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
12972 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
12973
12974 return directControl->OnUSBDeviceDetach(aId, aError);
12975}
12976
12977// protected methods
12978/////////////////////////////////////////////////////////////////////////////
12979
12980/**
12981 * Helper method to finalize saving the state.
12982 *
12983 * @note Must be called from under this object's lock.
12984 *
12985 * @param aRc S_OK if the snapshot has been taken successfully
12986 * @param aErrMsg human readable error message for failure
12987 *
12988 * @note Locks mParent + this objects for writing.
12989 */
12990HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
12991{
12992 LogFlowThisFuncEnter();
12993
12994 AutoCaller autoCaller(this);
12995 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12996
12997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12998
12999 HRESULT rc = S_OK;
13000
13001 if (SUCCEEDED(aRc))
13002 {
13003 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13004
13005 /* save all VM settings */
13006 rc = saveSettings(NULL);
13007 // no need to check whether VirtualBox.xml needs saving also since
13008 // we can't have a name change pending at this point
13009 }
13010 else
13011 {
13012 // delete the saved state file (it might have been already created);
13013 // we need not check whether this is shared with a snapshot here because
13014 // we certainly created this saved state file here anew
13015 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13016 }
13017
13018 /* notify the progress object about operation completion */
13019 Assert(mConsoleTaskData.mProgress);
13020 if (SUCCEEDED(aRc))
13021 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13022 else
13023 {
13024 if (aErrMsg.length())
13025 mConsoleTaskData.mProgress->notifyComplete(aRc,
13026 COM_IIDOF(ISession),
13027 getComponentName(),
13028 aErrMsg.c_str());
13029 else
13030 mConsoleTaskData.mProgress->notifyComplete(aRc);
13031 }
13032
13033 /* clear out the temporary saved state data */
13034 mConsoleTaskData.mLastState = MachineState_Null;
13035 mConsoleTaskData.strStateFilePath.setNull();
13036 mConsoleTaskData.mProgress.setNull();
13037
13038 LogFlowThisFuncLeave();
13039 return rc;
13040}
13041
13042/**
13043 * Deletes the given file if it is no longer in use by either the current machine state
13044 * (if the machine is "saved") or any of the machine's snapshots.
13045 *
13046 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13047 * but is different for each SnapshotMachine. When calling this, the order of calling this
13048 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13049 * is therefore critical. I know, it's all rather messy.
13050 *
13051 * @param strStateFile
13052 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
13053 */
13054void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
13055 Snapshot *pSnapshotToIgnore)
13056{
13057 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13058 if ( (strStateFile.isNotEmpty())
13059 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13060 )
13061 // ... and it must also not be shared with other snapshots
13062 if ( !mData->mFirstSnapshot
13063 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13064 // this checks the SnapshotMachine's state file paths
13065 )
13066 RTFileDelete(strStateFile.c_str());
13067}
13068
13069/**
13070 * Locks the attached media.
13071 *
13072 * All attached hard disks are locked for writing and DVD/floppy are locked for
13073 * reading. Parents of attached hard disks (if any) are locked for reading.
13074 *
13075 * This method also performs accessibility check of all media it locks: if some
13076 * media is inaccessible, the method will return a failure and a bunch of
13077 * extended error info objects per each inaccessible medium.
13078 *
13079 * Note that this method is atomic: if it returns a success, all media are
13080 * locked as described above; on failure no media is locked at all (all
13081 * succeeded individual locks will be undone).
13082 *
13083 * This method is intended to be called when the machine is in Starting or
13084 * Restoring state and asserts otherwise.
13085 *
13086 * The locks made by this method must be undone by calling #unlockMedia() when
13087 * no more needed.
13088 */
13089HRESULT SessionMachine::lockMedia()
13090{
13091 AutoCaller autoCaller(this);
13092 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13093
13094 AutoMultiWriteLock2 alock(this->lockHandle(),
13095 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13096
13097 AssertReturn( mData->mMachineState == MachineState_Starting
13098 || mData->mMachineState == MachineState_Restoring
13099 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13100 /* bail out if trying to lock things with already set up locking */
13101 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13102
13103 clearError();
13104 MultiResult mrc(S_OK);
13105
13106 /* Collect locking information for all medium objects attached to the VM. */
13107 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13108 it != mMediaData->mAttachments.end();
13109 ++it)
13110 {
13111 MediumAttachment* pAtt = *it;
13112 DeviceType_T devType = pAtt->getType();
13113 Medium *pMedium = pAtt->getMedium();
13114
13115 MediumLockList *pMediumLockList(new MediumLockList());
13116 // There can be attachments without a medium (floppy/dvd), and thus
13117 // it's impossible to create a medium lock list. It still makes sense
13118 // to have the empty medium lock list in the map in case a medium is
13119 // attached later.
13120 if (pMedium != NULL)
13121 {
13122 MediumType_T mediumType = pMedium->getType();
13123 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13124 || mediumType == MediumType_Shareable;
13125 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13126
13127 alock.release();
13128 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13129 !fIsReadOnlyLock /* fMediumLockWrite */,
13130 NULL,
13131 *pMediumLockList);
13132 alock.acquire();
13133 if (FAILED(mrc))
13134 {
13135 delete pMediumLockList;
13136 mData->mSession.mLockedMedia.Clear();
13137 break;
13138 }
13139 }
13140
13141 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13142 if (FAILED(rc))
13143 {
13144 mData->mSession.mLockedMedia.Clear();
13145 mrc = setError(rc,
13146 tr("Collecting locking information for all attached media failed"));
13147 break;
13148 }
13149 }
13150
13151 if (SUCCEEDED(mrc))
13152 {
13153 /* Now lock all media. If this fails, nothing is locked. */
13154 alock.release();
13155 HRESULT rc = mData->mSession.mLockedMedia.Lock();
13156 alock.acquire();
13157 if (FAILED(rc))
13158 {
13159 mrc = setError(rc,
13160 tr("Locking of attached media failed"));
13161 }
13162 }
13163
13164 return mrc;
13165}
13166
13167/**
13168 * Undoes the locks made by by #lockMedia().
13169 */
13170void SessionMachine::unlockMedia()
13171{
13172 AutoCaller autoCaller(this);
13173 AssertComRCReturnVoid(autoCaller.rc());
13174
13175 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13176
13177 /* we may be holding important error info on the current thread;
13178 * preserve it */
13179 ErrorInfoKeeper eik;
13180
13181 HRESULT rc = mData->mSession.mLockedMedia.Clear();
13182 AssertComRC(rc);
13183}
13184
13185/**
13186 * Helper to change the machine state (reimplementation).
13187 *
13188 * @note Locks this object for writing.
13189 */
13190HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
13191{
13192 LogFlowThisFuncEnter();
13193 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
13194
13195 AutoCaller autoCaller(this);
13196 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13197
13198 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13199
13200 MachineState_T oldMachineState = mData->mMachineState;
13201
13202 AssertMsgReturn(oldMachineState != aMachineState,
13203 ("oldMachineState=%s, aMachineState=%s\n",
13204 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
13205 E_FAIL);
13206
13207 HRESULT rc = S_OK;
13208
13209 int stsFlags = 0;
13210 bool deleteSavedState = false;
13211
13212 /* detect some state transitions */
13213
13214 if ( ( oldMachineState == MachineState_Saved
13215 && aMachineState == MachineState_Restoring)
13216 || ( ( oldMachineState == MachineState_PoweredOff
13217 || oldMachineState == MachineState_Teleported
13218 || oldMachineState == MachineState_Aborted
13219 )
13220 && ( aMachineState == MachineState_TeleportingIn
13221 || aMachineState == MachineState_Starting
13222 )
13223 )
13224 )
13225 {
13226 /* The EMT thread is about to start */
13227
13228 /* Nothing to do here for now... */
13229
13230 /// @todo NEWMEDIA don't let mDVDDrive and other children
13231 /// change anything when in the Starting/Restoring state
13232 }
13233 else if ( ( oldMachineState == MachineState_Running
13234 || oldMachineState == MachineState_Paused
13235 || oldMachineState == MachineState_Teleporting
13236 || oldMachineState == MachineState_LiveSnapshotting
13237 || oldMachineState == MachineState_Stuck
13238 || oldMachineState == MachineState_Starting
13239 || oldMachineState == MachineState_Stopping
13240 || oldMachineState == MachineState_Saving
13241 || oldMachineState == MachineState_Restoring
13242 || oldMachineState == MachineState_TeleportingPausedVM
13243 || oldMachineState == MachineState_TeleportingIn
13244 )
13245 && ( aMachineState == MachineState_PoweredOff
13246 || aMachineState == MachineState_Saved
13247 || aMachineState == MachineState_Teleported
13248 || aMachineState == MachineState_Aborted
13249 )
13250 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
13251 * snapshot */
13252 && ( mConsoleTaskData.mSnapshot.isNull()
13253 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
13254 )
13255 )
13256 {
13257 /* The EMT thread has just stopped, unlock attached media. Note that as
13258 * opposed to locking that is done from Console, we do unlocking here
13259 * because the VM process may have aborted before having a chance to
13260 * properly unlock all media it locked. */
13261
13262 unlockMedia();
13263 }
13264
13265 if (oldMachineState == MachineState_Restoring)
13266 {
13267 if (aMachineState != MachineState_Saved)
13268 {
13269 /*
13270 * delete the saved state file once the machine has finished
13271 * restoring from it (note that Console sets the state from
13272 * Restoring to Saved if the VM couldn't restore successfully,
13273 * to give the user an ability to fix an error and retry --
13274 * we keep the saved state file in this case)
13275 */
13276 deleteSavedState = true;
13277 }
13278 }
13279 else if ( oldMachineState == MachineState_Saved
13280 && ( aMachineState == MachineState_PoweredOff
13281 || aMachineState == MachineState_Aborted
13282 || aMachineState == MachineState_Teleported
13283 )
13284 )
13285 {
13286 /*
13287 * delete the saved state after Console::ForgetSavedState() is called
13288 * or if the VM process (owning a direct VM session) crashed while the
13289 * VM was Saved
13290 */
13291
13292 /// @todo (dmik)
13293 // Not sure that deleting the saved state file just because of the
13294 // client death before it attempted to restore the VM is a good
13295 // thing. But when it crashes we need to go to the Aborted state
13296 // which cannot have the saved state file associated... The only
13297 // way to fix this is to make the Aborted condition not a VM state
13298 // but a bool flag: i.e., when a crash occurs, set it to true and
13299 // change the state to PoweredOff or Saved depending on the
13300 // saved state presence.
13301
13302 deleteSavedState = true;
13303 mData->mCurrentStateModified = TRUE;
13304 stsFlags |= SaveSTS_CurStateModified;
13305 }
13306
13307 if ( aMachineState == MachineState_Starting
13308 || aMachineState == MachineState_Restoring
13309 || aMachineState == MachineState_TeleportingIn
13310 )
13311 {
13312 /* set the current state modified flag to indicate that the current
13313 * state is no more identical to the state in the
13314 * current snapshot */
13315 if (!mData->mCurrentSnapshot.isNull())
13316 {
13317 mData->mCurrentStateModified = TRUE;
13318 stsFlags |= SaveSTS_CurStateModified;
13319 }
13320 }
13321
13322 if (deleteSavedState)
13323 {
13324 if (mRemoveSavedState)
13325 {
13326 Assert(!mSSData->strStateFilePath.isEmpty());
13327
13328 // it is safe to delete the saved state file if ...
13329 if ( !mData->mFirstSnapshot // ... we have no snapshots or
13330 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
13331 // ... none of the snapshots share the saved state file
13332 )
13333 RTFileDelete(mSSData->strStateFilePath.c_str());
13334 }
13335
13336 mSSData->strStateFilePath.setNull();
13337 stsFlags |= SaveSTS_StateFilePath;
13338 }
13339
13340 /* redirect to the underlying peer machine */
13341 mPeer->setMachineState(aMachineState);
13342
13343 if ( aMachineState == MachineState_PoweredOff
13344 || aMachineState == MachineState_Teleported
13345 || aMachineState == MachineState_Aborted
13346 || aMachineState == MachineState_Saved)
13347 {
13348 /* the machine has stopped execution
13349 * (or the saved state file was adopted) */
13350 stsFlags |= SaveSTS_StateTimeStamp;
13351 }
13352
13353 if ( ( oldMachineState == MachineState_PoweredOff
13354 || oldMachineState == MachineState_Aborted
13355 || oldMachineState == MachineState_Teleported
13356 )
13357 && aMachineState == MachineState_Saved)
13358 {
13359 /* the saved state file was adopted */
13360 Assert(!mSSData->strStateFilePath.isEmpty());
13361 stsFlags |= SaveSTS_StateFilePath;
13362 }
13363
13364#ifdef VBOX_WITH_GUEST_PROPS
13365 if ( aMachineState == MachineState_PoweredOff
13366 || aMachineState == MachineState_Aborted
13367 || aMachineState == MachineState_Teleported)
13368 {
13369 /* Make sure any transient guest properties get removed from the
13370 * property store on shutdown. */
13371
13372 HWData::GuestPropertyList::iterator it;
13373 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
13374 if (!fNeedsSaving)
13375 for (it = mHWData->mGuestProperties.begin();
13376 it != mHWData->mGuestProperties.end(); ++it)
13377 if ( (it->mFlags & guestProp::TRANSIENT)
13378 || (it->mFlags & guestProp::TRANSRESET))
13379 {
13380 fNeedsSaving = true;
13381 break;
13382 }
13383 if (fNeedsSaving)
13384 {
13385 mData->mCurrentStateModified = TRUE;
13386 stsFlags |= SaveSTS_CurStateModified;
13387 SaveSettings(); // @todo r=dj why the public method? why first SaveSettings and then saveStateSettings?
13388 }
13389 }
13390#endif
13391
13392 rc = saveStateSettings(stsFlags);
13393
13394 if ( ( oldMachineState != MachineState_PoweredOff
13395 && oldMachineState != MachineState_Aborted
13396 && oldMachineState != MachineState_Teleported
13397 )
13398 && ( aMachineState == MachineState_PoweredOff
13399 || aMachineState == MachineState_Aborted
13400 || aMachineState == MachineState_Teleported
13401 )
13402 )
13403 {
13404 /* we've been shut down for any reason */
13405 /* no special action so far */
13406 }
13407
13408 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
13409 LogFlowThisFuncLeave();
13410 return rc;
13411}
13412
13413/**
13414 * Sends the current machine state value to the VM process.
13415 *
13416 * @note Locks this object for reading, then calls a client process.
13417 */
13418HRESULT SessionMachine::updateMachineStateOnClient()
13419{
13420 AutoCaller autoCaller(this);
13421 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13422
13423 ComPtr<IInternalSessionControl> directControl;
13424 {
13425 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13426 AssertReturn(!!mData, E_FAIL);
13427 directControl = mData->mSession.mDirectControl;
13428
13429 /* directControl may be already set to NULL here in #OnSessionEnd()
13430 * called too early by the direct session process while there is still
13431 * some operation (like deleting the snapshot) in progress. The client
13432 * process in this case is waiting inside Session::close() for the
13433 * "end session" process object to complete, while #uninit() called by
13434 * #checkForDeath() on the Watcher thread is waiting for the pending
13435 * operation to complete. For now, we accept this inconsistent behavior
13436 * and simply do nothing here. */
13437
13438 if (mData->mSession.mState == SessionState_Unlocking)
13439 return S_OK;
13440
13441 AssertReturn(!directControl.isNull(), E_FAIL);
13442 }
13443
13444 return directControl->UpdateMachineState(mData->mMachineState);
13445}
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