VirtualBox

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

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

More autostart service updates, work in progress

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 448.1 KB
Line 
1/* $Id: MachineImpl.cpp 41999 2012-07-03 12:50:18Z 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 /* We can remove the controller only if there is no device attached. */
5830 /* check if the device slot is already busy */
5831 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
5832 it != mMediaData->mAttachments.end();
5833 ++it)
5834 {
5835 if ((*it)->getControllerName() == aName)
5836 return setError(VBOX_E_OBJECT_IN_USE,
5837 tr("Storage controller named '%ls' has still devices attached"),
5838 aName);
5839 }
5840
5841 /* We can remove it now. */
5842 setModified(IsModified_Storage);
5843 mStorageControllers.backup();
5844
5845 ctrl->unshare();
5846
5847 mStorageControllers->remove(ctrl);
5848
5849 /* inform the direct session if any */
5850 alock.release();
5851 onStorageControllerChange();
5852
5853 return S_OK;
5854}
5855
5856STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
5857 ULONG *puOriginX,
5858 ULONG *puOriginY,
5859 ULONG *puWidth,
5860 ULONG *puHeight,
5861 BOOL *pfEnabled)
5862{
5863 LogFlowThisFunc(("\n"));
5864
5865 CheckComArgNotNull(puOriginX);
5866 CheckComArgNotNull(puOriginY);
5867 CheckComArgNotNull(puWidth);
5868 CheckComArgNotNull(puHeight);
5869 CheckComArgNotNull(pfEnabled);
5870
5871 uint32_t u32OriginX= 0;
5872 uint32_t u32OriginY= 0;
5873 uint32_t u32Width = 0;
5874 uint32_t u32Height = 0;
5875 uint16_t u16Flags = 0;
5876
5877 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
5878 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
5879 if (RT_FAILURE(vrc))
5880 {
5881#ifdef RT_OS_WINDOWS
5882 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
5883 * This works with XPCOM. But Windows COM sets all output parameters to zero.
5884 * So just assign fEnable to TRUE again.
5885 * The right fix would be to change GUI API wrappers to make sure that parameters
5886 * are changed only if API succeeds.
5887 */
5888 *pfEnabled = TRUE;
5889#endif
5890 return setError(VBOX_E_IPRT_ERROR,
5891 tr("Saved guest size is not available (%Rrc)"),
5892 vrc);
5893 }
5894
5895 *puOriginX = u32OriginX;
5896 *puOriginY = u32OriginY;
5897 *puWidth = u32Width;
5898 *puHeight = u32Height;
5899 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
5900
5901 return S_OK;
5902}
5903
5904STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
5905{
5906 LogFlowThisFunc(("\n"));
5907
5908 CheckComArgNotNull(aSize);
5909 CheckComArgNotNull(aWidth);
5910 CheckComArgNotNull(aHeight);
5911
5912 if (aScreenId != 0)
5913 return E_NOTIMPL;
5914
5915 AutoCaller autoCaller(this);
5916 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5917
5918 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5919
5920 uint8_t *pu8Data = NULL;
5921 uint32_t cbData = 0;
5922 uint32_t u32Width = 0;
5923 uint32_t u32Height = 0;
5924
5925 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
5926
5927 if (RT_FAILURE(vrc))
5928 return setError(VBOX_E_IPRT_ERROR,
5929 tr("Saved screenshot data is not available (%Rrc)"),
5930 vrc);
5931
5932 *aSize = cbData;
5933 *aWidth = u32Width;
5934 *aHeight = u32Height;
5935
5936 freeSavedDisplayScreenshot(pu8Data);
5937
5938 return S_OK;
5939}
5940
5941STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
5942{
5943 LogFlowThisFunc(("\n"));
5944
5945 CheckComArgNotNull(aWidth);
5946 CheckComArgNotNull(aHeight);
5947 CheckComArgOutSafeArrayPointerValid(aData);
5948
5949 if (aScreenId != 0)
5950 return E_NOTIMPL;
5951
5952 AutoCaller autoCaller(this);
5953 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5954
5955 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5956
5957 uint8_t *pu8Data = NULL;
5958 uint32_t cbData = 0;
5959 uint32_t u32Width = 0;
5960 uint32_t u32Height = 0;
5961
5962 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
5963
5964 if (RT_FAILURE(vrc))
5965 return setError(VBOX_E_IPRT_ERROR,
5966 tr("Saved screenshot data is not available (%Rrc)"),
5967 vrc);
5968
5969 *aWidth = u32Width;
5970 *aHeight = u32Height;
5971
5972 com::SafeArray<BYTE> bitmap(cbData);
5973 /* Convert pixels to format expected by the API caller. */
5974 if (aBGR)
5975 {
5976 /* [0] B, [1] G, [2] R, [3] A. */
5977 for (unsigned i = 0; i < cbData; i += 4)
5978 {
5979 bitmap[i] = pu8Data[i];
5980 bitmap[i + 1] = pu8Data[i + 1];
5981 bitmap[i + 2] = pu8Data[i + 2];
5982 bitmap[i + 3] = 0xff;
5983 }
5984 }
5985 else
5986 {
5987 /* [0] R, [1] G, [2] B, [3] A. */
5988 for (unsigned i = 0; i < cbData; i += 4)
5989 {
5990 bitmap[i] = pu8Data[i + 2];
5991 bitmap[i + 1] = pu8Data[i + 1];
5992 bitmap[i + 2] = pu8Data[i];
5993 bitmap[i + 3] = 0xff;
5994 }
5995 }
5996 bitmap.detachTo(ComSafeArrayOutArg(aData));
5997
5998 freeSavedDisplayScreenshot(pu8Data);
5999
6000 return S_OK;
6001}
6002
6003
6004STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6005{
6006 LogFlowThisFunc(("\n"));
6007
6008 CheckComArgNotNull(aWidth);
6009 CheckComArgNotNull(aHeight);
6010 CheckComArgOutSafeArrayPointerValid(aData);
6011
6012 if (aScreenId != 0)
6013 return E_NOTIMPL;
6014
6015 AutoCaller autoCaller(this);
6016 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6017
6018 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6019
6020 uint8_t *pu8Data = NULL;
6021 uint32_t cbData = 0;
6022 uint32_t u32Width = 0;
6023 uint32_t u32Height = 0;
6024
6025 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6026
6027 if (RT_FAILURE(vrc))
6028 return setError(VBOX_E_IPRT_ERROR,
6029 tr("Saved screenshot data is not available (%Rrc)"),
6030 vrc);
6031
6032 *aWidth = u32Width;
6033 *aHeight = u32Height;
6034
6035 HRESULT rc = S_OK;
6036 uint8_t *pu8PNG = NULL;
6037 uint32_t cbPNG = 0;
6038 uint32_t cxPNG = 0;
6039 uint32_t cyPNG = 0;
6040
6041 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6042
6043 if (RT_SUCCESS(vrc))
6044 {
6045 com::SafeArray<BYTE> screenData(cbPNG);
6046 screenData.initFrom(pu8PNG, cbPNG);
6047 if (pu8PNG)
6048 RTMemFree(pu8PNG);
6049 screenData.detachTo(ComSafeArrayOutArg(aData));
6050 }
6051 else
6052 {
6053 if (pu8PNG)
6054 RTMemFree(pu8PNG);
6055 return setError(VBOX_E_IPRT_ERROR,
6056 tr("Could not convert screenshot to PNG (%Rrc)"),
6057 vrc);
6058 }
6059
6060 freeSavedDisplayScreenshot(pu8Data);
6061
6062 return rc;
6063}
6064
6065STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6066{
6067 LogFlowThisFunc(("\n"));
6068
6069 CheckComArgNotNull(aSize);
6070 CheckComArgNotNull(aWidth);
6071 CheckComArgNotNull(aHeight);
6072
6073 if (aScreenId != 0)
6074 return E_NOTIMPL;
6075
6076 AutoCaller autoCaller(this);
6077 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6078
6079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6080
6081 uint8_t *pu8Data = NULL;
6082 uint32_t cbData = 0;
6083 uint32_t u32Width = 0;
6084 uint32_t u32Height = 0;
6085
6086 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6087
6088 if (RT_FAILURE(vrc))
6089 return setError(VBOX_E_IPRT_ERROR,
6090 tr("Saved screenshot data is not available (%Rrc)"),
6091 vrc);
6092
6093 *aSize = cbData;
6094 *aWidth = u32Width;
6095 *aHeight = u32Height;
6096
6097 freeSavedDisplayScreenshot(pu8Data);
6098
6099 return S_OK;
6100}
6101
6102STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6103{
6104 LogFlowThisFunc(("\n"));
6105
6106 CheckComArgNotNull(aWidth);
6107 CheckComArgNotNull(aHeight);
6108 CheckComArgOutSafeArrayPointerValid(aData);
6109
6110 if (aScreenId != 0)
6111 return E_NOTIMPL;
6112
6113 AutoCaller autoCaller(this);
6114 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6115
6116 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6117
6118 uint8_t *pu8Data = NULL;
6119 uint32_t cbData = 0;
6120 uint32_t u32Width = 0;
6121 uint32_t u32Height = 0;
6122
6123 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6124
6125 if (RT_FAILURE(vrc))
6126 return setError(VBOX_E_IPRT_ERROR,
6127 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6128 vrc);
6129
6130 *aWidth = u32Width;
6131 *aHeight = u32Height;
6132
6133 com::SafeArray<BYTE> png(cbData);
6134 png.initFrom(pu8Data, cbData);
6135 png.detachTo(ComSafeArrayOutArg(aData));
6136
6137 freeSavedDisplayScreenshot(pu8Data);
6138
6139 return S_OK;
6140}
6141
6142STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6143{
6144 HRESULT rc = S_OK;
6145 LogFlowThisFunc(("\n"));
6146
6147 AutoCaller autoCaller(this);
6148 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6149
6150 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6151
6152 if (!mHWData->mCPUHotPlugEnabled)
6153 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6154
6155 if (aCpu >= mHWData->mCPUCount)
6156 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6157
6158 if (mHWData->mCPUAttached[aCpu])
6159 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6160
6161 alock.release();
6162 rc = onCPUChange(aCpu, false);
6163 alock.acquire();
6164 if (FAILED(rc)) return rc;
6165
6166 setModified(IsModified_MachineData);
6167 mHWData.backup();
6168 mHWData->mCPUAttached[aCpu] = true;
6169
6170 /* Save settings if online */
6171 if (Global::IsOnline(mData->mMachineState))
6172 saveSettings(NULL);
6173
6174 return S_OK;
6175}
6176
6177STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6178{
6179 HRESULT rc = S_OK;
6180 LogFlowThisFunc(("\n"));
6181
6182 AutoCaller autoCaller(this);
6183 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6184
6185 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6186
6187 if (!mHWData->mCPUHotPlugEnabled)
6188 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6189
6190 if (aCpu >= SchemaDefs::MaxCPUCount)
6191 return setError(E_INVALIDARG,
6192 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6193 SchemaDefs::MaxCPUCount);
6194
6195 if (!mHWData->mCPUAttached[aCpu])
6196 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6197
6198 /* CPU 0 can't be detached */
6199 if (aCpu == 0)
6200 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6201
6202 alock.release();
6203 rc = onCPUChange(aCpu, true);
6204 alock.acquire();
6205 if (FAILED(rc)) return rc;
6206
6207 setModified(IsModified_MachineData);
6208 mHWData.backup();
6209 mHWData->mCPUAttached[aCpu] = false;
6210
6211 /* Save settings if online */
6212 if (Global::IsOnline(mData->mMachineState))
6213 saveSettings(NULL);
6214
6215 return S_OK;
6216}
6217
6218STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6219{
6220 LogFlowThisFunc(("\n"));
6221
6222 CheckComArgNotNull(aCpuAttached);
6223
6224 *aCpuAttached = false;
6225
6226 AutoCaller autoCaller(this);
6227 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6228
6229 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6230
6231 /* If hotplug is enabled the CPU is always enabled. */
6232 if (!mHWData->mCPUHotPlugEnabled)
6233 {
6234 if (aCpu < mHWData->mCPUCount)
6235 *aCpuAttached = true;
6236 }
6237 else
6238 {
6239 if (aCpu < SchemaDefs::MaxCPUCount)
6240 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6241 }
6242
6243 return S_OK;
6244}
6245
6246STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6247{
6248 CheckComArgOutPointerValid(aName);
6249
6250 AutoCaller autoCaller(this);
6251 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6252
6253 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6254
6255 Utf8Str log = queryLogFilename(aIdx);
6256 if (!RTFileExists(log.c_str()))
6257 log.setNull();
6258 log.cloneTo(aName);
6259
6260 return S_OK;
6261}
6262
6263STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6264{
6265 LogFlowThisFunc(("\n"));
6266 CheckComArgOutSafeArrayPointerValid(aData);
6267 if (aSize < 0)
6268 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6269
6270 AutoCaller autoCaller(this);
6271 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6272
6273 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6274
6275 HRESULT rc = S_OK;
6276 Utf8Str log = queryLogFilename(aIdx);
6277
6278 /* do not unnecessarily hold the lock while doing something which does
6279 * not need the lock and potentially takes a long time. */
6280 alock.release();
6281
6282 /* Limit the chunk size to 32K for now, as that gives better performance
6283 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6284 * One byte expands to approx. 25 bytes of breathtaking XML. */
6285 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6286 com::SafeArray<BYTE> logData(cbData);
6287
6288 RTFILE LogFile;
6289 int vrc = RTFileOpen(&LogFile, log.c_str(),
6290 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6291 if (RT_SUCCESS(vrc))
6292 {
6293 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6294 if (RT_SUCCESS(vrc))
6295 logData.resize(cbData);
6296 else
6297 rc = setError(VBOX_E_IPRT_ERROR,
6298 tr("Could not read log file '%s' (%Rrc)"),
6299 log.c_str(), vrc);
6300 RTFileClose(LogFile);
6301 }
6302 else
6303 rc = setError(VBOX_E_IPRT_ERROR,
6304 tr("Could not open log file '%s' (%Rrc)"),
6305 log.c_str(), vrc);
6306
6307 if (FAILED(rc))
6308 logData.resize(0);
6309 logData.detachTo(ComSafeArrayOutArg(aData));
6310
6311 return rc;
6312}
6313
6314
6315/**
6316 * Currently this method doesn't attach device to the running VM,
6317 * just makes sure it's plugged on next VM start.
6318 */
6319STDMETHODIMP Machine::AttachHostPciDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6320{
6321 AutoCaller autoCaller(this);
6322 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6323
6324 // lock scope
6325 {
6326 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6327
6328 HRESULT rc = checkStateDependency(MutableStateDep);
6329 if (FAILED(rc)) return rc;
6330
6331 ChipsetType_T aChipset = ChipsetType_PIIX3;
6332 COMGETTER(ChipsetType)(&aChipset);
6333
6334 if (aChipset != ChipsetType_ICH9)
6335 {
6336 return setError(E_INVALIDARG,
6337 tr("Host PCI attachment only supported with ICH9 chipset"));
6338 }
6339
6340 // check if device with this host PCI address already attached
6341 for (HWData::PciDeviceAssignmentList::iterator it = mHWData->mPciDeviceAssignments.begin();
6342 it != mHWData->mPciDeviceAssignments.end();
6343 ++it)
6344 {
6345 LONG iHostAddress = -1;
6346 ComPtr<PciDeviceAttachment> pAttach;
6347 pAttach = *it;
6348 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6349 if (iHostAddress == hostAddress)
6350 return setError(E_INVALIDARG,
6351 tr("Device with host PCI address already attached to this VM"));
6352 }
6353
6354 ComObjPtr<PciDeviceAttachment> pda;
6355 char name[32];
6356
6357 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6358 Bstr bname(name);
6359 pda.createObject();
6360 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
6361 setModified(IsModified_MachineData);
6362 mHWData.backup();
6363 mHWData->mPciDeviceAssignments.push_back(pda);
6364 }
6365
6366 return S_OK;
6367}
6368
6369/**
6370 * Currently this method doesn't detach device from the running VM,
6371 * just makes sure it's not plugged on next VM start.
6372 */
6373STDMETHODIMP Machine::DetachHostPciDevice(LONG hostAddress)
6374{
6375 AutoCaller autoCaller(this);
6376 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6377
6378 ComObjPtr<PciDeviceAttachment> pAttach;
6379 bool fRemoved = false;
6380 HRESULT rc;
6381
6382 // lock scope
6383 {
6384 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6385
6386 rc = checkStateDependency(MutableStateDep);
6387 if (FAILED(rc)) return rc;
6388
6389 for (HWData::PciDeviceAssignmentList::iterator it = mHWData->mPciDeviceAssignments.begin();
6390 it != mHWData->mPciDeviceAssignments.end();
6391 ++it)
6392 {
6393 LONG iHostAddress = -1;
6394 pAttach = *it;
6395 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6396 if (iHostAddress != -1 && iHostAddress == hostAddress)
6397 {
6398 setModified(IsModified_MachineData);
6399 mHWData.backup();
6400 mHWData->mPciDeviceAssignments.remove(pAttach);
6401 fRemoved = true;
6402 break;
6403 }
6404 }
6405 }
6406
6407
6408 /* Fire event outside of the lock */
6409 if (fRemoved)
6410 {
6411 Assert(!pAttach.isNull());
6412 ComPtr<IEventSource> es;
6413 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6414 Assert(SUCCEEDED(rc));
6415 Bstr mid;
6416 rc = this->COMGETTER(Id)(mid.asOutParam());
6417 Assert(SUCCEEDED(rc));
6418 fireHostPciDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6419 }
6420
6421 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6422 tr("No host PCI device %08x attached"),
6423 hostAddress
6424 );
6425}
6426
6427STDMETHODIMP Machine::COMGETTER(PciDeviceAssignments)(ComSafeArrayOut(IPciDeviceAttachment *, aAssignments))
6428{
6429 CheckComArgOutSafeArrayPointerValid(aAssignments);
6430
6431 AutoCaller autoCaller(this);
6432 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6433
6434 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6435
6436 SafeIfaceArray<IPciDeviceAttachment> assignments(mHWData->mPciDeviceAssignments);
6437 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
6438
6439 return S_OK;
6440}
6441
6442STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
6443{
6444 CheckComArgOutPointerValid(aBandwidthControl);
6445
6446 AutoCaller autoCaller(this);
6447 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6448
6449 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
6450
6451 return S_OK;
6452}
6453
6454STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
6455{
6456 CheckComArgOutPointerValid(pfEnabled);
6457 AutoCaller autoCaller(this);
6458 HRESULT hrc = autoCaller.rc();
6459 if (SUCCEEDED(hrc))
6460 {
6461 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6462 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
6463 }
6464 return hrc;
6465}
6466
6467STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
6468{
6469 AutoCaller autoCaller(this);
6470 HRESULT hrc = autoCaller.rc();
6471 if (SUCCEEDED(hrc))
6472 {
6473 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6474 hrc = checkStateDependency(MutableStateDep);
6475 if (SUCCEEDED(hrc))
6476 {
6477 hrc = mHWData.backupEx();
6478 if (SUCCEEDED(hrc))
6479 {
6480 setModified(IsModified_MachineData);
6481 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
6482 }
6483 }
6484 }
6485 return hrc;
6486}
6487
6488STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
6489{
6490 CheckComArgOutPointerValid(pbstrConfig);
6491 AutoCaller autoCaller(this);
6492 HRESULT hrc = autoCaller.rc();
6493 if (SUCCEEDED(hrc))
6494 {
6495 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6496 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
6497 }
6498 return hrc;
6499}
6500
6501STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
6502{
6503 CheckComArgStr(bstrConfig);
6504 AutoCaller autoCaller(this);
6505 HRESULT hrc = autoCaller.rc();
6506 if (SUCCEEDED(hrc))
6507 {
6508 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6509 hrc = checkStateDependency(MutableStateDep);
6510 if (SUCCEEDED(hrc))
6511 {
6512 hrc = mHWData.backupEx();
6513 if (SUCCEEDED(hrc))
6514 {
6515 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
6516 if (SUCCEEDED(hrc))
6517 setModified(IsModified_MachineData);
6518 }
6519 }
6520 }
6521 return hrc;
6522
6523}
6524
6525STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
6526{
6527 CheckComArgOutPointerValid(pfAllow);
6528 AutoCaller autoCaller(this);
6529 HRESULT hrc = autoCaller.rc();
6530 if (SUCCEEDED(hrc))
6531 {
6532 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6533 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
6534 }
6535 return hrc;
6536}
6537
6538STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
6539{
6540 AutoCaller autoCaller(this);
6541 HRESULT hrc = autoCaller.rc();
6542 if (SUCCEEDED(hrc))
6543 {
6544 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6545 hrc = checkStateDependency(MutableStateDep);
6546 if (SUCCEEDED(hrc))
6547 {
6548 hrc = mHWData.backupEx();
6549 if (SUCCEEDED(hrc))
6550 {
6551 setModified(IsModified_MachineData);
6552 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
6553 }
6554 }
6555 }
6556 return hrc;
6557}
6558
6559STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
6560{
6561 CheckComArgOutPointerValid(pfEnabled);
6562 AutoCaller autoCaller(this);
6563 HRESULT hrc = autoCaller.rc();
6564 if (SUCCEEDED(hrc))
6565 {
6566 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6567 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
6568 }
6569 return hrc;
6570}
6571
6572STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
6573{
6574 AutoCaller autoCaller(this);
6575 HRESULT hrc = autoCaller.rc();
6576 if (SUCCEEDED(hrc))
6577 {
6578 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6579 hrc = checkStateDependency(MutableStateDep);
6580 if ( SUCCEEDED(hrc)
6581 && mHWData->mAutostart.fAutostartEnabled != fEnabled)
6582 {
6583 AutostartDb *autostartDb = mParent->getAutostartDb();
6584 int vrc;
6585
6586 if (fEnabled)
6587 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6588 else
6589 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6590
6591 if (RT_SUCCESS(vrc))
6592 {
6593 hrc = mHWData.backupEx();
6594 if (SUCCEEDED(hrc))
6595 {
6596 setModified(IsModified_MachineData);
6597 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
6598 }
6599 }
6600 else if (vrc == VERR_NOT_SUPPORTED)
6601 hrc = setError(VBOX_E_NOT_SUPPORTED,
6602 tr("The VM autostart feature is not supported on this platform"));
6603 else
6604 hrc = setError(E_UNEXPECTED,
6605 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6606 fEnabled ? "Adding" : "Removing",
6607 mUserData->s.strName.c_str(), vrc);
6608 }
6609 }
6610 return hrc;
6611}
6612
6613STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
6614{
6615 CheckComArgOutPointerValid(puDelay);
6616 AutoCaller autoCaller(this);
6617 HRESULT hrc = autoCaller.rc();
6618 if (SUCCEEDED(hrc))
6619 {
6620 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6621 *puDelay = mHWData->mAutostart.uAutostartDelay;
6622 }
6623 return hrc;
6624}
6625
6626STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
6627{
6628 AutoCaller autoCaller(this);
6629 HRESULT hrc = autoCaller.rc();
6630 if (SUCCEEDED(hrc))
6631 {
6632 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6633 hrc = checkStateDependency(MutableStateDep);
6634 if (SUCCEEDED(hrc))
6635 {
6636 hrc = mHWData.backupEx();
6637 if (SUCCEEDED(hrc))
6638 {
6639 setModified(IsModified_MachineData);
6640 mHWData->mAutostart.uAutostartDelay = uDelay;
6641 }
6642 }
6643 }
6644 return hrc;
6645}
6646
6647STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
6648{
6649 CheckComArgOutPointerValid(penmAutostopType);
6650 AutoCaller autoCaller(this);
6651 HRESULT hrc = autoCaller.rc();
6652 if (SUCCEEDED(hrc))
6653 {
6654 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6655 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
6656 }
6657 return hrc;
6658}
6659
6660STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
6661{
6662 AutoCaller autoCaller(this);
6663 HRESULT hrc = autoCaller.rc();
6664 if (SUCCEEDED(hrc))
6665 {
6666 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6667 hrc = checkStateDependency(MutableStateDep);
6668 if ( SUCCEEDED(hrc)
6669 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
6670 {
6671 AutostartDb *autostartDb = mParent->getAutostartDb();
6672 int vrc;
6673
6674 if (enmAutostopType != AutostopType_Disabled)
6675 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6676 else
6677 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6678
6679 if (RT_SUCCESS(vrc))
6680 {
6681 hrc = mHWData.backupEx();
6682 if (SUCCEEDED(hrc))
6683 {
6684 setModified(IsModified_MachineData);
6685 mHWData->mAutostart.enmAutostopType = enmAutostopType;
6686 }
6687 }
6688 else if (vrc == VERR_NOT_SUPPORTED)
6689 hrc = setError(VBOX_E_NOT_SUPPORTED,
6690 tr("The VM autostop feature is not supported on this platform"));
6691 else
6692 hrc = setError(E_UNEXPECTED,
6693 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6694 fEnabled ? "Adding" : "Removing",
6695 mUserData->s.strName.c_str(), vrc);
6696 }
6697 }
6698 return hrc;
6699}
6700
6701
6702STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
6703{
6704 LogFlowFuncEnter();
6705
6706 CheckComArgNotNull(pTarget);
6707 CheckComArgOutPointerValid(pProgress);
6708
6709 /* Convert the options. */
6710 RTCList<CloneOptions_T> optList;
6711 if (options != NULL)
6712 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
6713
6714 if (optList.contains(CloneOptions_Link))
6715 {
6716 if (!isSnapshotMachine())
6717 return setError(E_INVALIDARG,
6718 tr("Linked clone can only be created from a snapshot"));
6719 if (mode != CloneMode_MachineState)
6720 return setError(E_INVALIDARG,
6721 tr("Linked clone can only be created for a single machine state"));
6722 }
6723 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6724
6725 AutoCaller autoCaller(this);
6726 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6727
6728
6729 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
6730
6731 HRESULT rc = pWorker->start(pProgress);
6732
6733 LogFlowFuncLeave();
6734
6735 return rc;
6736}
6737
6738// public methods for internal purposes
6739/////////////////////////////////////////////////////////////////////////////
6740
6741/**
6742 * Adds the given IsModified_* flag to the dirty flags of the machine.
6743 * This must be called either during loadSettings or under the machine write lock.
6744 * @param fl
6745 */
6746void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
6747{
6748 mData->flModifications |= fl;
6749 if (fAllowStateModification && isStateModificationAllowed())
6750 mData->mCurrentStateModified = true;
6751}
6752
6753/**
6754 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
6755 * care of the write locking.
6756 *
6757 * @param fModifications The flag to add.
6758 */
6759void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
6760{
6761 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6762 setModified(fModification, fAllowStateModification);
6763}
6764
6765/**
6766 * Saves the registry entry of this machine to the given configuration node.
6767 *
6768 * @param aEntryNode Node to save the registry entry to.
6769 *
6770 * @note locks this object for reading.
6771 */
6772HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
6773{
6774 AutoLimitedCaller autoCaller(this);
6775 AssertComRCReturnRC(autoCaller.rc());
6776
6777 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6778
6779 data.uuid = mData->mUuid;
6780 data.strSettingsFile = mData->m_strConfigFile;
6781
6782 return S_OK;
6783}
6784
6785/**
6786 * Calculates the absolute path of the given path taking the directory of the
6787 * machine settings file as the current directory.
6788 *
6789 * @param aPath Path to calculate the absolute path for.
6790 * @param aResult Where to put the result (used only on success, can be the
6791 * same Utf8Str instance as passed in @a aPath).
6792 * @return IPRT result.
6793 *
6794 * @note Locks this object for reading.
6795 */
6796int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
6797{
6798 AutoCaller autoCaller(this);
6799 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
6800
6801 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6802
6803 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
6804
6805 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
6806
6807 strSettingsDir.stripFilename();
6808 char folder[RTPATH_MAX];
6809 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
6810 if (RT_SUCCESS(vrc))
6811 aResult = folder;
6812
6813 return vrc;
6814}
6815
6816/**
6817 * Copies strSource to strTarget, making it relative to the machine folder
6818 * if it is a subdirectory thereof, or simply copying it otherwise.
6819 *
6820 * @param strSource Path to evaluate and copy.
6821 * @param strTarget Buffer to receive target path.
6822 *
6823 * @note Locks this object for reading.
6824 */
6825void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
6826 Utf8Str &strTarget)
6827{
6828 AutoCaller autoCaller(this);
6829 AssertComRCReturn(autoCaller.rc(), (void)0);
6830
6831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6832
6833 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
6834 // use strTarget as a temporary buffer to hold the machine settings dir
6835 strTarget = mData->m_strConfigFileFull;
6836 strTarget.stripFilename();
6837 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
6838 {
6839 // is relative: then append what's left
6840 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
6841 // for empty paths (only possible for subdirs) use "." to avoid
6842 // triggering default settings for not present config attributes.
6843 if (strTarget.isEmpty())
6844 strTarget = ".";
6845 }
6846 else
6847 // is not relative: then overwrite
6848 strTarget = strSource;
6849}
6850
6851/**
6852 * Returns the full path to the machine's log folder in the
6853 * \a aLogFolder argument.
6854 */
6855void Machine::getLogFolder(Utf8Str &aLogFolder)
6856{
6857 AutoCaller autoCaller(this);
6858 AssertComRCReturnVoid(autoCaller.rc());
6859
6860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6861
6862 char szTmp[RTPATH_MAX];
6863 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
6864 if (RT_SUCCESS(vrc))
6865 {
6866 if (szTmp[0] && !mUserData.isNull())
6867 {
6868 char szTmp2[RTPATH_MAX];
6869 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
6870 if (RT_SUCCESS(vrc))
6871 aLogFolder = BstrFmt("%s%c%s",
6872 szTmp2,
6873 RTPATH_DELIMITER,
6874 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
6875 }
6876 else
6877 vrc = VERR_PATH_IS_RELATIVE;
6878 }
6879
6880 if (RT_FAILURE(vrc))
6881 {
6882 // fallback if VBOX_USER_LOGHOME is not set or invalid
6883 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
6884 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
6885 aLogFolder.append(RTPATH_DELIMITER);
6886 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
6887 }
6888}
6889
6890/**
6891 * Returns the full path to the machine's log file for an given index.
6892 */
6893Utf8Str Machine::queryLogFilename(ULONG idx)
6894{
6895 Utf8Str logFolder;
6896 getLogFolder(logFolder);
6897 Assert(logFolder.length());
6898 Utf8Str log;
6899 if (idx == 0)
6900 log = Utf8StrFmt("%s%cVBox.log",
6901 logFolder.c_str(), RTPATH_DELIMITER);
6902 else
6903 log = Utf8StrFmt("%s%cVBox.log.%d",
6904 logFolder.c_str(), RTPATH_DELIMITER, idx);
6905 return log;
6906}
6907
6908/**
6909 * Composes a unique saved state filename based on the current system time. The filename is
6910 * granular to the second so this will work so long as no more than one snapshot is taken on
6911 * a machine per second.
6912 *
6913 * Before version 4.1, we used this formula for saved state files:
6914 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
6915 * which no longer works because saved state files can now be shared between the saved state of the
6916 * "saved" machine and an online snapshot, and the following would cause problems:
6917 * 1) save machine
6918 * 2) create online snapshot from that machine state --> reusing saved state file
6919 * 3) save machine again --> filename would be reused, breaking the online snapshot
6920 *
6921 * So instead we now use a timestamp.
6922 *
6923 * @param str
6924 */
6925void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
6926{
6927 AutoCaller autoCaller(this);
6928 AssertComRCReturnVoid(autoCaller.rc());
6929
6930 {
6931 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6932 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
6933 }
6934
6935 RTTIMESPEC ts;
6936 RTTimeNow(&ts);
6937 RTTIME time;
6938 RTTimeExplode(&time, &ts);
6939
6940 strStateFilePath += RTPATH_DELIMITER;
6941 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
6942 time.i32Year, time.u8Month, time.u8MonthDay,
6943 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
6944}
6945
6946/**
6947 * @note Locks this object for writing, calls the client process
6948 * (inside the lock).
6949 */
6950HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
6951 const Utf8Str &strType,
6952 const Utf8Str &strEnvironment,
6953 ProgressProxy *aProgress)
6954{
6955 LogFlowThisFuncEnter();
6956
6957 AssertReturn(aControl, E_FAIL);
6958 AssertReturn(aProgress, E_FAIL);
6959
6960 AutoCaller autoCaller(this);
6961 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6962
6963 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6964
6965 if (!mData->mRegistered)
6966 return setError(E_UNEXPECTED,
6967 tr("The machine '%s' is not registered"),
6968 mUserData->s.strName.c_str());
6969
6970 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
6971
6972 if ( mData->mSession.mState == SessionState_Locked
6973 || mData->mSession.mState == SessionState_Spawning
6974 || mData->mSession.mState == SessionState_Unlocking)
6975 return setError(VBOX_E_INVALID_OBJECT_STATE,
6976 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
6977 mUserData->s.strName.c_str());
6978
6979 /* may not be busy */
6980 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
6981
6982 /* get the path to the executable */
6983 char szPath[RTPATH_MAX];
6984 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
6985 size_t sz = strlen(szPath);
6986 szPath[sz++] = RTPATH_DELIMITER;
6987 szPath[sz] = 0;
6988 char *cmd = szPath + sz;
6989 sz = RTPATH_MAX - sz;
6990
6991 int vrc = VINF_SUCCESS;
6992 RTPROCESS pid = NIL_RTPROCESS;
6993
6994 RTENV env = RTENV_DEFAULT;
6995
6996 if (!strEnvironment.isEmpty())
6997 {
6998 char *newEnvStr = NULL;
6999
7000 do
7001 {
7002 /* clone the current environment */
7003 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7004 AssertRCBreakStmt(vrc2, vrc = vrc2);
7005
7006 newEnvStr = RTStrDup(strEnvironment.c_str());
7007 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7008
7009 /* put new variables to the environment
7010 * (ignore empty variable names here since RTEnv API
7011 * intentionally doesn't do that) */
7012 char *var = newEnvStr;
7013 for (char *p = newEnvStr; *p; ++p)
7014 {
7015 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7016 {
7017 *p = '\0';
7018 if (*var)
7019 {
7020 char *val = strchr(var, '=');
7021 if (val)
7022 {
7023 *val++ = '\0';
7024 vrc2 = RTEnvSetEx(env, var, val);
7025 }
7026 else
7027 vrc2 = RTEnvUnsetEx(env, var);
7028 if (RT_FAILURE(vrc2))
7029 break;
7030 }
7031 var = p + 1;
7032 }
7033 }
7034 if (RT_SUCCESS(vrc2) && *var)
7035 vrc2 = RTEnvPutEx(env, var);
7036
7037 AssertRCBreakStmt(vrc2, vrc = vrc2);
7038 }
7039 while (0);
7040
7041 if (newEnvStr != NULL)
7042 RTStrFree(newEnvStr);
7043 }
7044
7045 /* Qt is default */
7046#ifdef VBOX_WITH_QTGUI
7047 if (strType == "gui" || strType == "GUI/Qt")
7048 {
7049# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7050 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7051# else
7052 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7053# endif
7054 Assert(sz >= sizeof(VirtualBox_exe));
7055 strcpy(cmd, VirtualBox_exe);
7056
7057 Utf8Str idStr = mData->mUuid.toString();
7058 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7059 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7060 }
7061#else /* !VBOX_WITH_QTGUI */
7062 if (0)
7063 ;
7064#endif /* VBOX_WITH_QTGUI */
7065
7066 else
7067
7068#ifdef VBOX_WITH_VBOXSDL
7069 if (strType == "sdl" || strType == "GUI/SDL")
7070 {
7071 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7072 Assert(sz >= sizeof(VBoxSDL_exe));
7073 strcpy(cmd, VBoxSDL_exe);
7074
7075 Utf8Str idStr = mData->mUuid.toString();
7076 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7077 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7078 }
7079#else /* !VBOX_WITH_VBOXSDL */
7080 if (0)
7081 ;
7082#endif /* !VBOX_WITH_VBOXSDL */
7083
7084 else
7085
7086#ifdef VBOX_WITH_HEADLESS
7087 if ( strType == "headless"
7088 || strType == "capture"
7089 || strType == "vrdp" /* Deprecated. Same as headless. */
7090 )
7091 {
7092 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7093 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7094 * and a VM works even if the server has not been installed.
7095 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7096 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7097 * differently in 4.0 and 3.x.
7098 */
7099 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7100 Assert(sz >= sizeof(VBoxHeadless_exe));
7101 strcpy(cmd, VBoxHeadless_exe);
7102
7103 Utf8Str idStr = mData->mUuid.toString();
7104 /* Leave space for "--capture" arg. */
7105 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7106 "--startvm", idStr.c_str(),
7107 "--vrde", "config",
7108 0, /* For "--capture". */
7109 0 };
7110 if (strType == "capture")
7111 {
7112 unsigned pos = RT_ELEMENTS(args) - 2;
7113 args[pos] = "--capture";
7114 }
7115 vrc = RTProcCreate(szPath, args, env,
7116#ifdef RT_OS_WINDOWS
7117 RTPROC_FLAGS_NO_WINDOW
7118#else
7119 0
7120#endif
7121 , &pid);
7122 }
7123#else /* !VBOX_WITH_HEADLESS */
7124 if (0)
7125 ;
7126#endif /* !VBOX_WITH_HEADLESS */
7127 else
7128 {
7129 RTEnvDestroy(env);
7130 return setError(E_INVALIDARG,
7131 tr("Invalid session type: '%s'"),
7132 strType.c_str());
7133 }
7134
7135 RTEnvDestroy(env);
7136
7137 if (RT_FAILURE(vrc))
7138 return setError(VBOX_E_IPRT_ERROR,
7139 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7140 mUserData->s.strName.c_str(), vrc);
7141
7142 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7143
7144 /*
7145 * Note that we don't release the lock here before calling the client,
7146 * because it doesn't need to call us back if called with a NULL argument.
7147 * Releasing the lock here is dangerous because we didn't prepare the
7148 * launch data yet, but the client we've just started may happen to be
7149 * too fast and call openSession() that will fail (because of PID, etc.),
7150 * so that the Machine will never get out of the Spawning session state.
7151 */
7152
7153 /* inform the session that it will be a remote one */
7154 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7155 HRESULT rc = aControl->AssignMachine(NULL);
7156 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7157
7158 if (FAILED(rc))
7159 {
7160 /* restore the session state */
7161 mData->mSession.mState = SessionState_Unlocked;
7162 /* The failure may occur w/o any error info (from RPC), so provide one */
7163 return setError(VBOX_E_VM_ERROR,
7164 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7165 }
7166
7167 /* attach launch data to the machine */
7168 Assert(mData->mSession.mPid == NIL_RTPROCESS);
7169 mData->mSession.mRemoteControls.push_back(aControl);
7170 mData->mSession.mProgress = aProgress;
7171 mData->mSession.mPid = pid;
7172 mData->mSession.mState = SessionState_Spawning;
7173 mData->mSession.mType = strType;
7174
7175 LogFlowThisFuncLeave();
7176 return S_OK;
7177}
7178
7179/**
7180 * Returns @c true if the given machine has an open direct session and returns
7181 * the session machine instance and additional session data (on some platforms)
7182 * if so.
7183 *
7184 * Note that when the method returns @c false, the arguments remain unchanged.
7185 *
7186 * @param aMachine Session machine object.
7187 * @param aControl Direct session control object (optional).
7188 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7189 *
7190 * @note locks this object for reading.
7191 */
7192#if defined(RT_OS_WINDOWS)
7193bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7194 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7195 HANDLE *aIPCSem /*= NULL*/,
7196 bool aAllowClosing /*= false*/)
7197#elif defined(RT_OS_OS2)
7198bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7199 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7200 HMTX *aIPCSem /*= NULL*/,
7201 bool aAllowClosing /*= false*/)
7202#else
7203bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7204 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7205 bool aAllowClosing /*= false*/)
7206#endif
7207{
7208 AutoLimitedCaller autoCaller(this);
7209 AssertComRCReturn(autoCaller.rc(), false);
7210
7211 /* just return false for inaccessible machines */
7212 if (autoCaller.state() != Ready)
7213 return false;
7214
7215 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7216
7217 if ( mData->mSession.mState == SessionState_Locked
7218 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7219 )
7220 {
7221 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7222
7223 aMachine = mData->mSession.mMachine;
7224
7225 if (aControl != NULL)
7226 *aControl = mData->mSession.mDirectControl;
7227
7228#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7229 /* Additional session data */
7230 if (aIPCSem != NULL)
7231 *aIPCSem = aMachine->mIPCSem;
7232#endif
7233 return true;
7234 }
7235
7236 return false;
7237}
7238
7239/**
7240 * Returns @c true if the given machine has an spawning direct session and
7241 * returns and additional session data (on some platforms) if so.
7242 *
7243 * Note that when the method returns @c false, the arguments remain unchanged.
7244 *
7245 * @param aPID PID of the spawned direct session process.
7246 *
7247 * @note locks this object for reading.
7248 */
7249#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7250bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7251#else
7252bool Machine::isSessionSpawning()
7253#endif
7254{
7255 AutoLimitedCaller autoCaller(this);
7256 AssertComRCReturn(autoCaller.rc(), false);
7257
7258 /* just return false for inaccessible machines */
7259 if (autoCaller.state() != Ready)
7260 return false;
7261
7262 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7263
7264 if (mData->mSession.mState == SessionState_Spawning)
7265 {
7266#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7267 /* Additional session data */
7268 if (aPID != NULL)
7269 {
7270 AssertReturn(mData->mSession.mPid != NIL_RTPROCESS, false);
7271 *aPID = mData->mSession.mPid;
7272 }
7273#endif
7274 return true;
7275 }
7276
7277 return false;
7278}
7279
7280/**
7281 * Called from the client watcher thread to check for unexpected client process
7282 * death during Session_Spawning state (e.g. before it successfully opened a
7283 * direct session).
7284 *
7285 * On Win32 and on OS/2, this method is called only when we've got the
7286 * direct client's process termination notification, so it always returns @c
7287 * true.
7288 *
7289 * On other platforms, this method returns @c true if the client process is
7290 * terminated and @c false if it's still alive.
7291 *
7292 * @note Locks this object for writing.
7293 */
7294bool Machine::checkForSpawnFailure()
7295{
7296 AutoCaller autoCaller(this);
7297 if (!autoCaller.isOk())
7298 {
7299 /* nothing to do */
7300 LogFlowThisFunc(("Already uninitialized!\n"));
7301 return true;
7302 }
7303
7304 /* VirtualBox::addProcessToReap() needs a write lock */
7305 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
7306
7307 if (mData->mSession.mState != SessionState_Spawning)
7308 {
7309 /* nothing to do */
7310 LogFlowThisFunc(("Not spawning any more!\n"));
7311 return true;
7312 }
7313
7314 HRESULT rc = S_OK;
7315
7316#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7317
7318 /* the process was already unexpectedly terminated, we just need to set an
7319 * error and finalize session spawning */
7320 rc = setError(E_FAIL,
7321 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
7322 getName().c_str());
7323#else
7324
7325 /* PID not yet initialized, skip check. */
7326 if (mData->mSession.mPid == NIL_RTPROCESS)
7327 return false;
7328
7329 RTPROCSTATUS status;
7330 int vrc = ::RTProcWait(mData->mSession.mPid, RTPROCWAIT_FLAGS_NOBLOCK,
7331 &status);
7332
7333 if (vrc != VERR_PROCESS_RUNNING)
7334 {
7335 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7336 rc = setError(E_FAIL,
7337 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7338 getName().c_str(), status.iStatus);
7339 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7340 rc = setError(E_FAIL,
7341 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7342 getName().c_str(), status.iStatus);
7343 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7344 rc = setError(E_FAIL,
7345 tr("The virtual machine '%s' has terminated abnormally"),
7346 getName().c_str(), status.iStatus);
7347 else
7348 rc = setError(E_FAIL,
7349 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7350 getName().c_str(), rc);
7351 }
7352
7353#endif
7354
7355 if (FAILED(rc))
7356 {
7357 /* Close the remote session, remove the remote control from the list
7358 * and reset session state to Closed (@note keep the code in sync with
7359 * the relevant part in checkForSpawnFailure()). */
7360
7361 Assert(mData->mSession.mRemoteControls.size() == 1);
7362 if (mData->mSession.mRemoteControls.size() == 1)
7363 {
7364 ErrorInfoKeeper eik;
7365 mData->mSession.mRemoteControls.front()->Uninitialize();
7366 }
7367
7368 mData->mSession.mRemoteControls.clear();
7369 mData->mSession.mState = SessionState_Unlocked;
7370
7371 /* finalize the progress after setting the state */
7372 if (!mData->mSession.mProgress.isNull())
7373 {
7374 mData->mSession.mProgress->notifyComplete(rc);
7375 mData->mSession.mProgress.setNull();
7376 }
7377
7378 mParent->addProcessToReap(mData->mSession.mPid);
7379 mData->mSession.mPid = NIL_RTPROCESS;
7380
7381 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7382 return true;
7383 }
7384
7385 return false;
7386}
7387
7388/**
7389 * Checks whether the machine can be registered. If so, commits and saves
7390 * all settings.
7391 *
7392 * @note Must be called from mParent's write lock. Locks this object and
7393 * children for writing.
7394 */
7395HRESULT Machine::prepareRegister()
7396{
7397 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7398
7399 AutoLimitedCaller autoCaller(this);
7400 AssertComRCReturnRC(autoCaller.rc());
7401
7402 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7403
7404 /* wait for state dependents to drop to zero */
7405 ensureNoStateDependencies();
7406
7407 if (!mData->mAccessible)
7408 return setError(VBOX_E_INVALID_OBJECT_STATE,
7409 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7410 mUserData->s.strName.c_str(),
7411 mData->mUuid.toString().c_str());
7412
7413 AssertReturn(autoCaller.state() == Ready, E_FAIL);
7414
7415 if (mData->mRegistered)
7416 return setError(VBOX_E_INVALID_OBJECT_STATE,
7417 tr("The machine '%s' with UUID {%s} is already registered"),
7418 mUserData->s.strName.c_str(),
7419 mData->mUuid.toString().c_str());
7420
7421 HRESULT rc = S_OK;
7422
7423 // Ensure the settings are saved. If we are going to be registered and
7424 // no config file exists yet, create it by calling saveSettings() too.
7425 if ( (mData->flModifications)
7426 || (!mData->pMachineConfigFile->fileExists())
7427 )
7428 {
7429 rc = saveSettings(NULL);
7430 // no need to check whether VirtualBox.xml needs saving too since
7431 // we can't have a machine XML file rename pending
7432 if (FAILED(rc)) return rc;
7433 }
7434
7435 /* more config checking goes here */
7436
7437 if (SUCCEEDED(rc))
7438 {
7439 /* we may have had implicit modifications we want to fix on success */
7440 commit();
7441
7442 mData->mRegistered = true;
7443 }
7444 else
7445 {
7446 /* we may have had implicit modifications we want to cancel on failure*/
7447 rollback(false /* aNotify */);
7448 }
7449
7450 return rc;
7451}
7452
7453/**
7454 * Increases the number of objects dependent on the machine state or on the
7455 * registered state. Guarantees that these two states will not change at least
7456 * until #releaseStateDependency() is called.
7457 *
7458 * Depending on the @a aDepType value, additional state checks may be made.
7459 * These checks will set extended error info on failure. See
7460 * #checkStateDependency() for more info.
7461 *
7462 * If this method returns a failure, the dependency is not added and the caller
7463 * is not allowed to rely on any particular machine state or registration state
7464 * value and may return the failed result code to the upper level.
7465 *
7466 * @param aDepType Dependency type to add.
7467 * @param aState Current machine state (NULL if not interested).
7468 * @param aRegistered Current registered state (NULL if not interested).
7469 *
7470 * @note Locks this object for writing.
7471 */
7472HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7473 MachineState_T *aState /* = NULL */,
7474 BOOL *aRegistered /* = NULL */)
7475{
7476 AutoCaller autoCaller(this);
7477 AssertComRCReturnRC(autoCaller.rc());
7478
7479 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7480
7481 HRESULT rc = checkStateDependency(aDepType);
7482 if (FAILED(rc)) return rc;
7483
7484 {
7485 if (mData->mMachineStateChangePending != 0)
7486 {
7487 /* ensureNoStateDependencies() is waiting for state dependencies to
7488 * drop to zero so don't add more. It may make sense to wait a bit
7489 * and retry before reporting an error (since the pending state
7490 * transition should be really quick) but let's just assert for
7491 * now to see if it ever happens on practice. */
7492
7493 AssertFailed();
7494
7495 return setError(E_ACCESSDENIED,
7496 tr("Machine state change is in progress. Please retry the operation later."));
7497 }
7498
7499 ++mData->mMachineStateDeps;
7500 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7501 }
7502
7503 if (aState)
7504 *aState = mData->mMachineState;
7505 if (aRegistered)
7506 *aRegistered = mData->mRegistered;
7507
7508 return S_OK;
7509}
7510
7511/**
7512 * Decreases the number of objects dependent on the machine state.
7513 * Must always complete the #addStateDependency() call after the state
7514 * dependency is no more necessary.
7515 */
7516void Machine::releaseStateDependency()
7517{
7518 AutoCaller autoCaller(this);
7519 AssertComRCReturnVoid(autoCaller.rc());
7520
7521 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7522
7523 /* releaseStateDependency() w/o addStateDependency()? */
7524 AssertReturnVoid(mData->mMachineStateDeps != 0);
7525 -- mData->mMachineStateDeps;
7526
7527 if (mData->mMachineStateDeps == 0)
7528 {
7529 /* inform ensureNoStateDependencies() that there are no more deps */
7530 if (mData->mMachineStateChangePending != 0)
7531 {
7532 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7533 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7534 }
7535 }
7536}
7537
7538// protected methods
7539/////////////////////////////////////////////////////////////////////////////
7540
7541/**
7542 * Performs machine state checks based on the @a aDepType value. If a check
7543 * fails, this method will set extended error info, otherwise it will return
7544 * S_OK. It is supposed, that on failure, the caller will immediately return
7545 * the return value of this method to the upper level.
7546 *
7547 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7548 *
7549 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7550 * current state of this machine object allows to change settings of the
7551 * machine (i.e. the machine is not registered, or registered but not running
7552 * and not saved). It is useful to call this method from Machine setters
7553 * before performing any change.
7554 *
7555 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7556 * as for MutableStateDep except that if the machine is saved, S_OK is also
7557 * returned. This is useful in setters which allow changing machine
7558 * properties when it is in the saved state.
7559 *
7560 * @param aDepType Dependency type to check.
7561 *
7562 * @note Non Machine based classes should use #addStateDependency() and
7563 * #releaseStateDependency() methods or the smart AutoStateDependency
7564 * template.
7565 *
7566 * @note This method must be called from under this object's read or write
7567 * lock.
7568 */
7569HRESULT Machine::checkStateDependency(StateDependency aDepType)
7570{
7571 switch (aDepType)
7572 {
7573 case AnyStateDep:
7574 {
7575 break;
7576 }
7577 case MutableStateDep:
7578 {
7579 if ( mData->mRegistered
7580 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7581 || ( mData->mMachineState != MachineState_Paused
7582 && mData->mMachineState != MachineState_Running
7583 && mData->mMachineState != MachineState_Aborted
7584 && mData->mMachineState != MachineState_Teleported
7585 && mData->mMachineState != MachineState_PoweredOff
7586 )
7587 )
7588 )
7589 return setError(VBOX_E_INVALID_VM_STATE,
7590 tr("The machine is not mutable (state is %s)"),
7591 Global::stringifyMachineState(mData->mMachineState));
7592 break;
7593 }
7594 case MutableOrSavedStateDep:
7595 {
7596 if ( mData->mRegistered
7597 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7598 || ( mData->mMachineState != MachineState_Paused
7599 && mData->mMachineState != MachineState_Running
7600 && mData->mMachineState != MachineState_Aborted
7601 && mData->mMachineState != MachineState_Teleported
7602 && mData->mMachineState != MachineState_Saved
7603 && mData->mMachineState != MachineState_PoweredOff
7604 )
7605 )
7606 )
7607 return setError(VBOX_E_INVALID_VM_STATE,
7608 tr("The machine is not mutable (state is %s)"),
7609 Global::stringifyMachineState(mData->mMachineState));
7610 break;
7611 }
7612 }
7613
7614 return S_OK;
7615}
7616
7617/**
7618 * Helper to initialize all associated child objects and allocate data
7619 * structures.
7620 *
7621 * This method must be called as a part of the object's initialization procedure
7622 * (usually done in the #init() method).
7623 *
7624 * @note Must be called only from #init() or from #registeredInit().
7625 */
7626HRESULT Machine::initDataAndChildObjects()
7627{
7628 AutoCaller autoCaller(this);
7629 AssertComRCReturnRC(autoCaller.rc());
7630 AssertComRCReturn(autoCaller.state() == InInit ||
7631 autoCaller.state() == Limited, E_FAIL);
7632
7633 AssertReturn(!mData->mAccessible, E_FAIL);
7634
7635 /* allocate data structures */
7636 mSSData.allocate();
7637 mUserData.allocate();
7638 mHWData.allocate();
7639 mMediaData.allocate();
7640 mStorageControllers.allocate();
7641
7642 /* initialize mOSTypeId */
7643 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
7644
7645 /* create associated BIOS settings object */
7646 unconst(mBIOSSettings).createObject();
7647 mBIOSSettings->init(this);
7648
7649 /* create an associated VRDE object (default is disabled) */
7650 unconst(mVRDEServer).createObject();
7651 mVRDEServer->init(this);
7652
7653 /* create associated serial port objects */
7654 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
7655 {
7656 unconst(mSerialPorts[slot]).createObject();
7657 mSerialPorts[slot]->init(this, slot);
7658 }
7659
7660 /* create associated parallel port objects */
7661 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7662 {
7663 unconst(mParallelPorts[slot]).createObject();
7664 mParallelPorts[slot]->init(this, slot);
7665 }
7666
7667 /* create the audio adapter object (always present, default is disabled) */
7668 unconst(mAudioAdapter).createObject();
7669 mAudioAdapter->init(this);
7670
7671 /* create the USB controller object (always present, default is disabled) */
7672 unconst(mUSBController).createObject();
7673 mUSBController->init(this);
7674
7675 /* create associated network adapter objects */
7676 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
7677 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7678 {
7679 unconst(mNetworkAdapters[slot]).createObject();
7680 mNetworkAdapters[slot]->init(this, slot);
7681 }
7682
7683 /* create the bandwidth control */
7684 unconst(mBandwidthControl).createObject();
7685 mBandwidthControl->init(this);
7686
7687 return S_OK;
7688}
7689
7690/**
7691 * Helper to uninitialize all associated child objects and to free all data
7692 * structures.
7693 *
7694 * This method must be called as a part of the object's uninitialization
7695 * procedure (usually done in the #uninit() method).
7696 *
7697 * @note Must be called only from #uninit() or from #registeredInit().
7698 */
7699void Machine::uninitDataAndChildObjects()
7700{
7701 AutoCaller autoCaller(this);
7702 AssertComRCReturnVoid(autoCaller.rc());
7703 AssertComRCReturnVoid( autoCaller.state() == InUninit
7704 || autoCaller.state() == Limited);
7705
7706 /* tell all our other child objects we've been uninitialized */
7707 if (mBandwidthControl)
7708 {
7709 mBandwidthControl->uninit();
7710 unconst(mBandwidthControl).setNull();
7711 }
7712
7713 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7714 {
7715 if (mNetworkAdapters[slot])
7716 {
7717 mNetworkAdapters[slot]->uninit();
7718 unconst(mNetworkAdapters[slot]).setNull();
7719 }
7720 }
7721
7722 if (mUSBController)
7723 {
7724 mUSBController->uninit();
7725 unconst(mUSBController).setNull();
7726 }
7727
7728 if (mAudioAdapter)
7729 {
7730 mAudioAdapter->uninit();
7731 unconst(mAudioAdapter).setNull();
7732 }
7733
7734 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7735 {
7736 if (mParallelPorts[slot])
7737 {
7738 mParallelPorts[slot]->uninit();
7739 unconst(mParallelPorts[slot]).setNull();
7740 }
7741 }
7742
7743 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
7744 {
7745 if (mSerialPorts[slot])
7746 {
7747 mSerialPorts[slot]->uninit();
7748 unconst(mSerialPorts[slot]).setNull();
7749 }
7750 }
7751
7752 if (mVRDEServer)
7753 {
7754 mVRDEServer->uninit();
7755 unconst(mVRDEServer).setNull();
7756 }
7757
7758 if (mBIOSSettings)
7759 {
7760 mBIOSSettings->uninit();
7761 unconst(mBIOSSettings).setNull();
7762 }
7763
7764 /* Deassociate hard disks (only when a real Machine or a SnapshotMachine
7765 * instance is uninitialized; SessionMachine instances refer to real
7766 * Machine hard disks). This is necessary for a clean re-initialization of
7767 * the VM after successfully re-checking the accessibility state. Note
7768 * that in case of normal Machine or SnapshotMachine uninitialization (as
7769 * a result of unregistering or deleting the snapshot), outdated hard
7770 * disk attachments will already be uninitialized and deleted, so this
7771 * code will not affect them. */
7772 if ( !!mMediaData
7773 && (!isSessionMachine())
7774 )
7775 {
7776 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
7777 it != mMediaData->mAttachments.end();
7778 ++it)
7779 {
7780 ComObjPtr<Medium> hd = (*it)->getMedium();
7781 if (hd.isNull())
7782 continue;
7783 HRESULT rc = hd->removeBackReference(mData->mUuid, getSnapshotId());
7784 AssertComRC(rc);
7785 }
7786 }
7787
7788 if (!isSessionMachine() && !isSnapshotMachine())
7789 {
7790 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
7791 if (mData->mFirstSnapshot)
7792 {
7793 // snapshots tree is protected by media write lock; strictly
7794 // this isn't necessary here since we're deleting the entire
7795 // machine, but otherwise we assert in Snapshot::uninit()
7796 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7797 mData->mFirstSnapshot->uninit();
7798 mData->mFirstSnapshot.setNull();
7799 }
7800
7801 mData->mCurrentSnapshot.setNull();
7802 }
7803
7804 /* free data structures (the essential mData structure is not freed here
7805 * since it may be still in use) */
7806 mMediaData.free();
7807 mStorageControllers.free();
7808 mHWData.free();
7809 mUserData.free();
7810 mSSData.free();
7811}
7812
7813/**
7814 * Returns a pointer to the Machine object for this machine that acts like a
7815 * parent for complex machine data objects such as shared folders, etc.
7816 *
7817 * For primary Machine objects and for SnapshotMachine objects, returns this
7818 * object's pointer itself. For SessionMachine objects, returns the peer
7819 * (primary) machine pointer.
7820 */
7821Machine* Machine::getMachine()
7822{
7823 if (isSessionMachine())
7824 return (Machine*)mPeer;
7825 return this;
7826}
7827
7828/**
7829 * Makes sure that there are no machine state dependents. If necessary, waits
7830 * for the number of dependents to drop to zero.
7831 *
7832 * Make sure this method is called from under this object's write lock to
7833 * guarantee that no new dependents may be added when this method returns
7834 * control to the caller.
7835 *
7836 * @note Locks this object for writing. The lock will be released while waiting
7837 * (if necessary).
7838 *
7839 * @warning To be used only in methods that change the machine state!
7840 */
7841void Machine::ensureNoStateDependencies()
7842{
7843 AssertReturnVoid(isWriteLockOnCurrentThread());
7844
7845 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7846
7847 /* Wait for all state dependents if necessary */
7848 if (mData->mMachineStateDeps != 0)
7849 {
7850 /* lazy semaphore creation */
7851 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
7852 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
7853
7854 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
7855 mData->mMachineStateDeps));
7856
7857 ++mData->mMachineStateChangePending;
7858
7859 /* reset the semaphore before waiting, the last dependent will signal
7860 * it */
7861 RTSemEventMultiReset(mData->mMachineStateDepsSem);
7862
7863 alock.release();
7864
7865 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
7866
7867 alock.acquire();
7868
7869 -- mData->mMachineStateChangePending;
7870 }
7871}
7872
7873/**
7874 * Changes the machine state and informs callbacks.
7875 *
7876 * This method is not intended to fail so it either returns S_OK or asserts (and
7877 * returns a failure).
7878 *
7879 * @note Locks this object for writing.
7880 */
7881HRESULT Machine::setMachineState(MachineState_T aMachineState)
7882{
7883 LogFlowThisFuncEnter();
7884 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
7885
7886 AutoCaller autoCaller(this);
7887 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7888
7889 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7890
7891 /* wait for state dependents to drop to zero */
7892 ensureNoStateDependencies();
7893
7894 if (mData->mMachineState != aMachineState)
7895 {
7896 mData->mMachineState = aMachineState;
7897
7898 RTTimeNow(&mData->mLastStateChange);
7899
7900 mParent->onMachineStateChange(mData->mUuid, aMachineState);
7901 }
7902
7903 LogFlowThisFuncLeave();
7904 return S_OK;
7905}
7906
7907/**
7908 * Searches for a shared folder with the given logical name
7909 * in the collection of shared folders.
7910 *
7911 * @param aName logical name of the shared folder
7912 * @param aSharedFolder where to return the found object
7913 * @param aSetError whether to set the error info if the folder is
7914 * not found
7915 * @return
7916 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
7917 *
7918 * @note
7919 * must be called from under the object's lock!
7920 */
7921HRESULT Machine::findSharedFolder(const Utf8Str &aName,
7922 ComObjPtr<SharedFolder> &aSharedFolder,
7923 bool aSetError /* = false */)
7924{
7925 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
7926 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
7927 it != mHWData->mSharedFolders.end();
7928 ++it)
7929 {
7930 SharedFolder *pSF = *it;
7931 AutoCaller autoCaller(pSF);
7932 if (pSF->getName() == aName)
7933 {
7934 aSharedFolder = pSF;
7935 rc = S_OK;
7936 break;
7937 }
7938 }
7939
7940 if (aSetError && FAILED(rc))
7941 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
7942
7943 return rc;
7944}
7945
7946/**
7947 * Initializes all machine instance data from the given settings structures
7948 * from XML. The exception is the machine UUID which needs special handling
7949 * depending on the caller's use case, so the caller needs to set that herself.
7950 *
7951 * This gets called in several contexts during machine initialization:
7952 *
7953 * -- When machine XML exists on disk already and needs to be loaded into memory,
7954 * for example, from registeredInit() to load all registered machines on
7955 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
7956 * attached to the machine should be part of some media registry already.
7957 *
7958 * -- During OVF import, when a machine config has been constructed from an
7959 * OVF file. In this case, puuidRegistry is set to the machine UUID to
7960 * ensure that the media listed as attachments in the config (which have
7961 * been imported from the OVF) receive the correct registry ID.
7962 *
7963 * -- During VM cloning.
7964 *
7965 * @param config Machine settings from XML.
7966 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
7967 * @return
7968 */
7969HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
7970 const Guid *puuidRegistry)
7971{
7972 // copy name, description, OS type, teleporter, UTC etc.
7973 mUserData->s = config.machineUserData;
7974
7975 // look up the object by Id to check it is valid
7976 ComPtr<IGuestOSType> guestOSType;
7977 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
7978 guestOSType.asOutParam());
7979 if (FAILED(rc)) return rc;
7980
7981 // stateFile (optional)
7982 if (config.strStateFile.isEmpty())
7983 mSSData->strStateFilePath.setNull();
7984 else
7985 {
7986 Utf8Str stateFilePathFull(config.strStateFile);
7987 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
7988 if (RT_FAILURE(vrc))
7989 return setError(E_FAIL,
7990 tr("Invalid saved state file path '%s' (%Rrc)"),
7991 config.strStateFile.c_str(),
7992 vrc);
7993 mSSData->strStateFilePath = stateFilePathFull;
7994 }
7995
7996 // snapshot folder needs special processing so set it again
7997 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
7998 if (FAILED(rc)) return rc;
7999
8000 /* Copy the extra data items (Not in any case config is already the same as
8001 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8002 * make sure the extra data map is copied). */
8003 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8004
8005 /* currentStateModified (optional, default is true) */
8006 mData->mCurrentStateModified = config.fCurrentStateModified;
8007
8008 mData->mLastStateChange = config.timeLastStateChange;
8009
8010 /*
8011 * note: all mUserData members must be assigned prior this point because
8012 * we need to commit changes in order to let mUserData be shared by all
8013 * snapshot machine instances.
8014 */
8015 mUserData.commitCopy();
8016
8017 // machine registry, if present (must be loaded before snapshots)
8018 if (config.canHaveOwnMediaRegistry())
8019 {
8020 // determine machine folder
8021 Utf8Str strMachineFolder = getSettingsFileFull();
8022 strMachineFolder.stripFilename();
8023 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8024 config.mediaRegistry,
8025 strMachineFolder);
8026 if (FAILED(rc)) return rc;
8027 }
8028
8029 /* Snapshot node (optional) */
8030 size_t cRootSnapshots;
8031 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8032 {
8033 // there must be only one root snapshot
8034 Assert(cRootSnapshots == 1);
8035
8036 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8037
8038 rc = loadSnapshot(snap,
8039 config.uuidCurrentSnapshot,
8040 NULL); // no parent == first snapshot
8041 if (FAILED(rc)) return rc;
8042 }
8043
8044 // hardware data
8045 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8046 if (FAILED(rc)) return rc;
8047
8048 // load storage controllers
8049 rc = loadStorageControllers(config.storageMachine,
8050 puuidRegistry,
8051 NULL /* puuidSnapshot */);
8052 if (FAILED(rc)) return rc;
8053
8054 /*
8055 * NOTE: the assignment below must be the last thing to do,
8056 * otherwise it will be not possible to change the settings
8057 * somewhere in the code above because all setters will be
8058 * blocked by checkStateDependency(MutableStateDep).
8059 */
8060
8061 /* set the machine state to Aborted or Saved when appropriate */
8062 if (config.fAborted)
8063 {
8064 mSSData->strStateFilePath.setNull();
8065
8066 /* no need to use setMachineState() during init() */
8067 mData->mMachineState = MachineState_Aborted;
8068 }
8069 else if (!mSSData->strStateFilePath.isEmpty())
8070 {
8071 /* no need to use setMachineState() during init() */
8072 mData->mMachineState = MachineState_Saved;
8073 }
8074
8075 // after loading settings, we are no longer different from the XML on disk
8076 mData->flModifications = 0;
8077
8078 return S_OK;
8079}
8080
8081/**
8082 * Recursively loads all snapshots starting from the given.
8083 *
8084 * @param aNode <Snapshot> node.
8085 * @param aCurSnapshotId Current snapshot ID from the settings file.
8086 * @param aParentSnapshot Parent snapshot.
8087 */
8088HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8089 const Guid &aCurSnapshotId,
8090 Snapshot *aParentSnapshot)
8091{
8092 AssertReturn(!isSnapshotMachine(), E_FAIL);
8093 AssertReturn(!isSessionMachine(), E_FAIL);
8094
8095 HRESULT rc = S_OK;
8096
8097 Utf8Str strStateFile;
8098 if (!data.strStateFile.isEmpty())
8099 {
8100 /* optional */
8101 strStateFile = data.strStateFile;
8102 int vrc = calculateFullPath(strStateFile, strStateFile);
8103 if (RT_FAILURE(vrc))
8104 return setError(E_FAIL,
8105 tr("Invalid saved state file path '%s' (%Rrc)"),
8106 strStateFile.c_str(),
8107 vrc);
8108 }
8109
8110 /* create a snapshot machine object */
8111 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8112 pSnapshotMachine.createObject();
8113 rc = pSnapshotMachine->initFromSettings(this,
8114 data.hardware,
8115 &data.debugging,
8116 &data.autostart,
8117 data.storage,
8118 data.uuid.ref(),
8119 strStateFile);
8120 if (FAILED(rc)) return rc;
8121
8122 /* create a snapshot object */
8123 ComObjPtr<Snapshot> pSnapshot;
8124 pSnapshot.createObject();
8125 /* initialize the snapshot */
8126 rc = pSnapshot->init(mParent, // VirtualBox object
8127 data.uuid,
8128 data.strName,
8129 data.strDescription,
8130 data.timestamp,
8131 pSnapshotMachine,
8132 aParentSnapshot);
8133 if (FAILED(rc)) return rc;
8134
8135 /* memorize the first snapshot if necessary */
8136 if (!mData->mFirstSnapshot)
8137 mData->mFirstSnapshot = pSnapshot;
8138
8139 /* memorize the current snapshot when appropriate */
8140 if ( !mData->mCurrentSnapshot
8141 && pSnapshot->getId() == aCurSnapshotId
8142 )
8143 mData->mCurrentSnapshot = pSnapshot;
8144
8145 // now create the children
8146 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8147 it != data.llChildSnapshots.end();
8148 ++it)
8149 {
8150 const settings::Snapshot &childData = *it;
8151 // recurse
8152 rc = loadSnapshot(childData,
8153 aCurSnapshotId,
8154 pSnapshot); // parent = the one we created above
8155 if (FAILED(rc)) return rc;
8156 }
8157
8158 return rc;
8159}
8160
8161/**
8162 * Loads settings into mHWData.
8163 *
8164 * @param data Reference to the hardware settings.
8165 * @param pDbg Pointer to the debugging settings.
8166 * @param pAutostart Pointer to the autostart settings.
8167 */
8168HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8169 const settings::Autostart *pAutostart)
8170{
8171 AssertReturn(!isSessionMachine(), E_FAIL);
8172
8173 HRESULT rc = S_OK;
8174
8175 try
8176 {
8177 /* The hardware version attribute (optional). */
8178 mHWData->mHWVersion = data.strVersion;
8179 mHWData->mHardwareUUID = data.uuid;
8180
8181 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8182 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8183 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8184 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8185 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8186 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8187 mHWData->mPAEEnabled = data.fPAE;
8188 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8189
8190 mHWData->mCPUCount = data.cCPUs;
8191 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8192 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8193
8194 // cpu
8195 if (mHWData->mCPUHotPlugEnabled)
8196 {
8197 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8198 it != data.llCpus.end();
8199 ++it)
8200 {
8201 const settings::Cpu &cpu = *it;
8202
8203 mHWData->mCPUAttached[cpu.ulId] = true;
8204 }
8205 }
8206
8207 // cpuid leafs
8208 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8209 it != data.llCpuIdLeafs.end();
8210 ++it)
8211 {
8212 const settings::CpuIdLeaf &leaf = *it;
8213
8214 switch (leaf.ulId)
8215 {
8216 case 0x0:
8217 case 0x1:
8218 case 0x2:
8219 case 0x3:
8220 case 0x4:
8221 case 0x5:
8222 case 0x6:
8223 case 0x7:
8224 case 0x8:
8225 case 0x9:
8226 case 0xA:
8227 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8228 break;
8229
8230 case 0x80000000:
8231 case 0x80000001:
8232 case 0x80000002:
8233 case 0x80000003:
8234 case 0x80000004:
8235 case 0x80000005:
8236 case 0x80000006:
8237 case 0x80000007:
8238 case 0x80000008:
8239 case 0x80000009:
8240 case 0x8000000A:
8241 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8242 break;
8243
8244 default:
8245 /* just ignore */
8246 break;
8247 }
8248 }
8249
8250 mHWData->mMemorySize = data.ulMemorySizeMB;
8251 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8252
8253 // boot order
8254 for (size_t i = 0;
8255 i < RT_ELEMENTS(mHWData->mBootOrder);
8256 i++)
8257 {
8258 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8259 if (it == data.mapBootOrder.end())
8260 mHWData->mBootOrder[i] = DeviceType_Null;
8261 else
8262 mHWData->mBootOrder[i] = it->second;
8263 }
8264
8265 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8266 mHWData->mMonitorCount = data.cMonitors;
8267 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8268 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8269 mHWData->mFirmwareType = data.firmwareType;
8270 mHWData->mPointingHidType = data.pointingHidType;
8271 mHWData->mKeyboardHidType = data.keyboardHidType;
8272 mHWData->mChipsetType = data.chipsetType;
8273 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8274 mHWData->mHpetEnabled = data.fHpetEnabled;
8275
8276 /* VRDEServer */
8277 rc = mVRDEServer->loadSettings(data.vrdeSettings);
8278 if (FAILED(rc)) return rc;
8279
8280 /* BIOS */
8281 rc = mBIOSSettings->loadSettings(data.biosSettings);
8282 if (FAILED(rc)) return rc;
8283
8284 // Bandwidth control (must come before network adapters)
8285 rc = mBandwidthControl->loadSettings(data.ioSettings);
8286 if (FAILED(rc)) return rc;
8287
8288 /* USB Controller */
8289 rc = mUSBController->loadSettings(data.usbController);
8290 if (FAILED(rc)) return rc;
8291
8292 // network adapters
8293 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8294 uint32_t oldCount = mNetworkAdapters.size();
8295 if (newCount > oldCount)
8296 {
8297 mNetworkAdapters.resize(newCount);
8298 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
8299 {
8300 unconst(mNetworkAdapters[slot]).createObject();
8301 mNetworkAdapters[slot]->init(this, slot);
8302 }
8303 }
8304 else if (newCount < oldCount)
8305 mNetworkAdapters.resize(newCount);
8306 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8307 it != data.llNetworkAdapters.end();
8308 ++it)
8309 {
8310 const settings::NetworkAdapter &nic = *it;
8311
8312 /* slot unicity is guaranteed by XML Schema */
8313 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8314 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
8315 if (FAILED(rc)) return rc;
8316 }
8317
8318 // serial ports
8319 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8320 it != data.llSerialPorts.end();
8321 ++it)
8322 {
8323 const settings::SerialPort &s = *it;
8324
8325 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8326 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
8327 if (FAILED(rc)) return rc;
8328 }
8329
8330 // parallel ports (optional)
8331 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8332 it != data.llParallelPorts.end();
8333 ++it)
8334 {
8335 const settings::ParallelPort &p = *it;
8336
8337 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8338 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
8339 if (FAILED(rc)) return rc;
8340 }
8341
8342 /* AudioAdapter */
8343 rc = mAudioAdapter->loadSettings(data.audioAdapter);
8344 if (FAILED(rc)) return rc;
8345
8346 /* Shared folders */
8347 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8348 it != data.llSharedFolders.end();
8349 ++it)
8350 {
8351 const settings::SharedFolder &sf = *it;
8352
8353 ComObjPtr<SharedFolder> sharedFolder;
8354 /* Check for double entries. Not allowed! */
8355 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8356 if (SUCCEEDED(rc))
8357 return setError(VBOX_E_OBJECT_IN_USE,
8358 tr("Shared folder named '%s' already exists"),
8359 sf.strName.c_str());
8360
8361 /* Create the new shared folder. Don't break on error. This will be
8362 * reported when the machine starts. */
8363 sharedFolder.createObject();
8364 rc = sharedFolder->init(getMachine(),
8365 sf.strName,
8366 sf.strHostPath,
8367 RT_BOOL(sf.fWritable),
8368 RT_BOOL(sf.fAutoMount),
8369 false /* fFailOnError */);
8370 if (FAILED(rc)) return rc;
8371 mHWData->mSharedFolders.push_back(sharedFolder);
8372 }
8373
8374 // Clipboard
8375 mHWData->mClipboardMode = data.clipboardMode;
8376
8377 // guest settings
8378 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8379
8380 // IO settings
8381 mHWData->mIoCacheEnabled = data.ioSettings.fIoCacheEnabled;
8382 mHWData->mIoCacheSize = data.ioSettings.ulIoCacheSize;
8383
8384 // Host PCI devices
8385 for (settings::HostPciDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8386 it != data.pciAttachments.end();
8387 ++it)
8388 {
8389 const settings::HostPciDeviceAttachment &hpda = *it;
8390 ComObjPtr<PciDeviceAttachment> pda;
8391
8392 pda.createObject();
8393 pda->loadSettings(this, hpda);
8394 mHWData->mPciDeviceAssignments.push_back(pda);
8395 }
8396
8397 /*
8398 * (The following isn't really real hardware, but it lives in HWData
8399 * for reasons of convenience.)
8400 */
8401
8402#ifdef VBOX_WITH_GUEST_PROPS
8403 /* Guest properties (optional) */
8404 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8405 it != data.llGuestProperties.end();
8406 ++it)
8407 {
8408 const settings::GuestProperty &prop = *it;
8409 uint32_t fFlags = guestProp::NILFLAG;
8410 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8411 HWData::GuestProperty property = { prop.strName, prop.strValue, (LONG64) prop.timestamp, fFlags };
8412 mHWData->mGuestProperties.push_back(property);
8413 }
8414
8415 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8416#endif /* VBOX_WITH_GUEST_PROPS defined */
8417
8418 rc = loadDebugging(pDbg);
8419 if (FAILED(rc))
8420 return rc;
8421
8422 mHWData->mAutostart = *pAutostart;
8423 }
8424 catch(std::bad_alloc &)
8425 {
8426 return E_OUTOFMEMORY;
8427 }
8428
8429 AssertComRC(rc);
8430 return rc;
8431}
8432
8433/**
8434 * Called from Machine::loadHardware() to load the debugging settings of the
8435 * machine.
8436 *
8437 * @param pDbg Pointer to the settings.
8438 */
8439HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
8440{
8441 mHWData->mDebugging = *pDbg;
8442 /* no more processing currently required, this will probably change. */
8443 return S_OK;
8444}
8445
8446/**
8447 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
8448 *
8449 * @param data
8450 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8451 * @param puuidSnapshot
8452 * @return
8453 */
8454HRESULT Machine::loadStorageControllers(const settings::Storage &data,
8455 const Guid *puuidRegistry,
8456 const Guid *puuidSnapshot)
8457{
8458 AssertReturn(!isSessionMachine(), E_FAIL);
8459
8460 HRESULT rc = S_OK;
8461
8462 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8463 it != data.llStorageControllers.end();
8464 ++it)
8465 {
8466 const settings::StorageController &ctlData = *it;
8467
8468 ComObjPtr<StorageController> pCtl;
8469 /* Try to find one with the name first. */
8470 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8471 if (SUCCEEDED(rc))
8472 return setError(VBOX_E_OBJECT_IN_USE,
8473 tr("Storage controller named '%s' already exists"),
8474 ctlData.strName.c_str());
8475
8476 pCtl.createObject();
8477 rc = pCtl->init(this,
8478 ctlData.strName,
8479 ctlData.storageBus,
8480 ctlData.ulInstance,
8481 ctlData.fBootable);
8482 if (FAILED(rc)) return rc;
8483
8484 mStorageControllers->push_back(pCtl);
8485
8486 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8487 if (FAILED(rc)) return rc;
8488
8489 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8490 if (FAILED(rc)) return rc;
8491
8492 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8493 if (FAILED(rc)) return rc;
8494
8495 /* Set IDE emulation settings (only for AHCI controller). */
8496 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8497 {
8498 if ( (FAILED(rc = pCtl->SetIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8499 || (FAILED(rc = pCtl->SetIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8500 || (FAILED(rc = pCtl->SetIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8501 || (FAILED(rc = pCtl->SetIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8502 )
8503 return rc;
8504 }
8505
8506 /* Load the attached devices now. */
8507 rc = loadStorageDevices(pCtl,
8508 ctlData,
8509 puuidRegistry,
8510 puuidSnapshot);
8511 if (FAILED(rc)) return rc;
8512 }
8513
8514 return S_OK;
8515}
8516
8517/**
8518 * Called from loadStorageControllers for a controller's devices.
8519 *
8520 * @param aStorageController
8521 * @param data
8522 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8523 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
8524 * @return
8525 */
8526HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
8527 const settings::StorageController &data,
8528 const Guid *puuidRegistry,
8529 const Guid *puuidSnapshot)
8530{
8531 HRESULT rc = S_OK;
8532
8533 /* paranoia: detect duplicate attachments */
8534 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8535 it != data.llAttachedDevices.end();
8536 ++it)
8537 {
8538 const settings::AttachedDevice &ad = *it;
8539
8540 for (settings::AttachedDevicesList::const_iterator it2 = it;
8541 it2 != data.llAttachedDevices.end();
8542 ++it2)
8543 {
8544 if (it == it2)
8545 continue;
8546
8547 const settings::AttachedDevice &ad2 = *it2;
8548
8549 if ( ad.lPort == ad2.lPort
8550 && ad.lDevice == ad2.lDevice)
8551 {
8552 return setError(E_FAIL,
8553 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
8554 aStorageController->getName().c_str(),
8555 ad.lPort,
8556 ad.lDevice,
8557 mUserData->s.strName.c_str());
8558 }
8559 }
8560 }
8561
8562 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8563 it != data.llAttachedDevices.end();
8564 ++it)
8565 {
8566 const settings::AttachedDevice &dev = *it;
8567 ComObjPtr<Medium> medium;
8568
8569 switch (dev.deviceType)
8570 {
8571 case DeviceType_Floppy:
8572 case DeviceType_DVD:
8573 if (dev.strHostDriveSrc.isNotEmpty())
8574 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
8575 else
8576 rc = mParent->findRemoveableMedium(dev.deviceType,
8577 dev.uuid,
8578 false /* fRefresh */,
8579 false /* aSetError */,
8580 medium);
8581 if (rc == VBOX_E_OBJECT_NOT_FOUND)
8582 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
8583 rc = S_OK;
8584 break;
8585
8586 case DeviceType_HardDisk:
8587 {
8588 /* find a hard disk by UUID */
8589 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
8590 if (FAILED(rc))
8591 {
8592 if (isSnapshotMachine())
8593 {
8594 // wrap another error message around the "cannot find hard disk" set by findHardDisk
8595 // so the user knows that the bad disk is in a snapshot somewhere
8596 com::ErrorInfo info;
8597 return setError(E_FAIL,
8598 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
8599 puuidSnapshot->raw(),
8600 info.getText().raw());
8601 }
8602 else
8603 return rc;
8604 }
8605
8606 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
8607
8608 if (medium->getType() == MediumType_Immutable)
8609 {
8610 if (isSnapshotMachine())
8611 return setError(E_FAIL,
8612 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8613 "of the virtual machine '%s' ('%s')"),
8614 medium->getLocationFull().c_str(),
8615 dev.uuid.raw(),
8616 puuidSnapshot->raw(),
8617 mUserData->s.strName.c_str(),
8618 mData->m_strConfigFileFull.c_str());
8619
8620 return setError(E_FAIL,
8621 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8622 medium->getLocationFull().c_str(),
8623 dev.uuid.raw(),
8624 mUserData->s.strName.c_str(),
8625 mData->m_strConfigFileFull.c_str());
8626 }
8627
8628 if (medium->getType() == MediumType_MultiAttach)
8629 {
8630 if (isSnapshotMachine())
8631 return setError(E_FAIL,
8632 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8633 "of the virtual machine '%s' ('%s')"),
8634 medium->getLocationFull().c_str(),
8635 dev.uuid.raw(),
8636 puuidSnapshot->raw(),
8637 mUserData->s.strName.c_str(),
8638 mData->m_strConfigFileFull.c_str());
8639
8640 return setError(E_FAIL,
8641 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8642 medium->getLocationFull().c_str(),
8643 dev.uuid.raw(),
8644 mUserData->s.strName.c_str(),
8645 mData->m_strConfigFileFull.c_str());
8646 }
8647
8648 if ( !isSnapshotMachine()
8649 && medium->getChildren().size() != 0
8650 )
8651 return setError(E_FAIL,
8652 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
8653 "because it has %d differencing child hard disks"),
8654 medium->getLocationFull().c_str(),
8655 dev.uuid.raw(),
8656 mUserData->s.strName.c_str(),
8657 mData->m_strConfigFileFull.c_str(),
8658 medium->getChildren().size());
8659
8660 if (findAttachment(mMediaData->mAttachments,
8661 medium))
8662 return setError(E_FAIL,
8663 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
8664 medium->getLocationFull().c_str(),
8665 dev.uuid.raw(),
8666 mUserData->s.strName.c_str(),
8667 mData->m_strConfigFileFull.c_str());
8668
8669 break;
8670 }
8671
8672 default:
8673 return setError(E_FAIL,
8674 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
8675 medium->getLocationFull().c_str(),
8676 mUserData->s.strName.c_str(),
8677 mData->m_strConfigFileFull.c_str());
8678 }
8679
8680 if (FAILED(rc))
8681 break;
8682
8683 /* Bandwidth groups are loaded at this point. */
8684 ComObjPtr<BandwidthGroup> pBwGroup;
8685
8686 if (!dev.strBwGroup.isEmpty())
8687 {
8688 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
8689 if (FAILED(rc))
8690 return setError(E_FAIL,
8691 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
8692 medium->getLocationFull().c_str(),
8693 dev.strBwGroup.c_str(),
8694 mUserData->s.strName.c_str(),
8695 mData->m_strConfigFileFull.c_str());
8696 pBwGroup->reference();
8697 }
8698
8699 const Bstr controllerName = aStorageController->getName();
8700 ComObjPtr<MediumAttachment> pAttachment;
8701 pAttachment.createObject();
8702 rc = pAttachment->init(this,
8703 medium,
8704 controllerName,
8705 dev.lPort,
8706 dev.lDevice,
8707 dev.deviceType,
8708 false,
8709 dev.fPassThrough,
8710 dev.fTempEject,
8711 dev.fNonRotational,
8712 dev.fDiscard,
8713 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
8714 if (FAILED(rc)) break;
8715
8716 /* associate the medium with this machine and snapshot */
8717 if (!medium.isNull())
8718 {
8719 AutoCaller medCaller(medium);
8720 if (FAILED(medCaller.rc())) return medCaller.rc();
8721 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
8722
8723 if (isSnapshotMachine())
8724 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
8725 else
8726 rc = medium->addBackReference(mData->mUuid);
8727 /* If the medium->addBackReference fails it sets an appropriate
8728 * error message, so no need to do any guesswork here. */
8729
8730 if (puuidRegistry)
8731 // caller wants registry ID to be set on all attached media (OVF import case)
8732 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
8733 }
8734
8735 if (FAILED(rc))
8736 break;
8737
8738 /* back up mMediaData to let registeredInit() properly rollback on failure
8739 * (= limited accessibility) */
8740 setModified(IsModified_Storage);
8741 mMediaData.backup();
8742 mMediaData->mAttachments.push_back(pAttachment);
8743 }
8744
8745 return rc;
8746}
8747
8748/**
8749 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
8750 *
8751 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
8752 * @param aSnapshot where to return the found snapshot
8753 * @param aSetError true to set extended error info on failure
8754 */
8755HRESULT Machine::findSnapshotById(const Guid &aId,
8756 ComObjPtr<Snapshot> &aSnapshot,
8757 bool aSetError /* = false */)
8758{
8759 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
8760
8761 if (!mData->mFirstSnapshot)
8762 {
8763 if (aSetError)
8764 return setError(E_FAIL, tr("This machine does not have any snapshots"));
8765 return E_FAIL;
8766 }
8767
8768 if (aId.isEmpty())
8769 aSnapshot = mData->mFirstSnapshot;
8770 else
8771 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
8772
8773 if (!aSnapshot)
8774 {
8775 if (aSetError)
8776 return setError(E_FAIL,
8777 tr("Could not find a snapshot with UUID {%s}"),
8778 aId.toString().c_str());
8779 return E_FAIL;
8780 }
8781
8782 return S_OK;
8783}
8784
8785/**
8786 * Returns the snapshot with the given name or fails of no such snapshot.
8787 *
8788 * @param aName snapshot name to find
8789 * @param aSnapshot where to return the found snapshot
8790 * @param aSetError true to set extended error info on failure
8791 */
8792HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
8793 ComObjPtr<Snapshot> &aSnapshot,
8794 bool aSetError /* = false */)
8795{
8796 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
8797
8798 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
8799
8800 if (!mData->mFirstSnapshot)
8801 {
8802 if (aSetError)
8803 return setError(VBOX_E_OBJECT_NOT_FOUND,
8804 tr("This machine does not have any snapshots"));
8805 return VBOX_E_OBJECT_NOT_FOUND;
8806 }
8807
8808 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
8809
8810 if (!aSnapshot)
8811 {
8812 if (aSetError)
8813 return setError(VBOX_E_OBJECT_NOT_FOUND,
8814 tr("Could not find a snapshot named '%s'"), strName.c_str());
8815 return VBOX_E_OBJECT_NOT_FOUND;
8816 }
8817
8818 return S_OK;
8819}
8820
8821/**
8822 * Returns a storage controller object with the given name.
8823 *
8824 * @param aName storage controller name to find
8825 * @param aStorageController where to return the found storage controller
8826 * @param aSetError true to set extended error info on failure
8827 */
8828HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
8829 ComObjPtr<StorageController> &aStorageController,
8830 bool aSetError /* = false */)
8831{
8832 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
8833
8834 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
8835 it != mStorageControllers->end();
8836 ++it)
8837 {
8838 if ((*it)->getName() == aName)
8839 {
8840 aStorageController = (*it);
8841 return S_OK;
8842 }
8843 }
8844
8845 if (aSetError)
8846 return setError(VBOX_E_OBJECT_NOT_FOUND,
8847 tr("Could not find a storage controller named '%s'"),
8848 aName.c_str());
8849 return VBOX_E_OBJECT_NOT_FOUND;
8850}
8851
8852HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
8853 MediaData::AttachmentList &atts)
8854{
8855 AutoCaller autoCaller(this);
8856 if (FAILED(autoCaller.rc())) return autoCaller.rc();
8857
8858 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8859
8860 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
8861 it != mMediaData->mAttachments.end();
8862 ++it)
8863 {
8864 const ComObjPtr<MediumAttachment> &pAtt = *it;
8865
8866 // should never happen, but deal with NULL pointers in the list.
8867 AssertStmt(!pAtt.isNull(), continue);
8868
8869 // getControllerName() needs caller+read lock
8870 AutoCaller autoAttCaller(pAtt);
8871 if (FAILED(autoAttCaller.rc()))
8872 {
8873 atts.clear();
8874 return autoAttCaller.rc();
8875 }
8876 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
8877
8878 if (pAtt->getControllerName() == aName)
8879 atts.push_back(pAtt);
8880 }
8881
8882 return S_OK;
8883}
8884
8885/**
8886 * Helper for #saveSettings. Cares about renaming the settings directory and
8887 * file if the machine name was changed and about creating a new settings file
8888 * if this is a new machine.
8889 *
8890 * @note Must be never called directly but only from #saveSettings().
8891 */
8892HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
8893{
8894 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
8895
8896 HRESULT rc = S_OK;
8897
8898 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
8899
8900 /* attempt to rename the settings file if machine name is changed */
8901 if ( mUserData->s.fNameSync
8902 && mUserData.isBackedUp()
8903 && mUserData.backedUpData()->s.strName != mUserData->s.strName
8904 )
8905 {
8906 bool dirRenamed = false;
8907 bool fileRenamed = false;
8908
8909 Utf8Str configFile, newConfigFile;
8910 Utf8Str configFilePrev, newConfigFilePrev;
8911 Utf8Str configDir, newConfigDir;
8912
8913 do
8914 {
8915 int vrc = VINF_SUCCESS;
8916
8917 Utf8Str name = mUserData.backedUpData()->s.strName;
8918 Utf8Str newName = mUserData->s.strName;
8919
8920 configFile = mData->m_strConfigFileFull;
8921
8922 /* first, rename the directory if it matches the machine name */
8923 configDir = configFile;
8924 configDir.stripFilename();
8925 newConfigDir = configDir;
8926 if (!strcmp(RTPathFilename(configDir.c_str()), name.c_str()))
8927 {
8928 newConfigDir.stripFilename();
8929 newConfigDir.append(RTPATH_DELIMITER);
8930 newConfigDir.append(newName);
8931 /* new dir and old dir cannot be equal here because of 'if'
8932 * above and because name != newName */
8933 Assert(configDir != newConfigDir);
8934 if (!fSettingsFileIsNew)
8935 {
8936 /* perform real rename only if the machine is not new */
8937 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
8938 if (RT_FAILURE(vrc))
8939 {
8940 rc = setError(E_FAIL,
8941 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
8942 configDir.c_str(),
8943 newConfigDir.c_str(),
8944 vrc);
8945 break;
8946 }
8947 dirRenamed = true;
8948 }
8949 }
8950
8951 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
8952 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
8953
8954 /* then try to rename the settings file itself */
8955 if (newConfigFile != configFile)
8956 {
8957 /* get the path to old settings file in renamed directory */
8958 configFile = Utf8StrFmt("%s%c%s",
8959 newConfigDir.c_str(),
8960 RTPATH_DELIMITER,
8961 RTPathFilename(configFile.c_str()));
8962 if (!fSettingsFileIsNew)
8963 {
8964 /* perform real rename only if the machine is not new */
8965 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
8966 if (RT_FAILURE(vrc))
8967 {
8968 rc = setError(E_FAIL,
8969 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
8970 configFile.c_str(),
8971 newConfigFile.c_str(),
8972 vrc);
8973 break;
8974 }
8975 fileRenamed = true;
8976 configFilePrev = configFile;
8977 configFilePrev += "-prev";
8978 newConfigFilePrev = newConfigFile;
8979 newConfigFilePrev += "-prev";
8980 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
8981 }
8982 }
8983
8984 // update m_strConfigFileFull amd mConfigFile
8985 mData->m_strConfigFileFull = newConfigFile;
8986 // compute the relative path too
8987 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
8988
8989 // store the old and new so that VirtualBox::saveSettings() can update
8990 // the media registry
8991 if ( mData->mRegistered
8992 && configDir != newConfigDir)
8993 {
8994 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
8995
8996 if (pfNeedsGlobalSaveSettings)
8997 *pfNeedsGlobalSaveSettings = true;
8998 }
8999
9000 // in the saved state file path, replace the old directory with the new directory
9001 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9002 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9003
9004 // and do the same thing for the saved state file paths of all the online snapshots
9005 if (mData->mFirstSnapshot)
9006 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9007 newConfigDir.c_str());
9008 }
9009 while (0);
9010
9011 if (FAILED(rc))
9012 {
9013 /* silently try to rename everything back */
9014 if (fileRenamed)
9015 {
9016 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9017 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9018 }
9019 if (dirRenamed)
9020 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9021 }
9022
9023 if (FAILED(rc)) return rc;
9024 }
9025
9026 if (fSettingsFileIsNew)
9027 {
9028 /* create a virgin config file */
9029 int vrc = VINF_SUCCESS;
9030
9031 /* ensure the settings directory exists */
9032 Utf8Str path(mData->m_strConfigFileFull);
9033 path.stripFilename();
9034 if (!RTDirExists(path.c_str()))
9035 {
9036 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9037 if (RT_FAILURE(vrc))
9038 {
9039 return setError(E_FAIL,
9040 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9041 path.c_str(),
9042 vrc);
9043 }
9044 }
9045
9046 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9047 path = Utf8Str(mData->m_strConfigFileFull);
9048 RTFILE f = NIL_RTFILE;
9049 vrc = RTFileOpen(&f, path.c_str(),
9050 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9051 if (RT_FAILURE(vrc))
9052 return setError(E_FAIL,
9053 tr("Could not create the settings file '%s' (%Rrc)"),
9054 path.c_str(),
9055 vrc);
9056 RTFileClose(f);
9057 }
9058
9059 return rc;
9060}
9061
9062/**
9063 * Saves and commits machine data, user data and hardware data.
9064 *
9065 * Note that on failure, the data remains uncommitted.
9066 *
9067 * @a aFlags may combine the following flags:
9068 *
9069 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9070 * Used when saving settings after an operation that makes them 100%
9071 * correspond to the settings from the current snapshot.
9072 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9073 * #isReallyModified() returns false. This is necessary for cases when we
9074 * change machine data directly, not through the backup()/commit() mechanism.
9075 * - SaveS_Force: settings will be saved without doing a deep compare of the
9076 * settings structures. This is used when this is called because snapshots
9077 * have changed to avoid the overhead of the deep compare.
9078 *
9079 * @note Must be called from under this object's write lock. Locks children for
9080 * writing.
9081 *
9082 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9083 * initialized to false and that will be set to true by this function if
9084 * the caller must invoke VirtualBox::saveSettings() because the global
9085 * settings have changed. This will happen if a machine rename has been
9086 * saved and the global machine and media registries will therefore need
9087 * updating.
9088 */
9089HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9090 int aFlags /*= 0*/)
9091{
9092 LogFlowThisFuncEnter();
9093
9094 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9095
9096 /* make sure child objects are unable to modify the settings while we are
9097 * saving them */
9098 ensureNoStateDependencies();
9099
9100 AssertReturn(!isSnapshotMachine(),
9101 E_FAIL);
9102
9103 HRESULT rc = S_OK;
9104 bool fNeedsWrite = false;
9105
9106 /* First, prepare to save settings. It will care about renaming the
9107 * settings directory and file if the machine name was changed and about
9108 * creating a new settings file if this is a new machine. */
9109 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9110 if (FAILED(rc)) return rc;
9111
9112 // keep a pointer to the current settings structures
9113 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9114 settings::MachineConfigFile *pNewConfig = NULL;
9115
9116 try
9117 {
9118 // make a fresh one to have everyone write stuff into
9119 pNewConfig = new settings::MachineConfigFile(NULL);
9120 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9121
9122 // now go and copy all the settings data from COM to the settings structures
9123 // (this calles saveSettings() on all the COM objects in the machine)
9124 copyMachineDataToSettings(*pNewConfig);
9125
9126 if (aFlags & SaveS_ResetCurStateModified)
9127 {
9128 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9129 mData->mCurrentStateModified = FALSE;
9130 fNeedsWrite = true; // always, no need to compare
9131 }
9132 else if (aFlags & SaveS_Force)
9133 {
9134 fNeedsWrite = true; // always, no need to compare
9135 }
9136 else
9137 {
9138 if (!mData->mCurrentStateModified)
9139 {
9140 // do a deep compare of the settings that we just saved with the settings
9141 // previously stored in the config file; this invokes MachineConfigFile::operator==
9142 // which does a deep compare of all the settings, which is expensive but less expensive
9143 // than writing out XML in vain
9144 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9145
9146 // could still be modified if any settings changed
9147 mData->mCurrentStateModified = fAnySettingsChanged;
9148
9149 fNeedsWrite = fAnySettingsChanged;
9150 }
9151 else
9152 fNeedsWrite = true;
9153 }
9154
9155 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9156
9157 if (fNeedsWrite)
9158 // now spit it all out!
9159 pNewConfig->write(mData->m_strConfigFileFull);
9160
9161 mData->pMachineConfigFile = pNewConfig;
9162 delete pOldConfig;
9163 commit();
9164
9165 // after saving settings, we are no longer different from the XML on disk
9166 mData->flModifications = 0;
9167 }
9168 catch (HRESULT err)
9169 {
9170 // we assume that error info is set by the thrower
9171 rc = err;
9172
9173 // restore old config
9174 delete pNewConfig;
9175 mData->pMachineConfigFile = pOldConfig;
9176 }
9177 catch (...)
9178 {
9179 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9180 }
9181
9182 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9183 {
9184 /* Fire the data change event, even on failure (since we've already
9185 * committed all data). This is done only for SessionMachines because
9186 * mutable Machine instances are always not registered (i.e. private
9187 * to the client process that creates them) and thus don't need to
9188 * inform callbacks. */
9189 if (isSessionMachine())
9190 mParent->onMachineDataChange(mData->mUuid);
9191 }
9192
9193 LogFlowThisFunc(("rc=%08X\n", rc));
9194 LogFlowThisFuncLeave();
9195 return rc;
9196}
9197
9198/**
9199 * Implementation for saving the machine settings into the given
9200 * settings::MachineConfigFile instance. This copies machine extradata
9201 * from the previous machine config file in the instance data, if any.
9202 *
9203 * This gets called from two locations:
9204 *
9205 * -- Machine::saveSettings(), during the regular XML writing;
9206 *
9207 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9208 * exported to OVF and we write the VirtualBox proprietary XML
9209 * into a <vbox:Machine> tag.
9210 *
9211 * This routine fills all the fields in there, including snapshots, *except*
9212 * for the following:
9213 *
9214 * -- fCurrentStateModified. There is some special logic associated with that.
9215 *
9216 * The caller can then call MachineConfigFile::write() or do something else
9217 * with it.
9218 *
9219 * Caller must hold the machine lock!
9220 *
9221 * This throws XML errors and HRESULT, so the caller must have a catch block!
9222 */
9223void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
9224{
9225 // deep copy extradata
9226 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9227
9228 config.uuid = mData->mUuid;
9229
9230 // copy name, description, OS type, teleport, UTC etc.
9231 config.machineUserData = mUserData->s;
9232
9233 if ( mData->mMachineState == MachineState_Saved
9234 || mData->mMachineState == MachineState_Restoring
9235 // when deleting a snapshot we may or may not have a saved state in the current state,
9236 // so let's not assert here please
9237 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9238 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9239 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9240 && (!mSSData->strStateFilePath.isEmpty())
9241 )
9242 )
9243 {
9244 Assert(!mSSData->strStateFilePath.isEmpty());
9245 /* try to make the file name relative to the settings file dir */
9246 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9247 }
9248 else
9249 {
9250 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9251 config.strStateFile.setNull();
9252 }
9253
9254 if (mData->mCurrentSnapshot)
9255 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
9256 else
9257 config.uuidCurrentSnapshot.clear();
9258
9259 config.timeLastStateChange = mData->mLastStateChange;
9260 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9261 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9262
9263 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9264 if (FAILED(rc)) throw rc;
9265
9266 rc = saveStorageControllers(config.storageMachine);
9267 if (FAILED(rc)) throw rc;
9268
9269 // save machine's media registry if this is VirtualBox 4.0 or later
9270 if (config.canHaveOwnMediaRegistry())
9271 {
9272 // determine machine folder
9273 Utf8Str strMachineFolder = getSettingsFileFull();
9274 strMachineFolder.stripFilename();
9275 mParent->saveMediaRegistry(config.mediaRegistry,
9276 getId(), // only media with registry ID == machine UUID
9277 strMachineFolder);
9278 // this throws HRESULT
9279 }
9280
9281 // save snapshots
9282 rc = saveAllSnapshots(config);
9283 if (FAILED(rc)) throw rc;
9284}
9285
9286/**
9287 * Saves all snapshots of the machine into the given machine config file. Called
9288 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9289 * @param config
9290 * @return
9291 */
9292HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
9293{
9294 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9295
9296 HRESULT rc = S_OK;
9297
9298 try
9299 {
9300 config.llFirstSnapshot.clear();
9301
9302 if (mData->mFirstSnapshot)
9303 {
9304 settings::Snapshot snapNew;
9305 config.llFirstSnapshot.push_back(snapNew);
9306
9307 // get reference to the fresh copy of the snapshot on the list and
9308 // work on that copy directly to avoid excessive copying later
9309 settings::Snapshot &snap = config.llFirstSnapshot.front();
9310
9311 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
9312 if (FAILED(rc)) throw rc;
9313 }
9314
9315// if (mType == IsSessionMachine)
9316// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9317
9318 }
9319 catch (HRESULT err)
9320 {
9321 /* we assume that error info is set by the thrower */
9322 rc = err;
9323 }
9324 catch (...)
9325 {
9326 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9327 }
9328
9329 return rc;
9330}
9331
9332/**
9333 * Saves the VM hardware configuration. It is assumed that the
9334 * given node is empty.
9335 *
9336 * @param data Reference to the settings object for the hardware config.
9337 * @param pDbg Pointer to the settings object for the debugging config
9338 * which happens to live in mHWData.
9339 * @param pAutostart Pointer to the settings object for the autostart config
9340 * which happens to live in mHWData.
9341 */
9342HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9343 settings::Autostart *pAutostart)
9344{
9345 HRESULT rc = S_OK;
9346
9347 try
9348 {
9349 /* The hardware version attribute (optional).
9350 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9351 if ( mHWData->mHWVersion == "1"
9352 && mSSData->strStateFilePath.isEmpty()
9353 )
9354 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. */
9355
9356 data.strVersion = mHWData->mHWVersion;
9357 data.uuid = mHWData->mHardwareUUID;
9358
9359 // CPU
9360 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9361 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
9362 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9363 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9364 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9365 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9366 data.fPAE = !!mHWData->mPAEEnabled;
9367 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9368
9369 /* Standard and Extended CPUID leafs. */
9370 data.llCpuIdLeafs.clear();
9371 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
9372 {
9373 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9374 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9375 }
9376 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
9377 {
9378 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9379 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9380 }
9381
9382 data.cCPUs = mHWData->mCPUCount;
9383 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9384 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9385
9386 data.llCpus.clear();
9387 if (data.fCpuHotPlug)
9388 {
9389 for (unsigned idx = 0; idx < data.cCPUs; idx++)
9390 {
9391 if (mHWData->mCPUAttached[idx])
9392 {
9393 settings::Cpu cpu;
9394 cpu.ulId = idx;
9395 data.llCpus.push_back(cpu);
9396 }
9397 }
9398 }
9399
9400 // memory
9401 data.ulMemorySizeMB = mHWData->mMemorySize;
9402 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9403
9404 // firmware
9405 data.firmwareType = mHWData->mFirmwareType;
9406
9407 // HID
9408 data.pointingHidType = mHWData->mPointingHidType;
9409 data.keyboardHidType = mHWData->mKeyboardHidType;
9410
9411 // chipset
9412 data.chipsetType = mHWData->mChipsetType;
9413
9414 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9415
9416 // HPET
9417 data.fHpetEnabled = !!mHWData->mHpetEnabled;
9418
9419 // boot order
9420 data.mapBootOrder.clear();
9421 for (size_t i = 0;
9422 i < RT_ELEMENTS(mHWData->mBootOrder);
9423 ++i)
9424 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9425
9426 // display
9427 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9428 data.cMonitors = mHWData->mMonitorCount;
9429 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9430 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9431
9432 /* VRDEServer settings (optional) */
9433 rc = mVRDEServer->saveSettings(data.vrdeSettings);
9434 if (FAILED(rc)) throw rc;
9435
9436 /* BIOS (required) */
9437 rc = mBIOSSettings->saveSettings(data.biosSettings);
9438 if (FAILED(rc)) throw rc;
9439
9440 /* USB Controller (required) */
9441 rc = mUSBController->saveSettings(data.usbController);
9442 if (FAILED(rc)) throw rc;
9443
9444 /* Network adapters (required) */
9445 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
9446 data.llNetworkAdapters.clear();
9447 /* Write out only the nominal number of network adapters for this
9448 * chipset type. Since Machine::commit() hasn't been called there
9449 * may be extra NIC settings in the vector. */
9450 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
9451 {
9452 settings::NetworkAdapter nic;
9453 nic.ulSlot = slot;
9454 /* paranoia check... must not be NULL, but must not crash either. */
9455 if (mNetworkAdapters[slot])
9456 {
9457 rc = mNetworkAdapters[slot]->saveSettings(nic);
9458 if (FAILED(rc)) throw rc;
9459
9460 data.llNetworkAdapters.push_back(nic);
9461 }
9462 }
9463
9464 /* Serial ports */
9465 data.llSerialPorts.clear();
9466 for (ULONG slot = 0;
9467 slot < RT_ELEMENTS(mSerialPorts);
9468 ++slot)
9469 {
9470 settings::SerialPort s;
9471 s.ulSlot = slot;
9472 rc = mSerialPorts[slot]->saveSettings(s);
9473 if (FAILED(rc)) return rc;
9474
9475 data.llSerialPorts.push_back(s);
9476 }
9477
9478 /* Parallel ports */
9479 data.llParallelPorts.clear();
9480 for (ULONG slot = 0;
9481 slot < RT_ELEMENTS(mParallelPorts);
9482 ++slot)
9483 {
9484 settings::ParallelPort p;
9485 p.ulSlot = slot;
9486 rc = mParallelPorts[slot]->saveSettings(p);
9487 if (FAILED(rc)) return rc;
9488
9489 data.llParallelPorts.push_back(p);
9490 }
9491
9492 /* Audio adapter */
9493 rc = mAudioAdapter->saveSettings(data.audioAdapter);
9494 if (FAILED(rc)) return rc;
9495
9496 /* Shared folders */
9497 data.llSharedFolders.clear();
9498 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
9499 it != mHWData->mSharedFolders.end();
9500 ++it)
9501 {
9502 SharedFolder *pSF = *it;
9503 AutoCaller sfCaller(pSF);
9504 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
9505 settings::SharedFolder sf;
9506 sf.strName = pSF->getName();
9507 sf.strHostPath = pSF->getHostPath();
9508 sf.fWritable = !!pSF->isWritable();
9509 sf.fAutoMount = !!pSF->isAutoMounted();
9510
9511 data.llSharedFolders.push_back(sf);
9512 }
9513
9514 // clipboard
9515 data.clipboardMode = mHWData->mClipboardMode;
9516
9517 /* Guest */
9518 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
9519
9520 // IO settings
9521 data.ioSettings.fIoCacheEnabled = !!mHWData->mIoCacheEnabled;
9522 data.ioSettings.ulIoCacheSize = mHWData->mIoCacheSize;
9523
9524 /* BandwidthControl (required) */
9525 rc = mBandwidthControl->saveSettings(data.ioSettings);
9526 if (FAILED(rc)) throw rc;
9527
9528 /* Host PCI devices */
9529 for (HWData::PciDeviceAssignmentList::const_iterator it = mHWData->mPciDeviceAssignments.begin();
9530 it != mHWData->mPciDeviceAssignments.end();
9531 ++it)
9532 {
9533 ComObjPtr<PciDeviceAttachment> pda = *it;
9534 settings::HostPciDeviceAttachment hpda;
9535
9536 rc = pda->saveSettings(hpda);
9537 if (FAILED(rc)) throw rc;
9538
9539 data.pciAttachments.push_back(hpda);
9540 }
9541
9542
9543 // guest properties
9544 data.llGuestProperties.clear();
9545#ifdef VBOX_WITH_GUEST_PROPS
9546 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
9547 it != mHWData->mGuestProperties.end();
9548 ++it)
9549 {
9550 HWData::GuestProperty property = *it;
9551
9552 /* Remove transient guest properties at shutdown unless we
9553 * are saving state */
9554 if ( ( mData->mMachineState == MachineState_PoweredOff
9555 || mData->mMachineState == MachineState_Aborted
9556 || mData->mMachineState == MachineState_Teleported)
9557 && ( property.mFlags & guestProp::TRANSIENT
9558 || property.mFlags & guestProp::TRANSRESET))
9559 continue;
9560 settings::GuestProperty prop;
9561 prop.strName = property.strName;
9562 prop.strValue = property.strValue;
9563 prop.timestamp = property.mTimestamp;
9564 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
9565 guestProp::writeFlags(property.mFlags, szFlags);
9566 prop.strFlags = szFlags;
9567
9568 data.llGuestProperties.push_back(prop);
9569 }
9570
9571 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
9572 /* I presume this doesn't require a backup(). */
9573 mData->mGuestPropertiesModified = FALSE;
9574#endif /* VBOX_WITH_GUEST_PROPS defined */
9575
9576 *pDbg = mHWData->mDebugging;
9577 *pAutostart = mHWData->mAutostart;
9578 }
9579 catch(std::bad_alloc &)
9580 {
9581 return E_OUTOFMEMORY;
9582 }
9583
9584 AssertComRC(rc);
9585 return rc;
9586}
9587
9588/**
9589 * Saves the storage controller configuration.
9590 *
9591 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
9592 */
9593HRESULT Machine::saveStorageControllers(settings::Storage &data)
9594{
9595 data.llStorageControllers.clear();
9596
9597 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9598 it != mStorageControllers->end();
9599 ++it)
9600 {
9601 HRESULT rc;
9602 ComObjPtr<StorageController> pCtl = *it;
9603
9604 settings::StorageController ctl;
9605 ctl.strName = pCtl->getName();
9606 ctl.controllerType = pCtl->getControllerType();
9607 ctl.storageBus = pCtl->getStorageBus();
9608 ctl.ulInstance = pCtl->getInstance();
9609 ctl.fBootable = pCtl->getBootable();
9610
9611 /* Save the port count. */
9612 ULONG portCount;
9613 rc = pCtl->COMGETTER(PortCount)(&portCount);
9614 ComAssertComRCRet(rc, rc);
9615 ctl.ulPortCount = portCount;
9616
9617 /* Save fUseHostIOCache */
9618 BOOL fUseHostIOCache;
9619 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
9620 ComAssertComRCRet(rc, rc);
9621 ctl.fUseHostIOCache = !!fUseHostIOCache;
9622
9623 /* Save IDE emulation settings. */
9624 if (ctl.controllerType == StorageControllerType_IntelAhci)
9625 {
9626 if ( (FAILED(rc = pCtl->GetIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
9627 || (FAILED(rc = pCtl->GetIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
9628 || (FAILED(rc = pCtl->GetIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
9629 || (FAILED(rc = pCtl->GetIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
9630 )
9631 ComAssertComRCRet(rc, rc);
9632 }
9633
9634 /* save the devices now. */
9635 rc = saveStorageDevices(pCtl, ctl);
9636 ComAssertComRCRet(rc, rc);
9637
9638 data.llStorageControllers.push_back(ctl);
9639 }
9640
9641 return S_OK;
9642}
9643
9644/**
9645 * Saves the hard disk configuration.
9646 */
9647HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
9648 settings::StorageController &data)
9649{
9650 MediaData::AttachmentList atts;
9651
9652 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
9653 if (FAILED(rc)) return rc;
9654
9655 data.llAttachedDevices.clear();
9656 for (MediaData::AttachmentList::const_iterator it = atts.begin();
9657 it != atts.end();
9658 ++it)
9659 {
9660 settings::AttachedDevice dev;
9661
9662 MediumAttachment *pAttach = *it;
9663 Medium *pMedium = pAttach->getMedium();
9664
9665 dev.deviceType = pAttach->getType();
9666 dev.lPort = pAttach->getPort();
9667 dev.lDevice = pAttach->getDevice();
9668 if (pMedium)
9669 {
9670 if (pMedium->isHostDrive())
9671 dev.strHostDriveSrc = pMedium->getLocationFull();
9672 else
9673 dev.uuid = pMedium->getId();
9674 dev.fPassThrough = pAttach->getPassthrough();
9675 dev.fTempEject = pAttach->getTempEject();
9676 dev.fDiscard = pAttach->getDiscard();
9677 }
9678
9679 dev.strBwGroup = pAttach->getBandwidthGroup();
9680
9681 data.llAttachedDevices.push_back(dev);
9682 }
9683
9684 return S_OK;
9685}
9686
9687/**
9688 * Saves machine state settings as defined by aFlags
9689 * (SaveSTS_* values).
9690 *
9691 * @param aFlags Combination of SaveSTS_* flags.
9692 *
9693 * @note Locks objects for writing.
9694 */
9695HRESULT Machine::saveStateSettings(int aFlags)
9696{
9697 if (aFlags == 0)
9698 return S_OK;
9699
9700 AutoCaller autoCaller(this);
9701 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9702
9703 /* This object's write lock is also necessary to serialize file access
9704 * (prevent concurrent reads and writes) */
9705 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9706
9707 HRESULT rc = S_OK;
9708
9709 Assert(mData->pMachineConfigFile);
9710
9711 try
9712 {
9713 if (aFlags & SaveSTS_CurStateModified)
9714 mData->pMachineConfigFile->fCurrentStateModified = true;
9715
9716 if (aFlags & SaveSTS_StateFilePath)
9717 {
9718 if (!mSSData->strStateFilePath.isEmpty())
9719 /* try to make the file name relative to the settings file dir */
9720 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
9721 else
9722 mData->pMachineConfigFile->strStateFile.setNull();
9723 }
9724
9725 if (aFlags & SaveSTS_StateTimeStamp)
9726 {
9727 Assert( mData->mMachineState != MachineState_Aborted
9728 || mSSData->strStateFilePath.isEmpty());
9729
9730 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
9731
9732 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
9733//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
9734 }
9735
9736 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
9737 }
9738 catch (...)
9739 {
9740 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9741 }
9742
9743 return rc;
9744}
9745
9746/**
9747 * Ensures that the given medium is added to a media registry. If this machine
9748 * was created with 4.0 or later, then the machine registry is used. Otherwise
9749 * the global VirtualBox media registry is used.
9750 *
9751 * Caller must NOT hold machine lock, media tree or any medium locks!
9752 *
9753 * @param pMedium
9754 */
9755void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
9756{
9757 /* Paranoia checks: do not hold machine or media tree locks. */
9758 AssertReturnVoid(!isWriteLockOnCurrentThread());
9759 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
9760
9761 ComObjPtr<Medium> pBase;
9762 {
9763 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9764 pBase = pMedium->getBase();
9765 }
9766
9767 /* Paranoia checks: do not hold medium locks. */
9768 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
9769 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
9770
9771 // decide which medium registry to use now that the medium is attached:
9772 Guid uuid;
9773 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
9774 // machine XML is VirtualBox 4.0 or higher:
9775 uuid = getId(); // machine UUID
9776 else
9777 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
9778
9779 if (pMedium->addRegistry(uuid, false /* fRecurse */))
9780 mParent->markRegistryModified(uuid);
9781
9782 /* For more complex hard disk structures it can happen that the base
9783 * medium isn't yet associated with any medium registry. Do that now. */
9784 if (pMedium != pBase)
9785 {
9786 if (pBase->addRegistry(uuid, true /* fRecurse */))
9787 mParent->markRegistryModified(uuid);
9788 }
9789}
9790
9791/**
9792 * Creates differencing hard disks for all normal hard disks attached to this
9793 * machine and a new set of attachments to refer to created disks.
9794 *
9795 * Used when taking a snapshot or when deleting the current state. Gets called
9796 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
9797 *
9798 * This method assumes that mMediaData contains the original hard disk attachments
9799 * it needs to create diffs for. On success, these attachments will be replaced
9800 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
9801 * called to delete created diffs which will also rollback mMediaData and restore
9802 * whatever was backed up before calling this method.
9803 *
9804 * Attachments with non-normal hard disks are left as is.
9805 *
9806 * If @a aOnline is @c false then the original hard disks that require implicit
9807 * diffs will be locked for reading. Otherwise it is assumed that they are
9808 * already locked for writing (when the VM was started). Note that in the latter
9809 * case it is responsibility of the caller to lock the newly created diffs for
9810 * writing if this method succeeds.
9811 *
9812 * @param aProgress Progress object to run (must contain at least as
9813 * many operations left as the number of hard disks
9814 * attached).
9815 * @param aOnline Whether the VM was online prior to this operation.
9816 *
9817 * @note The progress object is not marked as completed, neither on success nor
9818 * on failure. This is a responsibility of the caller.
9819 *
9820 * @note Locks this object for writing.
9821 */
9822HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
9823 ULONG aWeight,
9824 bool aOnline)
9825{
9826 LogFlowThisFunc(("aOnline=%d\n", aOnline));
9827
9828 AutoCaller autoCaller(this);
9829 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9830
9831 AutoMultiWriteLock2 alock(this->lockHandle(),
9832 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9833
9834 /* must be in a protective state because we release the lock below */
9835 AssertReturn( mData->mMachineState == MachineState_Saving
9836 || mData->mMachineState == MachineState_LiveSnapshotting
9837 || mData->mMachineState == MachineState_RestoringSnapshot
9838 || mData->mMachineState == MachineState_DeletingSnapshot
9839 , E_FAIL);
9840
9841 HRESULT rc = S_OK;
9842
9843 MediumLockListMap lockedMediaOffline;
9844 MediumLockListMap *lockedMediaMap;
9845 if (aOnline)
9846 lockedMediaMap = &mData->mSession.mLockedMedia;
9847 else
9848 lockedMediaMap = &lockedMediaOffline;
9849
9850 try
9851 {
9852 if (!aOnline)
9853 {
9854 /* lock all attached hard disks early to detect "in use"
9855 * situations before creating actual diffs */
9856 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
9857 it != mMediaData->mAttachments.end();
9858 ++it)
9859 {
9860 MediumAttachment* pAtt = *it;
9861 if (pAtt->getType() == DeviceType_HardDisk)
9862 {
9863 Medium* pMedium = pAtt->getMedium();
9864 Assert(pMedium);
9865
9866 MediumLockList *pMediumLockList(new MediumLockList());
9867 alock.release();
9868 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
9869 false /* fMediumLockWrite */,
9870 NULL,
9871 *pMediumLockList);
9872 alock.acquire();
9873 if (FAILED(rc))
9874 {
9875 delete pMediumLockList;
9876 throw rc;
9877 }
9878 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
9879 if (FAILED(rc))
9880 {
9881 throw setError(rc,
9882 tr("Collecting locking information for all attached media failed"));
9883 }
9884 }
9885 }
9886
9887 /* Now lock all media. If this fails, nothing is locked. */
9888 alock.release();
9889 rc = lockedMediaMap->Lock();
9890 alock.acquire();
9891 if (FAILED(rc))
9892 {
9893 throw setError(rc,
9894 tr("Locking of attached media failed"));
9895 }
9896 }
9897
9898 /* remember the current list (note that we don't use backup() since
9899 * mMediaData may be already backed up) */
9900 MediaData::AttachmentList atts = mMediaData->mAttachments;
9901
9902 /* start from scratch */
9903 mMediaData->mAttachments.clear();
9904
9905 /* go through remembered attachments and create diffs for normal hard
9906 * disks and attach them */
9907 for (MediaData::AttachmentList::const_iterator it = atts.begin();
9908 it != atts.end();
9909 ++it)
9910 {
9911 MediumAttachment* pAtt = *it;
9912
9913 DeviceType_T devType = pAtt->getType();
9914 Medium* pMedium = pAtt->getMedium();
9915
9916 if ( devType != DeviceType_HardDisk
9917 || pMedium == NULL
9918 || pMedium->getType() != MediumType_Normal)
9919 {
9920 /* copy the attachment as is */
9921
9922 /** @todo the progress object created in Console::TakeSnaphot
9923 * only expects operations for hard disks. Later other
9924 * device types need to show up in the progress as well. */
9925 if (devType == DeviceType_HardDisk)
9926 {
9927 if (pMedium == NULL)
9928 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
9929 aWeight); // weight
9930 else
9931 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
9932 pMedium->getBase()->getName().c_str()).raw(),
9933 aWeight); // weight
9934 }
9935
9936 mMediaData->mAttachments.push_back(pAtt);
9937 continue;
9938 }
9939
9940 /* need a diff */
9941 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
9942 pMedium->getBase()->getName().c_str()).raw(),
9943 aWeight); // weight
9944
9945 Utf8Str strFullSnapshotFolder;
9946 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
9947
9948 ComObjPtr<Medium> diff;
9949 diff.createObject();
9950 // store the diff in the same registry as the parent
9951 // (this cannot fail here because we can't create implicit diffs for
9952 // unregistered images)
9953 Guid uuidRegistryParent;
9954 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
9955 Assert(fInRegistry); NOREF(fInRegistry);
9956 rc = diff->init(mParent,
9957 pMedium->getPreferredDiffFormat(),
9958 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
9959 uuidRegistryParent);
9960 if (FAILED(rc)) throw rc;
9961
9962 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
9963 * the push_back? Looks like we're going to release medium with the
9964 * wrong kind of lock (general issue with if we fail anywhere at all)
9965 * and an orphaned VDI in the snapshots folder. */
9966
9967 /* update the appropriate lock list */
9968 MediumLockList *pMediumLockList;
9969 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
9970 AssertComRCThrowRC(rc);
9971 if (aOnline)
9972 {
9973 alock.release();
9974 rc = pMediumLockList->Update(pMedium, false);
9975 alock.acquire();
9976 AssertComRCThrowRC(rc);
9977 }
9978
9979 /* release the locks before the potentially lengthy operation */
9980 alock.release();
9981 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
9982 pMediumLockList,
9983 NULL /* aProgress */,
9984 true /* aWait */);
9985 alock.acquire();
9986 if (FAILED(rc)) throw rc;
9987
9988 rc = lockedMediaMap->Unlock();
9989 AssertComRCThrowRC(rc);
9990 alock.release();
9991 rc = pMediumLockList->Append(diff, true);
9992 alock.acquire();
9993 AssertComRCThrowRC(rc);
9994 alock.release();
9995 rc = lockedMediaMap->Lock();
9996 alock.acquire();
9997 AssertComRCThrowRC(rc);
9998
9999 rc = diff->addBackReference(mData->mUuid);
10000 AssertComRCThrowRC(rc);
10001
10002 /* add a new attachment */
10003 ComObjPtr<MediumAttachment> attachment;
10004 attachment.createObject();
10005 rc = attachment->init(this,
10006 diff,
10007 pAtt->getControllerName(),
10008 pAtt->getPort(),
10009 pAtt->getDevice(),
10010 DeviceType_HardDisk,
10011 true /* aImplicit */,
10012 false /* aPassthrough */,
10013 false /* aTempEject */,
10014 pAtt->getNonRotational(),
10015 pAtt->getDiscard(),
10016 pAtt->getBandwidthGroup());
10017 if (FAILED(rc)) throw rc;
10018
10019 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10020 AssertComRCThrowRC(rc);
10021 mMediaData->mAttachments.push_back(attachment);
10022 }
10023 }
10024 catch (HRESULT aRC) { rc = aRC; }
10025
10026 /* unlock all hard disks we locked */
10027 if (!aOnline)
10028 {
10029 ErrorInfoKeeper eik;
10030
10031 HRESULT rc1 = lockedMediaMap->Clear();
10032 AssertComRC(rc1);
10033 }
10034
10035 if (FAILED(rc))
10036 {
10037 MultiResult mrc = rc;
10038
10039 alock.release();
10040 mrc = deleteImplicitDiffs();
10041 }
10042
10043 return rc;
10044}
10045
10046/**
10047 * Deletes implicit differencing hard disks created either by
10048 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10049 *
10050 * Note that to delete hard disks created by #AttachDevice() this method is
10051 * called from #fixupMedia() when the changes are rolled back.
10052 *
10053 * @note Locks this object for writing.
10054 */
10055HRESULT Machine::deleteImplicitDiffs()
10056{
10057 AutoCaller autoCaller(this);
10058 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10059
10060 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10061 LogFlowThisFuncEnter();
10062
10063 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10064
10065 HRESULT rc = S_OK;
10066
10067 MediaData::AttachmentList implicitAtts;
10068
10069 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10070
10071 /* enumerate new attachments */
10072 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10073 it != mMediaData->mAttachments.end();
10074 ++it)
10075 {
10076 ComObjPtr<Medium> hd = (*it)->getMedium();
10077 if (hd.isNull())
10078 continue;
10079
10080 if ((*it)->isImplicit())
10081 {
10082 /* deassociate and mark for deletion */
10083 LogFlowThisFunc(("Detaching '%s', pending deletion\n", (*it)->getLogName()));
10084 rc = hd->removeBackReference(mData->mUuid);
10085 AssertComRC(rc);
10086 implicitAtts.push_back(*it);
10087 continue;
10088 }
10089
10090 /* was this hard disk attached before? */
10091 if (!findAttachment(oldAtts, hd))
10092 {
10093 /* no: de-associate */
10094 LogFlowThisFunc(("Detaching '%s', no deletion\n", (*it)->getLogName()));
10095 rc = hd->removeBackReference(mData->mUuid);
10096 AssertComRC(rc);
10097 continue;
10098 }
10099 LogFlowThisFunc(("Not detaching '%s'\n", (*it)->getLogName()));
10100 }
10101
10102 /* rollback hard disk changes */
10103 mMediaData.rollback();
10104
10105 MultiResult mrc(S_OK);
10106
10107 /* delete unused implicit diffs */
10108 if (implicitAtts.size() != 0)
10109 {
10110 /* will release the lock before the potentially lengthy
10111 * operation, so protect with the special state (unless already
10112 * protected) */
10113 MachineState_T oldState = mData->mMachineState;
10114 if ( oldState != MachineState_Saving
10115 && oldState != MachineState_LiveSnapshotting
10116 && oldState != MachineState_RestoringSnapshot
10117 && oldState != MachineState_DeletingSnapshot
10118 && oldState != MachineState_DeletingSnapshotOnline
10119 && oldState != MachineState_DeletingSnapshotPaused
10120 )
10121 setMachineState(MachineState_SettingUp);
10122
10123 alock.release();
10124
10125 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
10126 it != implicitAtts.end();
10127 ++it)
10128 {
10129 LogFlowThisFunc(("Deleting '%s'\n", (*it)->getLogName()));
10130 ComObjPtr<Medium> hd = (*it)->getMedium();
10131
10132 rc = hd->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10133 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, (*it)->getLogName(), hd->getLocationFull().c_str() ));
10134 mrc = rc;
10135 }
10136
10137 alock.acquire();
10138
10139 if (mData->mMachineState == MachineState_SettingUp)
10140 setMachineState(oldState);
10141 }
10142
10143 return mrc;
10144}
10145
10146/**
10147 * Looks through the given list of media attachments for one with the given parameters
10148 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10149 * can be searched as well if needed.
10150 *
10151 * @param list
10152 * @param aControllerName
10153 * @param aControllerPort
10154 * @param aDevice
10155 * @return
10156 */
10157MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10158 IN_BSTR aControllerName,
10159 LONG aControllerPort,
10160 LONG aDevice)
10161{
10162 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10163 it != ll.end();
10164 ++it)
10165 {
10166 MediumAttachment *pAttach = *it;
10167 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
10168 return pAttach;
10169 }
10170
10171 return NULL;
10172}
10173
10174/**
10175 * Looks through the given list of media attachments for one with the given parameters
10176 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10177 * can be searched as well if needed.
10178 *
10179 * @param list
10180 * @param aControllerName
10181 * @param aControllerPort
10182 * @param aDevice
10183 * @return
10184 */
10185MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10186 ComObjPtr<Medium> pMedium)
10187{
10188 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10189 it != ll.end();
10190 ++it)
10191 {
10192 MediumAttachment *pAttach = *it;
10193 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10194 if (pMediumThis == pMedium)
10195 return pAttach;
10196 }
10197
10198 return NULL;
10199}
10200
10201/**
10202 * Looks through the given list of media attachments for one with the given parameters
10203 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10204 * can be searched as well if needed.
10205 *
10206 * @param list
10207 * @param aControllerName
10208 * @param aControllerPort
10209 * @param aDevice
10210 * @return
10211 */
10212MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10213 Guid &id)
10214{
10215 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10216 it != ll.end();
10217 ++it)
10218 {
10219 MediumAttachment *pAttach = *it;
10220 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10221 if (pMediumThis->getId() == id)
10222 return pAttach;
10223 }
10224
10225 return NULL;
10226}
10227
10228/**
10229 * Main implementation for Machine::DetachDevice. This also gets called
10230 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10231 *
10232 * @param pAttach Medium attachment to detach.
10233 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10234 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
10235 * @return
10236 */
10237HRESULT Machine::detachDevice(MediumAttachment *pAttach,
10238 AutoWriteLock &writeLock,
10239 Snapshot *pSnapshot)
10240{
10241 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
10242 DeviceType_T mediumType = pAttach->getType();
10243
10244 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
10245
10246 if (pAttach->isImplicit())
10247 {
10248 /* attempt to implicitly delete the implicitly created diff */
10249
10250 /// @todo move the implicit flag from MediumAttachment to Medium
10251 /// and forbid any hard disk operation when it is implicit. Or maybe
10252 /// a special media state for it to make it even more simple.
10253
10254 Assert(mMediaData.isBackedUp());
10255
10256 /* will release the lock before the potentially lengthy operation, so
10257 * protect with the special state */
10258 MachineState_T oldState = mData->mMachineState;
10259 setMachineState(MachineState_SettingUp);
10260
10261 writeLock.release();
10262
10263 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
10264 true /*aWait*/);
10265
10266 writeLock.acquire();
10267
10268 setMachineState(oldState);
10269
10270 if (FAILED(rc)) return rc;
10271 }
10272
10273 setModified(IsModified_Storage);
10274 mMediaData.backup();
10275 mMediaData->mAttachments.remove(pAttach);
10276
10277 if (!oldmedium.isNull())
10278 {
10279 // if this is from a snapshot, do not defer detachment to commitMedia()
10280 if (pSnapshot)
10281 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
10282 // else if non-hard disk media, do not defer detachment to commitMedia() either
10283 else if (mediumType != DeviceType_HardDisk)
10284 oldmedium->removeBackReference(mData->mUuid);
10285 }
10286
10287 return S_OK;
10288}
10289
10290/**
10291 * Goes thru all media of the given list and
10292 *
10293 * 1) calls detachDevice() on each of them for this machine and
10294 * 2) adds all Medium objects found in the process to the given list,
10295 * depending on cleanupMode.
10296 *
10297 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10298 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10299 * media to the list.
10300 *
10301 * This gets called from Machine::Unregister, both for the actual Machine and
10302 * the SnapshotMachine objects that might be found in the snapshots.
10303 *
10304 * Requires caller and locking. The machine lock must be passed in because it
10305 * will be passed on to detachDevice which needs it for temporary unlocking.
10306 *
10307 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
10308 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
10309 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
10310 * otherwise no media get added.
10311 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
10312 * @return
10313 */
10314HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
10315 Snapshot *pSnapshot,
10316 CleanupMode_T cleanupMode,
10317 MediaList &llMedia)
10318{
10319 Assert(isWriteLockOnCurrentThread());
10320
10321 HRESULT rc;
10322
10323 // make a temporary list because detachDevice invalidates iterators into
10324 // mMediaData->mAttachments
10325 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
10326
10327 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
10328 it != llAttachments2.end();
10329 ++it)
10330 {
10331 ComObjPtr<MediumAttachment> &pAttach = *it;
10332 ComObjPtr<Medium> pMedium = pAttach->getMedium();
10333
10334 if (!pMedium.isNull())
10335 {
10336 AutoCaller mac(pMedium);
10337 if (FAILED(mac.rc())) return mac.rc();
10338 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
10339 DeviceType_T devType = pMedium->getDeviceType();
10340 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
10341 && devType == DeviceType_HardDisk)
10342 || (cleanupMode == CleanupMode_Full)
10343 )
10344 {
10345 llMedia.push_back(pMedium);
10346 ComObjPtr<Medium> pParent = pMedium->getParent();
10347 /*
10348 * Search for medias which are not attached to any machine, but
10349 * in the chain to an attached disk. Mediums are only consided
10350 * if they are:
10351 * - have only one child
10352 * - no references to any machines
10353 * - are of normal medium type
10354 */
10355 while (!pParent.isNull())
10356 {
10357 AutoCaller mac1(pParent);
10358 if (FAILED(mac1.rc())) return mac1.rc();
10359 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
10360 if (pParent->getChildren().size() == 1)
10361 {
10362 if ( pParent->getMachineBackRefCount() == 0
10363 && pParent->getType() == MediumType_Normal
10364 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
10365 llMedia.push_back(pParent);
10366 }else
10367 break;
10368 pParent = pParent->getParent();
10369 }
10370 }
10371 }
10372
10373 // real machine: then we need to use the proper method
10374 rc = detachDevice(pAttach, writeLock, pSnapshot);
10375
10376 if (FAILED(rc))
10377 return rc;
10378 }
10379
10380 return S_OK;
10381}
10382
10383/**
10384 * Perform deferred hard disk detachments.
10385 *
10386 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10387 * backed up).
10388 *
10389 * If @a aOnline is @c true then this method will also unlock the old hard disks
10390 * for which the new implicit diffs were created and will lock these new diffs for
10391 * writing.
10392 *
10393 * @param aOnline Whether the VM was online prior to this operation.
10394 *
10395 * @note Locks this object for writing!
10396 */
10397void Machine::commitMedia(bool aOnline /*= false*/)
10398{
10399 AutoCaller autoCaller(this);
10400 AssertComRCReturnVoid(autoCaller.rc());
10401
10402 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10403
10404 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
10405
10406 HRESULT rc = S_OK;
10407
10408 /* no attach/detach operations -- nothing to do */
10409 if (!mMediaData.isBackedUp())
10410 return;
10411
10412 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10413 bool fMediaNeedsLocking = false;
10414
10415 /* enumerate new attachments */
10416 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10417 it != mMediaData->mAttachments.end();
10418 ++it)
10419 {
10420 MediumAttachment *pAttach = *it;
10421
10422 pAttach->commit();
10423
10424 Medium* pMedium = pAttach->getMedium();
10425 bool fImplicit = pAttach->isImplicit();
10426
10427 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
10428 (pMedium) ? pMedium->getName().c_str() : "NULL",
10429 fImplicit));
10430
10431 /** @todo convert all this Machine-based voodoo to MediumAttachment
10432 * based commit logic. */
10433 if (fImplicit)
10434 {
10435 /* convert implicit attachment to normal */
10436 pAttach->setImplicit(false);
10437
10438 if ( aOnline
10439 && pMedium
10440 && pAttach->getType() == DeviceType_HardDisk
10441 )
10442 {
10443 ComObjPtr<Medium> parent = pMedium->getParent();
10444 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
10445
10446 /* update the appropriate lock list */
10447 MediumLockList *pMediumLockList;
10448 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10449 AssertComRC(rc);
10450 if (pMediumLockList)
10451 {
10452 /* unlock if there's a need to change the locking */
10453 if (!fMediaNeedsLocking)
10454 {
10455 rc = mData->mSession.mLockedMedia.Unlock();
10456 AssertComRC(rc);
10457 fMediaNeedsLocking = true;
10458 }
10459 rc = pMediumLockList->Update(parent, false);
10460 AssertComRC(rc);
10461 rc = pMediumLockList->Append(pMedium, true);
10462 AssertComRC(rc);
10463 }
10464 }
10465
10466 continue;
10467 }
10468
10469 if (pMedium)
10470 {
10471 /* was this medium attached before? */
10472 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
10473 oldIt != oldAtts.end();
10474 ++oldIt)
10475 {
10476 MediumAttachment *pOldAttach = *oldIt;
10477 if (pOldAttach->getMedium() == pMedium)
10478 {
10479 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
10480
10481 /* yes: remove from old to avoid de-association */
10482 oldAtts.erase(oldIt);
10483 break;
10484 }
10485 }
10486 }
10487 }
10488
10489 /* enumerate remaining old attachments and de-associate from the
10490 * current machine state */
10491 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
10492 it != oldAtts.end();
10493 ++it)
10494 {
10495 MediumAttachment *pAttach = *it;
10496 Medium* pMedium = pAttach->getMedium();
10497
10498 /* Detach only hard disks, since DVD/floppy media is detached
10499 * instantly in MountMedium. */
10500 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
10501 {
10502 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
10503
10504 /* now de-associate from the current machine state */
10505 rc = pMedium->removeBackReference(mData->mUuid);
10506 AssertComRC(rc);
10507
10508 if (aOnline)
10509 {
10510 /* unlock since medium is not used anymore */
10511 MediumLockList *pMediumLockList;
10512 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10513 AssertComRC(rc);
10514 if (pMediumLockList)
10515 {
10516 rc = mData->mSession.mLockedMedia.Remove(pAttach);
10517 AssertComRC(rc);
10518 }
10519 }
10520 }
10521 }
10522
10523 /* take media locks again so that the locking state is consistent */
10524 if (fMediaNeedsLocking)
10525 {
10526 Assert(aOnline);
10527 rc = mData->mSession.mLockedMedia.Lock();
10528 AssertComRC(rc);
10529 }
10530
10531 /* commit the hard disk changes */
10532 mMediaData.commit();
10533
10534 if (isSessionMachine())
10535 {
10536 /*
10537 * Update the parent machine to point to the new owner.
10538 * This is necessary because the stored parent will point to the
10539 * session machine otherwise and cause crashes or errors later
10540 * when the session machine gets invalid.
10541 */
10542 /** @todo Change the MediumAttachment class to behave like any other
10543 * class in this regard by creating peer MediumAttachment
10544 * objects for session machines and share the data with the peer
10545 * machine.
10546 */
10547 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10548 it != mMediaData->mAttachments.end();
10549 ++it)
10550 {
10551 (*it)->updateParentMachine(mPeer);
10552 }
10553
10554 /* attach new data to the primary machine and reshare it */
10555 mPeer->mMediaData.attach(mMediaData);
10556 }
10557
10558 return;
10559}
10560
10561/**
10562 * Perform deferred deletion of implicitly created diffs.
10563 *
10564 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10565 * backed up).
10566 *
10567 * @note Locks this object for writing!
10568 */
10569void Machine::rollbackMedia()
10570{
10571 AutoCaller autoCaller(this);
10572 AssertComRCReturnVoid (autoCaller.rc());
10573
10574 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10575
10576 LogFlowThisFunc(("Entering\n"));
10577
10578 HRESULT rc = S_OK;
10579
10580 /* no attach/detach operations -- nothing to do */
10581 if (!mMediaData.isBackedUp())
10582 return;
10583
10584 /* enumerate new attachments */
10585 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10586 it != mMediaData->mAttachments.end();
10587 ++it)
10588 {
10589 MediumAttachment *pAttach = *it;
10590 /* Fix up the backrefs for DVD/floppy media. */
10591 if (pAttach->getType() != DeviceType_HardDisk)
10592 {
10593 Medium* pMedium = pAttach->getMedium();
10594 if (pMedium)
10595 {
10596 rc = pMedium->removeBackReference(mData->mUuid);
10597 AssertComRC(rc);
10598 }
10599 }
10600
10601 (*it)->rollback();
10602
10603 pAttach = *it;
10604 /* Fix up the backrefs for DVD/floppy media. */
10605 if (pAttach->getType() != DeviceType_HardDisk)
10606 {
10607 Medium* pMedium = pAttach->getMedium();
10608 if (pMedium)
10609 {
10610 rc = pMedium->addBackReference(mData->mUuid);
10611 AssertComRC(rc);
10612 }
10613 }
10614 }
10615
10616 /** @todo convert all this Machine-based voodoo to MediumAttachment
10617 * based rollback logic. */
10618 deleteImplicitDiffs();
10619
10620 return;
10621}
10622
10623/**
10624 * Returns true if the settings file is located in the directory named exactly
10625 * as the machine; this means, among other things, that the machine directory
10626 * should be auto-renamed.
10627 *
10628 * @param aSettingsDir if not NULL, the full machine settings file directory
10629 * name will be assigned there.
10630 *
10631 * @note Doesn't lock anything.
10632 * @note Not thread safe (must be called from this object's lock).
10633 */
10634bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
10635{
10636 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
10637 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
10638 if (aSettingsDir)
10639 *aSettingsDir = strMachineDirName;
10640 strMachineDirName.stripPath(); // vmname
10641 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
10642 strConfigFileOnly.stripPath() // vmname.vbox
10643 .stripExt(); // vmname
10644
10645 AssertReturn(!strMachineDirName.isEmpty(), false);
10646 AssertReturn(!strConfigFileOnly.isEmpty(), false);
10647
10648 return strMachineDirName == strConfigFileOnly;
10649}
10650
10651/**
10652 * Discards all changes to machine settings.
10653 *
10654 * @param aNotify Whether to notify the direct session about changes or not.
10655 *
10656 * @note Locks objects for writing!
10657 */
10658void Machine::rollback(bool aNotify)
10659{
10660 AutoCaller autoCaller(this);
10661 AssertComRCReturn(autoCaller.rc(), (void)0);
10662
10663 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10664
10665 if (!mStorageControllers.isNull())
10666 {
10667 if (mStorageControllers.isBackedUp())
10668 {
10669 /* unitialize all new devices (absent in the backed up list). */
10670 StorageControllerList::const_iterator it = mStorageControllers->begin();
10671 StorageControllerList *backedList = mStorageControllers.backedUpData();
10672 while (it != mStorageControllers->end())
10673 {
10674 if ( std::find(backedList->begin(), backedList->end(), *it)
10675 == backedList->end()
10676 )
10677 {
10678 (*it)->uninit();
10679 }
10680 ++it;
10681 }
10682
10683 /* restore the list */
10684 mStorageControllers.rollback();
10685 }
10686
10687 /* rollback any changes to devices after restoring the list */
10688 if (mData->flModifications & IsModified_Storage)
10689 {
10690 StorageControllerList::const_iterator it = mStorageControllers->begin();
10691 while (it != mStorageControllers->end())
10692 {
10693 (*it)->rollback();
10694 ++it;
10695 }
10696 }
10697 }
10698
10699 mUserData.rollback();
10700
10701 mHWData.rollback();
10702
10703 if (mData->flModifications & IsModified_Storage)
10704 rollbackMedia();
10705
10706 if (mBIOSSettings)
10707 mBIOSSettings->rollback();
10708
10709 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
10710 mVRDEServer->rollback();
10711
10712 if (mAudioAdapter)
10713 mAudioAdapter->rollback();
10714
10715 if (mUSBController && (mData->flModifications & IsModified_USB))
10716 mUSBController->rollback();
10717
10718 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
10719 mBandwidthControl->rollback();
10720
10721 if (!mHWData.isNull())
10722 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
10723 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
10724 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
10725 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
10726
10727 if (mData->flModifications & IsModified_NetworkAdapters)
10728 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
10729 if ( mNetworkAdapters[slot]
10730 && mNetworkAdapters[slot]->isModified())
10731 {
10732 mNetworkAdapters[slot]->rollback();
10733 networkAdapters[slot] = mNetworkAdapters[slot];
10734 }
10735
10736 if (mData->flModifications & IsModified_SerialPorts)
10737 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
10738 if ( mSerialPorts[slot]
10739 && mSerialPorts[slot]->isModified())
10740 {
10741 mSerialPorts[slot]->rollback();
10742 serialPorts[slot] = mSerialPorts[slot];
10743 }
10744
10745 if (mData->flModifications & IsModified_ParallelPorts)
10746 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
10747 if ( mParallelPorts[slot]
10748 && mParallelPorts[slot]->isModified())
10749 {
10750 mParallelPorts[slot]->rollback();
10751 parallelPorts[slot] = mParallelPorts[slot];
10752 }
10753
10754 if (aNotify)
10755 {
10756 /* inform the direct session about changes */
10757
10758 ComObjPtr<Machine> that = this;
10759 uint32_t flModifications = mData->flModifications;
10760 alock.release();
10761
10762 if (flModifications & IsModified_SharedFolders)
10763 that->onSharedFolderChange();
10764
10765 if (flModifications & IsModified_VRDEServer)
10766 that->onVRDEServerChange(/* aRestart */ TRUE);
10767 if (flModifications & IsModified_USB)
10768 that->onUSBControllerChange();
10769
10770 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
10771 if (networkAdapters[slot])
10772 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
10773 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
10774 if (serialPorts[slot])
10775 that->onSerialPortChange(serialPorts[slot]);
10776 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
10777 if (parallelPorts[slot])
10778 that->onParallelPortChange(parallelPorts[slot]);
10779
10780 if (flModifications & IsModified_Storage)
10781 that->onStorageControllerChange();
10782
10783#if 0
10784 if (flModifications & IsModified_BandwidthControl)
10785 that->onBandwidthControlChange();
10786#endif
10787 }
10788}
10789
10790/**
10791 * Commits all the changes to machine settings.
10792 *
10793 * Note that this operation is supposed to never fail.
10794 *
10795 * @note Locks this object and children for writing.
10796 */
10797void Machine::commit()
10798{
10799 AutoCaller autoCaller(this);
10800 AssertComRCReturnVoid(autoCaller.rc());
10801
10802 AutoCaller peerCaller(mPeer);
10803 AssertComRCReturnVoid(peerCaller.rc());
10804
10805 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
10806
10807 /*
10808 * use safe commit to ensure Snapshot machines (that share mUserData)
10809 * will still refer to a valid memory location
10810 */
10811 mUserData.commitCopy();
10812
10813 mHWData.commit();
10814
10815 if (mMediaData.isBackedUp())
10816 commitMedia();
10817
10818 mBIOSSettings->commit();
10819 mVRDEServer->commit();
10820 mAudioAdapter->commit();
10821 mUSBController->commit();
10822 mBandwidthControl->commit();
10823
10824 /* Keep the original network adapter count until this point, so that
10825 * discarding a chipset type change will not lose settings. */
10826 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
10827 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
10828 mNetworkAdapters[slot]->commit();
10829 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
10830 mSerialPorts[slot]->commit();
10831 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
10832 mParallelPorts[slot]->commit();
10833
10834 bool commitStorageControllers = false;
10835
10836 if (mStorageControllers.isBackedUp())
10837 {
10838 mStorageControllers.commit();
10839
10840 if (mPeer)
10841 {
10842 AutoWriteLock peerlock(mPeer COMMA_LOCKVAL_SRC_POS);
10843
10844 /* Commit all changes to new controllers (this will reshare data with
10845 * peers for those who have peers) */
10846 StorageControllerList *newList = new StorageControllerList();
10847 StorageControllerList::const_iterator it = mStorageControllers->begin();
10848 while (it != mStorageControllers->end())
10849 {
10850 (*it)->commit();
10851
10852 /* look if this controller has a peer device */
10853 ComObjPtr<StorageController> peer = (*it)->getPeer();
10854 if (!peer)
10855 {
10856 /* no peer means the device is a newly created one;
10857 * create a peer owning data this device share it with */
10858 peer.createObject();
10859 peer->init(mPeer, *it, true /* aReshare */);
10860 }
10861 else
10862 {
10863 /* remove peer from the old list */
10864 mPeer->mStorageControllers->remove(peer);
10865 }
10866 /* and add it to the new list */
10867 newList->push_back(peer);
10868
10869 ++it;
10870 }
10871
10872 /* uninit old peer's controllers that are left */
10873 it = mPeer->mStorageControllers->begin();
10874 while (it != mPeer->mStorageControllers->end())
10875 {
10876 (*it)->uninit();
10877 ++it;
10878 }
10879
10880 /* attach new list of controllers to our peer */
10881 mPeer->mStorageControllers.attach(newList);
10882 }
10883 else
10884 {
10885 /* we have no peer (our parent is the newly created machine);
10886 * just commit changes to devices */
10887 commitStorageControllers = true;
10888 }
10889 }
10890 else
10891 {
10892 /* the list of controllers itself is not changed,
10893 * just commit changes to controllers themselves */
10894 commitStorageControllers = true;
10895 }
10896
10897 if (commitStorageControllers)
10898 {
10899 StorageControllerList::const_iterator it = mStorageControllers->begin();
10900 while (it != mStorageControllers->end())
10901 {
10902 (*it)->commit();
10903 ++it;
10904 }
10905 }
10906
10907 if (isSessionMachine())
10908 {
10909 /* attach new data to the primary machine and reshare it */
10910 mPeer->mUserData.attach(mUserData);
10911 mPeer->mHWData.attach(mHWData);
10912 /* mMediaData is reshared by fixupMedia */
10913 // mPeer->mMediaData.attach(mMediaData);
10914 Assert(mPeer->mMediaData.data() == mMediaData.data());
10915 }
10916}
10917
10918/**
10919 * Copies all the hardware data from the given machine.
10920 *
10921 * Currently, only called when the VM is being restored from a snapshot. In
10922 * particular, this implies that the VM is not running during this method's
10923 * call.
10924 *
10925 * @note This method must be called from under this object's lock.
10926 *
10927 * @note This method doesn't call #commit(), so all data remains backed up and
10928 * unsaved.
10929 */
10930void Machine::copyFrom(Machine *aThat)
10931{
10932 AssertReturnVoid(!isSnapshotMachine());
10933 AssertReturnVoid(aThat->isSnapshotMachine());
10934
10935 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
10936
10937 mHWData.assignCopy(aThat->mHWData);
10938
10939 // create copies of all shared folders (mHWData after attaching a copy
10940 // contains just references to original objects)
10941 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
10942 it != mHWData->mSharedFolders.end();
10943 ++it)
10944 {
10945 ComObjPtr<SharedFolder> folder;
10946 folder.createObject();
10947 HRESULT rc = folder->initCopy(getMachine(), *it);
10948 AssertComRC(rc);
10949 *it = folder;
10950 }
10951
10952 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
10953 mVRDEServer->copyFrom(aThat->mVRDEServer);
10954 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
10955 mUSBController->copyFrom(aThat->mUSBController);
10956 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
10957
10958 /* create private copies of all controllers */
10959 mStorageControllers.backup();
10960 mStorageControllers->clear();
10961 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
10962 it != aThat->mStorageControllers->end();
10963 ++it)
10964 {
10965 ComObjPtr<StorageController> ctrl;
10966 ctrl.createObject();
10967 ctrl->initCopy(this, *it);
10968 mStorageControllers->push_back(ctrl);
10969 }
10970
10971 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
10972 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
10973 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
10974 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
10975 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
10976 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
10977}
10978
10979/**
10980 * Returns whether the given storage controller is hotplug capable.
10981 *
10982 * @returns true if the controller supports hotplugging
10983 * false otherwise.
10984 * @param enmCtrlType The controller type to check for.
10985 */
10986bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
10987{
10988 switch (enmCtrlType)
10989 {
10990 case StorageControllerType_IntelAhci:
10991 return true;
10992 case StorageControllerType_LsiLogic:
10993 case StorageControllerType_LsiLogicSas:
10994 case StorageControllerType_BusLogic:
10995 case StorageControllerType_PIIX3:
10996 case StorageControllerType_PIIX4:
10997 case StorageControllerType_ICH6:
10998 case StorageControllerType_I82078:
10999 default:
11000 return false;
11001 }
11002}
11003
11004#ifdef VBOX_WITH_RESOURCE_USAGE_API
11005
11006void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11007{
11008 AssertReturnVoid(isWriteLockOnCurrentThread());
11009 AssertPtrReturnVoid(aCollector);
11010
11011 pm::CollectorHAL *hal = aCollector->getHAL();
11012 /* Create sub metrics */
11013 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11014 "Percentage of processor time spent in user mode by the VM process.");
11015 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11016 "Percentage of processor time spent in kernel mode by the VM process.");
11017 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11018 "Size of resident portion of VM process in memory.");
11019 /* Create and register base metrics */
11020 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11021 cpuLoadUser, cpuLoadKernel);
11022 aCollector->registerBaseMetric(cpuLoad);
11023 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11024 ramUsageUsed);
11025 aCollector->registerBaseMetric(ramUsage);
11026
11027 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11028 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11029 new pm::AggregateAvg()));
11030 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11031 new pm::AggregateMin()));
11032 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11033 new pm::AggregateMax()));
11034 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11035 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11036 new pm::AggregateAvg()));
11037 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11038 new pm::AggregateMin()));
11039 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11040 new pm::AggregateMax()));
11041
11042 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11043 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11044 new pm::AggregateAvg()));
11045 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11046 new pm::AggregateMin()));
11047 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11048 new pm::AggregateMax()));
11049
11050
11051 /* Guest metrics collector */
11052 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11053 aCollector->registerGuest(mCollectorGuest);
11054 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11055 this, __PRETTY_FUNCTION__, mCollectorGuest));
11056
11057 /* Create sub metrics */
11058 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11059 "Percentage of processor time spent in user mode as seen by the guest.");
11060 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11061 "Percentage of processor time spent in kernel mode as seen by the guest.");
11062 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11063 "Percentage of processor time spent idling as seen by the guest.");
11064
11065 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11066 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11067 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11068 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11069 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11070 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11071
11072 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11073
11074 /* Create and register base metrics */
11075 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11076 guestLoadUser, guestLoadKernel, guestLoadIdle);
11077 aCollector->registerBaseMetric(guestCpuLoad);
11078
11079 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11080 guestMemTotal, guestMemFree,
11081 guestMemBalloon, guestMemShared,
11082 guestMemCache, guestPagedTotal);
11083 aCollector->registerBaseMetric(guestCpuMem);
11084
11085 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11086 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11087 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11088 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11089
11090 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11091 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11092 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11093 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11094
11095 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11096 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11097 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11098 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11099
11100 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11101 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11102 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11103 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11104
11105 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11106 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11107 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11108 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11109
11110 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11111 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11112 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11113 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11114
11115 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11116 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11117 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11118 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11119
11120 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11121 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11122 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11123 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11124
11125 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11126 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11127 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11128 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11129}
11130
11131void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11132{
11133 AssertReturnVoid(isWriteLockOnCurrentThread());
11134
11135 if (aCollector)
11136 {
11137 aCollector->unregisterMetricsFor(aMachine);
11138 aCollector->unregisterBaseMetricsFor(aMachine);
11139 }
11140}
11141
11142#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11143
11144
11145////////////////////////////////////////////////////////////////////////////////
11146
11147DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11148
11149HRESULT SessionMachine::FinalConstruct()
11150{
11151 LogFlowThisFunc(("\n"));
11152
11153#if defined(RT_OS_WINDOWS)
11154 mIPCSem = NULL;
11155#elif defined(RT_OS_OS2)
11156 mIPCSem = NULLHANDLE;
11157#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11158 mIPCSem = -1;
11159#else
11160# error "Port me!"
11161#endif
11162
11163 return BaseFinalConstruct();
11164}
11165
11166void SessionMachine::FinalRelease()
11167{
11168 LogFlowThisFunc(("\n"));
11169
11170 uninit(Uninit::Unexpected);
11171
11172 BaseFinalRelease();
11173}
11174
11175/**
11176 * @note Must be called only by Machine::openSession() from its own write lock.
11177 */
11178HRESULT SessionMachine::init(Machine *aMachine)
11179{
11180 LogFlowThisFuncEnter();
11181 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
11182
11183 AssertReturn(aMachine, E_INVALIDARG);
11184
11185 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
11186
11187 /* Enclose the state transition NotReady->InInit->Ready */
11188 AutoInitSpan autoInitSpan(this);
11189 AssertReturn(autoInitSpan.isOk(), E_FAIL);
11190
11191 /* create the interprocess semaphore */
11192#if defined(RT_OS_WINDOWS)
11193 mIPCSemName = aMachine->mData->m_strConfigFileFull;
11194 for (size_t i = 0; i < mIPCSemName.length(); i++)
11195 if (mIPCSemName.raw()[i] == '\\')
11196 mIPCSemName.raw()[i] = '/';
11197 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
11198 ComAssertMsgRet(mIPCSem,
11199 ("Cannot create IPC mutex '%ls', err=%d",
11200 mIPCSemName.raw(), ::GetLastError()),
11201 E_FAIL);
11202#elif defined(RT_OS_OS2)
11203 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
11204 aMachine->mData->mUuid.raw());
11205 mIPCSemName = ipcSem;
11206 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
11207 ComAssertMsgRet(arc == NO_ERROR,
11208 ("Cannot create IPC mutex '%s', arc=%ld",
11209 ipcSem.c_str(), arc),
11210 E_FAIL);
11211#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11212# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11213# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
11214 /** @todo Check that this still works correctly. */
11215 AssertCompileSize(key_t, 8);
11216# else
11217 AssertCompileSize(key_t, 4);
11218# endif
11219 key_t key;
11220 mIPCSem = -1;
11221 mIPCKey = "0";
11222 for (uint32_t i = 0; i < 1 << 24; i++)
11223 {
11224 key = ((uint32_t)'V' << 24) | i;
11225 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
11226 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
11227 {
11228 mIPCSem = sem;
11229 if (sem >= 0)
11230 mIPCKey = BstrFmt("%u", key);
11231 break;
11232 }
11233 }
11234# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11235 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
11236 char *pszSemName = NULL;
11237 RTStrUtf8ToCurrentCP(&pszSemName, semName);
11238 key_t key = ::ftok(pszSemName, 'V');
11239 RTStrFree(pszSemName);
11240
11241 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
11242# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11243
11244 int errnoSave = errno;
11245 if (mIPCSem < 0 && errnoSave == ENOSYS)
11246 {
11247 setError(E_FAIL,
11248 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
11249 "support for SysV IPC. Check the host kernel configuration for "
11250 "CONFIG_SYSVIPC=y"));
11251 return E_FAIL;
11252 }
11253 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
11254 * the IPC semaphores */
11255 if (mIPCSem < 0 && errnoSave == ENOSPC)
11256 {
11257#ifdef RT_OS_LINUX
11258 setError(E_FAIL,
11259 tr("Cannot create IPC semaphore because the system limit for the "
11260 "maximum number of semaphore sets (SEMMNI), or the system wide "
11261 "maximum number of semaphores (SEMMNS) would be exceeded. The "
11262 "current set of SysV IPC semaphores can be determined from "
11263 "the file /proc/sysvipc/sem"));
11264#else
11265 setError(E_FAIL,
11266 tr("Cannot create IPC semaphore because the system-imposed limit "
11267 "on the maximum number of allowed semaphores or semaphore "
11268 "identifiers system-wide would be exceeded"));
11269#endif
11270 return E_FAIL;
11271 }
11272 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
11273 E_FAIL);
11274 /* set the initial value to 1 */
11275 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
11276 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
11277 E_FAIL);
11278#else
11279# error "Port me!"
11280#endif
11281
11282 /* memorize the peer Machine */
11283 unconst(mPeer) = aMachine;
11284 /* share the parent pointer */
11285 unconst(mParent) = aMachine->mParent;
11286
11287 /* take the pointers to data to share */
11288 mData.share(aMachine->mData);
11289 mSSData.share(aMachine->mSSData);
11290
11291 mUserData.share(aMachine->mUserData);
11292 mHWData.share(aMachine->mHWData);
11293 mMediaData.share(aMachine->mMediaData);
11294
11295 mStorageControllers.allocate();
11296 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
11297 it != aMachine->mStorageControllers->end();
11298 ++it)
11299 {
11300 ComObjPtr<StorageController> ctl;
11301 ctl.createObject();
11302 ctl->init(this, *it);
11303 mStorageControllers->push_back(ctl);
11304 }
11305
11306 unconst(mBIOSSettings).createObject();
11307 mBIOSSettings->init(this, aMachine->mBIOSSettings);
11308 /* create another VRDEServer object that will be mutable */
11309 unconst(mVRDEServer).createObject();
11310 mVRDEServer->init(this, aMachine->mVRDEServer);
11311 /* create another audio adapter object that will be mutable */
11312 unconst(mAudioAdapter).createObject();
11313 mAudioAdapter->init(this, aMachine->mAudioAdapter);
11314 /* create a list of serial ports that will be mutable */
11315 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11316 {
11317 unconst(mSerialPorts[slot]).createObject();
11318 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
11319 }
11320 /* create a list of parallel ports that will be mutable */
11321 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11322 {
11323 unconst(mParallelPorts[slot]).createObject();
11324 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
11325 }
11326 /* create another USB controller object that will be mutable */
11327 unconst(mUSBController).createObject();
11328 mUSBController->init(this, aMachine->mUSBController);
11329
11330 /* create a list of network adapters that will be mutable */
11331 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
11332 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11333 {
11334 unconst(mNetworkAdapters[slot]).createObject();
11335 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
11336 }
11337
11338 /* create another bandwidth control object that will be mutable */
11339 unconst(mBandwidthControl).createObject();
11340 mBandwidthControl->init(this, aMachine->mBandwidthControl);
11341
11342 /* default is to delete saved state on Saved -> PoweredOff transition */
11343 mRemoveSavedState = true;
11344
11345 /* Confirm a successful initialization when it's the case */
11346 autoInitSpan.setSucceeded();
11347
11348 LogFlowThisFuncLeave();
11349 return S_OK;
11350}
11351
11352/**
11353 * Uninitializes this session object. If the reason is other than
11354 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
11355 *
11356 * @param aReason uninitialization reason
11357 *
11358 * @note Locks mParent + this object for writing.
11359 */
11360void SessionMachine::uninit(Uninit::Reason aReason)
11361{
11362 LogFlowThisFuncEnter();
11363 LogFlowThisFunc(("reason=%d\n", aReason));
11364
11365 /*
11366 * Strongly reference ourselves to prevent this object deletion after
11367 * mData->mSession.mMachine.setNull() below (which can release the last
11368 * reference and call the destructor). Important: this must be done before
11369 * accessing any members (and before AutoUninitSpan that does it as well).
11370 * This self reference will be released as the very last step on return.
11371 */
11372 ComObjPtr<SessionMachine> selfRef = this;
11373
11374 /* Enclose the state transition Ready->InUninit->NotReady */
11375 AutoUninitSpan autoUninitSpan(this);
11376 if (autoUninitSpan.uninitDone())
11377 {
11378 LogFlowThisFunc(("Already uninitialized\n"));
11379 LogFlowThisFuncLeave();
11380 return;
11381 }
11382
11383 if (autoUninitSpan.initFailed())
11384 {
11385 /* We've been called by init() because it's failed. It's not really
11386 * necessary (nor it's safe) to perform the regular uninit sequence
11387 * below, the following is enough.
11388 */
11389 LogFlowThisFunc(("Initialization failed.\n"));
11390#if defined(RT_OS_WINDOWS)
11391 if (mIPCSem)
11392 ::CloseHandle(mIPCSem);
11393 mIPCSem = NULL;
11394#elif defined(RT_OS_OS2)
11395 if (mIPCSem != NULLHANDLE)
11396 ::DosCloseMutexSem(mIPCSem);
11397 mIPCSem = NULLHANDLE;
11398#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11399 if (mIPCSem >= 0)
11400 ::semctl(mIPCSem, 0, IPC_RMID);
11401 mIPCSem = -1;
11402# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11403 mIPCKey = "0";
11404# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
11405#else
11406# error "Port me!"
11407#endif
11408 uninitDataAndChildObjects();
11409 mData.free();
11410 unconst(mParent) = NULL;
11411 unconst(mPeer) = NULL;
11412 LogFlowThisFuncLeave();
11413 return;
11414 }
11415
11416 MachineState_T lastState;
11417 {
11418 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
11419 lastState = mData->mMachineState;
11420 }
11421 NOREF(lastState);
11422
11423#ifdef VBOX_WITH_USB
11424 // release all captured USB devices, but do this before requesting the locks below
11425 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
11426 {
11427 /* Console::captureUSBDevices() is called in the VM process only after
11428 * setting the machine state to Starting or Restoring.
11429 * Console::detachAllUSBDevices() will be called upon successful
11430 * termination. So, we need to release USB devices only if there was
11431 * an abnormal termination of a running VM.
11432 *
11433 * This is identical to SessionMachine::DetachAllUSBDevices except
11434 * for the aAbnormal argument. */
11435 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
11436 AssertComRC(rc);
11437 NOREF(rc);
11438
11439 USBProxyService *service = mParent->host()->usbProxyService();
11440 if (service)
11441 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
11442 }
11443#endif /* VBOX_WITH_USB */
11444
11445 // we need to lock this object in uninit() because the lock is shared
11446 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
11447 // and others need mParent lock, and USB needs host lock.
11448 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
11449
11450#if 0
11451 // Trigger async cleanup tasks, avoid doing things here which are not
11452 // vital to be done immediately and maybe need more locks. This calls
11453 // Machine::unregisterMetrics().
11454 mParent->onMachineUninit(mPeer);
11455#else
11456 /*
11457 * It is safe to call Machine::unregisterMetrics() here because
11458 * PerformanceCollector::samplerCallback no longer accesses guest methods
11459 * holding the lock.
11460 */
11461 unregisterMetrics(mParent->performanceCollector(), mPeer);
11462#endif
11463 /* The guest must be unregistered after its metrics (@bugref{5949}). */
11464 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11465 this, __PRETTY_FUNCTION__, mCollectorGuest));
11466 if (mCollectorGuest)
11467 {
11468 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
11469 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
11470 mCollectorGuest = NULL;
11471 }
11472
11473 if (aReason == Uninit::Abnormal)
11474 {
11475 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
11476 Global::IsOnlineOrTransient(lastState)));
11477
11478 /* reset the state to Aborted */
11479 if (mData->mMachineState != MachineState_Aborted)
11480 setMachineState(MachineState_Aborted);
11481 }
11482
11483 // any machine settings modified?
11484 if (mData->flModifications)
11485 {
11486 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
11487 rollback(false /* aNotify */);
11488 }
11489
11490 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
11491 || !mConsoleTaskData.mSnapshot);
11492 if (!mConsoleTaskData.strStateFilePath.isEmpty())
11493 {
11494 LogWarningThisFunc(("canceling failed save state request!\n"));
11495 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
11496 }
11497 else if (!mConsoleTaskData.mSnapshot.isNull())
11498 {
11499 LogWarningThisFunc(("canceling untaken snapshot!\n"));
11500
11501 /* delete all differencing hard disks created (this will also attach
11502 * their parents back by rolling back mMediaData) */
11503 rollbackMedia();
11504
11505 // delete the saved state file (it might have been already created)
11506 // AFTER killing the snapshot so that releaseSavedStateFile() won't
11507 // think it's still in use
11508 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
11509 mConsoleTaskData.mSnapshot->uninit();
11510 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
11511 }
11512
11513 if (!mData->mSession.mType.isEmpty())
11514 {
11515 /* mType is not null when this machine's process has been started by
11516 * Machine::LaunchVMProcess(), therefore it is our child. We
11517 * need to queue the PID to reap the process (and avoid zombies on
11518 * Linux). */
11519 Assert(mData->mSession.mPid != NIL_RTPROCESS);
11520 mParent->addProcessToReap(mData->mSession.mPid);
11521 }
11522
11523 mData->mSession.mPid = NIL_RTPROCESS;
11524
11525 if (aReason == Uninit::Unexpected)
11526 {
11527 /* Uninitialization didn't come from #checkForDeath(), so tell the
11528 * client watcher thread to update the set of machines that have open
11529 * sessions. */
11530 mParent->updateClientWatcher();
11531 }
11532
11533 /* uninitialize all remote controls */
11534 if (mData->mSession.mRemoteControls.size())
11535 {
11536 LogFlowThisFunc(("Closing remote sessions (%d):\n",
11537 mData->mSession.mRemoteControls.size()));
11538
11539 Data::Session::RemoteControlList::iterator it =
11540 mData->mSession.mRemoteControls.begin();
11541 while (it != mData->mSession.mRemoteControls.end())
11542 {
11543 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
11544 HRESULT rc = (*it)->Uninitialize();
11545 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
11546 if (FAILED(rc))
11547 LogWarningThisFunc(("Forgot to close the remote session?\n"));
11548 ++it;
11549 }
11550 mData->mSession.mRemoteControls.clear();
11551 }
11552
11553 /*
11554 * An expected uninitialization can come only from #checkForDeath().
11555 * Otherwise it means that something's gone really wrong (for example,
11556 * the Session implementation has released the VirtualBox reference
11557 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
11558 * etc). However, it's also possible, that the client releases the IPC
11559 * semaphore correctly (i.e. before it releases the VirtualBox reference),
11560 * but the VirtualBox release event comes first to the server process.
11561 * This case is practically possible, so we should not assert on an
11562 * unexpected uninit, just log a warning.
11563 */
11564
11565 if ((aReason == Uninit::Unexpected))
11566 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
11567
11568 if (aReason != Uninit::Normal)
11569 {
11570 mData->mSession.mDirectControl.setNull();
11571 }
11572 else
11573 {
11574 /* this must be null here (see #OnSessionEnd()) */
11575 Assert(mData->mSession.mDirectControl.isNull());
11576 Assert(mData->mSession.mState == SessionState_Unlocking);
11577 Assert(!mData->mSession.mProgress.isNull());
11578 }
11579 if (mData->mSession.mProgress)
11580 {
11581 if (aReason == Uninit::Normal)
11582 mData->mSession.mProgress->notifyComplete(S_OK);
11583 else
11584 mData->mSession.mProgress->notifyComplete(E_FAIL,
11585 COM_IIDOF(ISession),
11586 getComponentName(),
11587 tr("The VM session was aborted"));
11588 mData->mSession.mProgress.setNull();
11589 }
11590
11591 /* remove the association between the peer machine and this session machine */
11592 Assert( (SessionMachine*)mData->mSession.mMachine == this
11593 || aReason == Uninit::Unexpected);
11594
11595 /* reset the rest of session data */
11596 mData->mSession.mMachine.setNull();
11597 mData->mSession.mState = SessionState_Unlocked;
11598 mData->mSession.mType.setNull();
11599
11600 /* close the interprocess semaphore before leaving the exclusive lock */
11601#if defined(RT_OS_WINDOWS)
11602 if (mIPCSem)
11603 ::CloseHandle(mIPCSem);
11604 mIPCSem = NULL;
11605#elif defined(RT_OS_OS2)
11606 if (mIPCSem != NULLHANDLE)
11607 ::DosCloseMutexSem(mIPCSem);
11608 mIPCSem = NULLHANDLE;
11609#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11610 if (mIPCSem >= 0)
11611 ::semctl(mIPCSem, 0, IPC_RMID);
11612 mIPCSem = -1;
11613# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11614 mIPCKey = "0";
11615# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
11616#else
11617# error "Port me!"
11618#endif
11619
11620 /* fire an event */
11621 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
11622
11623 uninitDataAndChildObjects();
11624
11625 /* free the essential data structure last */
11626 mData.free();
11627
11628 /* release the exclusive lock before setting the below two to NULL */
11629 multilock.release();
11630
11631 unconst(mParent) = NULL;
11632 unconst(mPeer) = NULL;
11633
11634 LogFlowThisFuncLeave();
11635}
11636
11637// util::Lockable interface
11638////////////////////////////////////////////////////////////////////////////////
11639
11640/**
11641 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
11642 * with the primary Machine instance (mPeer).
11643 */
11644RWLockHandle *SessionMachine::lockHandle() const
11645{
11646 AssertReturn(mPeer != NULL, NULL);
11647 return mPeer->lockHandle();
11648}
11649
11650// IInternalMachineControl methods
11651////////////////////////////////////////////////////////////////////////////////
11652
11653/**
11654 * Passes collected guest statistics to performance collector object
11655 */
11656STDMETHODIMP SessionMachine::ReportGuestStatistics(ULONG aValidStats, ULONG aCpuUser,
11657 ULONG aCpuKernel, ULONG aCpuIdle,
11658 ULONG aMemTotal, ULONG aMemFree,
11659 ULONG aMemBalloon, ULONG aMemShared,
11660 ULONG aMemCache, ULONG aPageTotal,
11661 ULONG aAllocVMM, ULONG aFreeVMM,
11662 ULONG aBalloonedVMM, ULONG aSharedVMM)
11663{
11664 if (mCollectorGuest)
11665 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
11666 aMemTotal, aMemFree, aMemBalloon, aMemShared,
11667 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
11668 aBalloonedVMM, aSharedVMM);
11669
11670 return S_OK;
11671}
11672
11673/**
11674 * @note Locks this object for writing.
11675 */
11676STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
11677{
11678 AutoCaller autoCaller(this);
11679 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11680
11681 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11682
11683 mRemoveSavedState = aRemove;
11684
11685 return S_OK;
11686}
11687
11688/**
11689 * @note Locks the same as #setMachineState() does.
11690 */
11691STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
11692{
11693 return setMachineState(aMachineState);
11694}
11695
11696/**
11697 * @note Locks this object for reading.
11698 */
11699STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
11700{
11701 AutoCaller autoCaller(this);
11702 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11703
11704 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11705
11706#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
11707 mIPCSemName.cloneTo(aId);
11708 return S_OK;
11709#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11710# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11711 mIPCKey.cloneTo(aId);
11712# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11713 mData->m_strConfigFileFull.cloneTo(aId);
11714# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11715 return S_OK;
11716#else
11717# error "Port me!"
11718#endif
11719}
11720
11721/**
11722 * @note Locks this object for writing.
11723 */
11724STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
11725{
11726 LogFlowThisFunc(("aProgress=%p\n", aProgress));
11727 AutoCaller autoCaller(this);
11728 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11729
11730 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11731
11732 if (mData->mSession.mState != SessionState_Locked)
11733 return VBOX_E_INVALID_OBJECT_STATE;
11734
11735 if (!mData->mSession.mProgress.isNull())
11736 mData->mSession.mProgress->setOtherProgressObject(aProgress);
11737
11738 LogFlowThisFunc(("returns S_OK.\n"));
11739 return S_OK;
11740}
11741
11742/**
11743 * @note Locks this object for writing.
11744 */
11745STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
11746{
11747 AutoCaller autoCaller(this);
11748 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11749
11750 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11751
11752 if (mData->mSession.mState != SessionState_Locked)
11753 return VBOX_E_INVALID_OBJECT_STATE;
11754
11755 /* Finalize the LaunchVMProcess progress object. */
11756 if (mData->mSession.mProgress)
11757 {
11758 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
11759 mData->mSession.mProgress.setNull();
11760 }
11761
11762 if (SUCCEEDED((HRESULT)iResult))
11763 {
11764#ifdef VBOX_WITH_RESOURCE_USAGE_API
11765 /* The VM has been powered up successfully, so it makes sense
11766 * now to offer the performance metrics for a running machine
11767 * object. Doing it earlier wouldn't be safe. */
11768 registerMetrics(mParent->performanceCollector(), mPeer,
11769 mData->mSession.mPid);
11770#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11771 }
11772
11773 return S_OK;
11774}
11775
11776/**
11777 * @note Locks this object for writing.
11778 */
11779STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
11780{
11781 LogFlowThisFuncEnter();
11782
11783 CheckComArgOutPointerValid(aProgress);
11784
11785 AutoCaller autoCaller(this);
11786 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11787
11788 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11789
11790 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
11791 E_FAIL);
11792
11793 /* create a progress object to track operation completion */
11794 ComObjPtr<Progress> pProgress;
11795 pProgress.createObject();
11796 pProgress->init(getVirtualBox(),
11797 static_cast<IMachine *>(this) /* aInitiator */,
11798 Bstr(tr("Stopping the virtual machine")).raw(),
11799 FALSE /* aCancelable */);
11800
11801 /* fill in the console task data */
11802 mConsoleTaskData.mLastState = mData->mMachineState;
11803 mConsoleTaskData.mProgress = pProgress;
11804
11805 /* set the state to Stopping (this is expected by Console::PowerDown()) */
11806 setMachineState(MachineState_Stopping);
11807
11808 pProgress.queryInterfaceTo(aProgress);
11809
11810 return S_OK;
11811}
11812
11813/**
11814 * @note Locks this object for writing.
11815 */
11816STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
11817{
11818 LogFlowThisFuncEnter();
11819
11820 AutoCaller autoCaller(this);
11821 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11822
11823 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11824
11825 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
11826 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
11827 && mConsoleTaskData.mLastState != MachineState_Null,
11828 E_FAIL);
11829
11830 /*
11831 * On failure, set the state to the state we had when BeginPoweringDown()
11832 * was called (this is expected by Console::PowerDown() and the associated
11833 * task). On success the VM process already changed the state to
11834 * MachineState_PoweredOff, so no need to do anything.
11835 */
11836 if (FAILED(iResult))
11837 setMachineState(mConsoleTaskData.mLastState);
11838
11839 /* notify the progress object about operation completion */
11840 Assert(mConsoleTaskData.mProgress);
11841 if (SUCCEEDED(iResult))
11842 mConsoleTaskData.mProgress->notifyComplete(S_OK);
11843 else
11844 {
11845 Utf8Str strErrMsg(aErrMsg);
11846 if (strErrMsg.length())
11847 mConsoleTaskData.mProgress->notifyComplete(iResult,
11848 COM_IIDOF(ISession),
11849 getComponentName(),
11850 strErrMsg.c_str());
11851 else
11852 mConsoleTaskData.mProgress->notifyComplete(iResult);
11853 }
11854
11855 /* clear out the temporary saved state data */
11856 mConsoleTaskData.mLastState = MachineState_Null;
11857 mConsoleTaskData.mProgress.setNull();
11858
11859 LogFlowThisFuncLeave();
11860 return S_OK;
11861}
11862
11863
11864/**
11865 * Goes through the USB filters of the given machine to see if the given
11866 * device matches any filter or not.
11867 *
11868 * @note Locks the same as USBController::hasMatchingFilter() does.
11869 */
11870STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
11871 BOOL *aMatched,
11872 ULONG *aMaskedIfs)
11873{
11874 LogFlowThisFunc(("\n"));
11875
11876 CheckComArgNotNull(aUSBDevice);
11877 CheckComArgOutPointerValid(aMatched);
11878
11879 AutoCaller autoCaller(this);
11880 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11881
11882#ifdef VBOX_WITH_USB
11883 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
11884#else
11885 NOREF(aUSBDevice);
11886 NOREF(aMaskedIfs);
11887 *aMatched = FALSE;
11888#endif
11889
11890 return S_OK;
11891}
11892
11893/**
11894 * @note Locks the same as Host::captureUSBDevice() does.
11895 */
11896STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
11897{
11898 LogFlowThisFunc(("\n"));
11899
11900 AutoCaller autoCaller(this);
11901 AssertComRCReturnRC(autoCaller.rc());
11902
11903#ifdef VBOX_WITH_USB
11904 /* if captureDeviceForVM() fails, it must have set extended error info */
11905 clearError();
11906 MultiResult rc = mParent->host()->checkUSBProxyService();
11907 if (FAILED(rc)) return rc;
11908
11909 USBProxyService *service = mParent->host()->usbProxyService();
11910 AssertReturn(service, E_FAIL);
11911 return service->captureDeviceForVM(this, Guid(aId).ref());
11912#else
11913 NOREF(aId);
11914 return E_NOTIMPL;
11915#endif
11916}
11917
11918/**
11919 * @note Locks the same as Host::detachUSBDevice() does.
11920 */
11921STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
11922{
11923 LogFlowThisFunc(("\n"));
11924
11925 AutoCaller autoCaller(this);
11926 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11927
11928#ifdef VBOX_WITH_USB
11929 USBProxyService *service = mParent->host()->usbProxyService();
11930 AssertReturn(service, E_FAIL);
11931 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
11932#else
11933 NOREF(aId);
11934 NOREF(aDone);
11935 return E_NOTIMPL;
11936#endif
11937}
11938
11939/**
11940 * Inserts all machine filters to the USB proxy service and then calls
11941 * Host::autoCaptureUSBDevices().
11942 *
11943 * Called by Console from the VM process upon VM startup.
11944 *
11945 * @note Locks what called methods lock.
11946 */
11947STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
11948{
11949 LogFlowThisFunc(("\n"));
11950
11951 AutoCaller autoCaller(this);
11952 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11953
11954#ifdef VBOX_WITH_USB
11955 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
11956 AssertComRC(rc);
11957 NOREF(rc);
11958
11959 USBProxyService *service = mParent->host()->usbProxyService();
11960 AssertReturn(service, E_FAIL);
11961 return service->autoCaptureDevicesForVM(this);
11962#else
11963 return S_OK;
11964#endif
11965}
11966
11967/**
11968 * Removes all machine filters from the USB proxy service and then calls
11969 * Host::detachAllUSBDevices().
11970 *
11971 * Called by Console from the VM process upon normal VM termination or by
11972 * SessionMachine::uninit() upon abnormal VM termination (from under the
11973 * Machine/SessionMachine lock).
11974 *
11975 * @note Locks what called methods lock.
11976 */
11977STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
11978{
11979 LogFlowThisFunc(("\n"));
11980
11981 AutoCaller autoCaller(this);
11982 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11983
11984#ifdef VBOX_WITH_USB
11985 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
11986 AssertComRC(rc);
11987 NOREF(rc);
11988
11989 USBProxyService *service = mParent->host()->usbProxyService();
11990 AssertReturn(service, E_FAIL);
11991 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
11992#else
11993 NOREF(aDone);
11994 return S_OK;
11995#endif
11996}
11997
11998/**
11999 * @note Locks this object for writing.
12000 */
12001STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12002 IProgress **aProgress)
12003{
12004 LogFlowThisFuncEnter();
12005
12006 AssertReturn(aSession, E_INVALIDARG);
12007 AssertReturn(aProgress, E_INVALIDARG);
12008
12009 AutoCaller autoCaller(this);
12010
12011 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
12012 /*
12013 * We don't assert below because it might happen that a non-direct session
12014 * informs us it is closed right after we've been uninitialized -- it's ok.
12015 */
12016 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12017
12018 /* get IInternalSessionControl interface */
12019 ComPtr<IInternalSessionControl> control(aSession);
12020
12021 ComAssertRet(!control.isNull(), E_INVALIDARG);
12022
12023 /* Creating a Progress object requires the VirtualBox lock, and
12024 * thus locking it here is required by the lock order rules. */
12025 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12026
12027 if (control == mData->mSession.mDirectControl)
12028 {
12029 ComAssertRet(aProgress, E_POINTER);
12030
12031 /* The direct session is being normally closed by the client process
12032 * ----------------------------------------------------------------- */
12033
12034 /* go to the closing state (essential for all open*Session() calls and
12035 * for #checkForDeath()) */
12036 Assert(mData->mSession.mState == SessionState_Locked);
12037 mData->mSession.mState = SessionState_Unlocking;
12038
12039 /* set direct control to NULL to release the remote instance */
12040 mData->mSession.mDirectControl.setNull();
12041 LogFlowThisFunc(("Direct control is set to NULL\n"));
12042
12043 if (mData->mSession.mProgress)
12044 {
12045 /* finalize the progress, someone might wait if a frontend
12046 * closes the session before powering on the VM. */
12047 mData->mSession.mProgress->notifyComplete(E_FAIL,
12048 COM_IIDOF(ISession),
12049 getComponentName(),
12050 tr("The VM session was closed before any attempt to power it on"));
12051 mData->mSession.mProgress.setNull();
12052 }
12053
12054 /* Create the progress object the client will use to wait until
12055 * #checkForDeath() is called to uninitialize this session object after
12056 * it releases the IPC semaphore.
12057 * Note! Because we're "reusing" mProgress here, this must be a proxy
12058 * object just like for LaunchVMProcess. */
12059 Assert(mData->mSession.mProgress.isNull());
12060 ComObjPtr<ProgressProxy> progress;
12061 progress.createObject();
12062 ComPtr<IUnknown> pPeer(mPeer);
12063 progress->init(mParent, pPeer,
12064 Bstr(tr("Closing session")).raw(),
12065 FALSE /* aCancelable */);
12066 progress.queryInterfaceTo(aProgress);
12067 mData->mSession.mProgress = progress;
12068 }
12069 else
12070 {
12071 /* the remote session is being normally closed */
12072 Data::Session::RemoteControlList::iterator it =
12073 mData->mSession.mRemoteControls.begin();
12074 while (it != mData->mSession.mRemoteControls.end())
12075 {
12076 if (control == *it)
12077 break;
12078 ++it;
12079 }
12080 BOOL found = it != mData->mSession.mRemoteControls.end();
12081 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12082 E_INVALIDARG);
12083 // This MUST be erase(it), not remove(*it) as the latter triggers a
12084 // very nasty use after free due to the place where the value "lives".
12085 mData->mSession.mRemoteControls.erase(it);
12086 }
12087
12088 LogFlowThisFuncLeave();
12089 return S_OK;
12090}
12091
12092/**
12093 * @note Locks this object for writing.
12094 */
12095STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12096{
12097 LogFlowThisFuncEnter();
12098
12099 CheckComArgOutPointerValid(aProgress);
12100 CheckComArgOutPointerValid(aStateFilePath);
12101
12102 AutoCaller autoCaller(this);
12103 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12104
12105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12106
12107 AssertReturn( mData->mMachineState == MachineState_Paused
12108 && mConsoleTaskData.mLastState == MachineState_Null
12109 && mConsoleTaskData.strStateFilePath.isEmpty(),
12110 E_FAIL);
12111
12112 /* create a progress object to track operation completion */
12113 ComObjPtr<Progress> pProgress;
12114 pProgress.createObject();
12115 pProgress->init(getVirtualBox(),
12116 static_cast<IMachine *>(this) /* aInitiator */,
12117 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12118 FALSE /* aCancelable */);
12119
12120 Utf8Str strStateFilePath;
12121 /* stateFilePath is null when the machine is not running */
12122 if (mData->mMachineState == MachineState_Paused)
12123 composeSavedStateFilename(strStateFilePath);
12124
12125 /* fill in the console task data */
12126 mConsoleTaskData.mLastState = mData->mMachineState;
12127 mConsoleTaskData.strStateFilePath = strStateFilePath;
12128 mConsoleTaskData.mProgress = pProgress;
12129
12130 /* set the state to Saving (this is expected by Console::SaveState()) */
12131 setMachineState(MachineState_Saving);
12132
12133 strStateFilePath.cloneTo(aStateFilePath);
12134 pProgress.queryInterfaceTo(aProgress);
12135
12136 return S_OK;
12137}
12138
12139/**
12140 * @note Locks mParent + this object for writing.
12141 */
12142STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12143{
12144 LogFlowThisFunc(("\n"));
12145
12146 AutoCaller autoCaller(this);
12147 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12148
12149 /* endSavingState() need mParent lock */
12150 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12151
12152 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12153 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12154 && mConsoleTaskData.mLastState != MachineState_Null
12155 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12156 E_FAIL);
12157
12158 /*
12159 * On failure, set the state to the state we had when BeginSavingState()
12160 * was called (this is expected by Console::SaveState() and the associated
12161 * task). On success the VM process already changed the state to
12162 * MachineState_Saved, so no need to do anything.
12163 */
12164 if (FAILED(iResult))
12165 setMachineState(mConsoleTaskData.mLastState);
12166
12167 return endSavingState(iResult, aErrMsg);
12168}
12169
12170/**
12171 * @note Locks this object for writing.
12172 */
12173STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12174{
12175 LogFlowThisFunc(("\n"));
12176
12177 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12178
12179 AutoCaller autoCaller(this);
12180 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12181
12182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12183
12184 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12185 || mData->mMachineState == MachineState_Teleported
12186 || mData->mMachineState == MachineState_Aborted
12187 , E_FAIL); /** @todo setError. */
12188
12189 Utf8Str stateFilePathFull = aSavedStateFile;
12190 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
12191 if (RT_FAILURE(vrc))
12192 return setError(VBOX_E_FILE_ERROR,
12193 tr("Invalid saved state file path '%ls' (%Rrc)"),
12194 aSavedStateFile,
12195 vrc);
12196
12197 mSSData->strStateFilePath = stateFilePathFull;
12198
12199 /* The below setMachineState() will detect the state transition and will
12200 * update the settings file */
12201
12202 return setMachineState(MachineState_Saved);
12203}
12204
12205STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
12206 ComSafeArrayOut(BSTR, aValues),
12207 ComSafeArrayOut(LONG64, aTimestamps),
12208 ComSafeArrayOut(BSTR, aFlags))
12209{
12210 LogFlowThisFunc(("\n"));
12211
12212#ifdef VBOX_WITH_GUEST_PROPS
12213 using namespace guestProp;
12214
12215 AutoCaller autoCaller(this);
12216 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12217
12218 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12219
12220 AssertReturn(!ComSafeArrayOutIsNull(aNames), E_POINTER);
12221 AssertReturn(!ComSafeArrayOutIsNull(aValues), E_POINTER);
12222 AssertReturn(!ComSafeArrayOutIsNull(aTimestamps), E_POINTER);
12223 AssertReturn(!ComSafeArrayOutIsNull(aFlags), E_POINTER);
12224
12225 size_t cEntries = mHWData->mGuestProperties.size();
12226 com::SafeArray<BSTR> names(cEntries);
12227 com::SafeArray<BSTR> values(cEntries);
12228 com::SafeArray<LONG64> timestamps(cEntries);
12229 com::SafeArray<BSTR> flags(cEntries);
12230 unsigned i = 0;
12231 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
12232 it != mHWData->mGuestProperties.end();
12233 ++it)
12234 {
12235 char szFlags[MAX_FLAGS_LEN + 1];
12236 it->strName.cloneTo(&names[i]);
12237 it->strValue.cloneTo(&values[i]);
12238 timestamps[i] = it->mTimestamp;
12239 /* If it is NULL, keep it NULL. */
12240 if (it->mFlags)
12241 {
12242 writeFlags(it->mFlags, szFlags);
12243 Bstr(szFlags).cloneTo(&flags[i]);
12244 }
12245 else
12246 flags[i] = NULL;
12247 ++i;
12248 }
12249 names.detachTo(ComSafeArrayOutArg(aNames));
12250 values.detachTo(ComSafeArrayOutArg(aValues));
12251 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
12252 flags.detachTo(ComSafeArrayOutArg(aFlags));
12253 return S_OK;
12254#else
12255 ReturnComNotImplemented();
12256#endif
12257}
12258
12259STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
12260 IN_BSTR aValue,
12261 LONG64 aTimestamp,
12262 IN_BSTR aFlags)
12263{
12264 LogFlowThisFunc(("\n"));
12265
12266#ifdef VBOX_WITH_GUEST_PROPS
12267 using namespace guestProp;
12268
12269 CheckComArgStrNotEmptyOrNull(aName);
12270 CheckComArgNotNull(aValue);
12271 CheckComArgNotNull(aFlags);
12272
12273 try
12274 {
12275 /*
12276 * Convert input up front.
12277 */
12278 Utf8Str utf8Name(aName);
12279 uint32_t fFlags = NILFLAG;
12280 if (aFlags)
12281 {
12282 Utf8Str utf8Flags(aFlags);
12283 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
12284 AssertRCReturn(vrc, E_INVALIDARG);
12285 }
12286
12287 /*
12288 * Now grab the object lock, validate the state and do the update.
12289 */
12290 AutoCaller autoCaller(this);
12291 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12292
12293 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12294
12295 switch (mData->mMachineState)
12296 {
12297 case MachineState_Paused:
12298 case MachineState_Running:
12299 case MachineState_Teleporting:
12300 case MachineState_TeleportingPausedVM:
12301 case MachineState_LiveSnapshotting:
12302 case MachineState_DeletingSnapshotOnline:
12303 case MachineState_DeletingSnapshotPaused:
12304 case MachineState_Saving:
12305 break;
12306
12307 default:
12308#ifndef DEBUG_sunlover
12309 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
12310 VBOX_E_INVALID_VM_STATE);
12311#else
12312 return VBOX_E_INVALID_VM_STATE;
12313#endif
12314 }
12315
12316 setModified(IsModified_MachineData);
12317 mHWData.backup();
12318
12319 /** @todo r=bird: The careful memory handling doesn't work out here because
12320 * the catch block won't undo any damage we've done. So, if push_back throws
12321 * bad_alloc then you've lost the value.
12322 *
12323 * Another thing. Doing a linear search here isn't extremely efficient, esp.
12324 * since values that changes actually bubbles to the end of the list. Using
12325 * something that has an efficient lookup and can tolerate a bit of updates
12326 * would be nice. RTStrSpace is one suggestion (it's not perfect). Some
12327 * combination of RTStrCache (for sharing names and getting uniqueness into
12328 * the bargain) and hash/tree is another. */
12329 for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();
12330 iter != mHWData->mGuestProperties.end();
12331 ++iter)
12332 if (utf8Name == iter->strName)
12333 {
12334 mHWData->mGuestProperties.erase(iter);
12335 mData->mGuestPropertiesModified = TRUE;
12336 break;
12337 }
12338 if (aValue != NULL)
12339 {
12340 HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };
12341 mHWData->mGuestProperties.push_back(property);
12342 mData->mGuestPropertiesModified = TRUE;
12343 }
12344
12345 /*
12346 * Send a callback notification if appropriate
12347 */
12348 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
12349 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
12350 RTSTR_MAX,
12351 utf8Name.c_str(),
12352 RTSTR_MAX, NULL)
12353 )
12354 {
12355 alock.release();
12356
12357 mParent->onGuestPropertyChange(mData->mUuid,
12358 aName,
12359 aValue,
12360 aFlags);
12361 }
12362 }
12363 catch (...)
12364 {
12365 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
12366 }
12367 return S_OK;
12368#else
12369 ReturnComNotImplemented();
12370#endif
12371}
12372
12373STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
12374 IMediumAttachment **aNewAttachment)
12375{
12376 CheckComArgNotNull(aAttachment);
12377 CheckComArgOutPointerValid(aNewAttachment);
12378
12379 AutoCaller autoCaller(this);
12380 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12381
12382 // request the host lock first, since might be calling Host methods for getting host drives;
12383 // next, protect the media tree all the while we're in here, as well as our member variables
12384 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
12385 this->lockHandle(),
12386 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
12387
12388 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
12389
12390 Bstr ctrlName;
12391 LONG lPort;
12392 LONG lDevice;
12393 bool fTempEject;
12394 {
12395 AutoCaller autoAttachCaller(this);
12396 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12397
12398 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12399
12400 /* Need to query the details first, as the IMediumAttachment reference
12401 * might be to the original settings, which we are going to change. */
12402 ctrlName = pAttach->getControllerName();
12403 lPort = pAttach->getPort();
12404 lDevice = pAttach->getDevice();
12405 fTempEject = pAttach->getTempEject();
12406 }
12407
12408 if (!fTempEject)
12409 {
12410 /* Remember previously mounted medium. The medium before taking the
12411 * backup is not necessarily the same thing. */
12412 ComObjPtr<Medium> oldmedium;
12413 oldmedium = pAttach->getMedium();
12414
12415 setModified(IsModified_Storage);
12416 mMediaData.backup();
12417
12418 // The backup operation makes the pAttach reference point to the
12419 // old settings. Re-get the correct reference.
12420 pAttach = findAttachment(mMediaData->mAttachments,
12421 ctrlName.raw(),
12422 lPort,
12423 lDevice);
12424
12425 {
12426 AutoCaller autoAttachCaller(this);
12427 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12428
12429 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12430 if (!oldmedium.isNull())
12431 oldmedium->removeBackReference(mData->mUuid);
12432
12433 pAttach->updateMedium(NULL);
12434 pAttach->updateEjected();
12435 }
12436
12437 setModified(IsModified_Storage);
12438 }
12439 else
12440 {
12441 {
12442 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12443 pAttach->updateEjected();
12444 }
12445 }
12446
12447 pAttach.queryInterfaceTo(aNewAttachment);
12448
12449 return S_OK;
12450}
12451
12452// public methods only for internal purposes
12453/////////////////////////////////////////////////////////////////////////////
12454
12455/**
12456 * Called from the client watcher thread to check for expected or unexpected
12457 * death of the client process that has a direct session to this machine.
12458 *
12459 * On Win32 and on OS/2, this method is called only when we've got the
12460 * mutex (i.e. the client has either died or terminated normally) so it always
12461 * returns @c true (the client is terminated, the session machine is
12462 * uninitialized).
12463 *
12464 * On other platforms, the method returns @c true if the client process has
12465 * terminated normally or abnormally and the session machine was uninitialized,
12466 * and @c false if the client process is still alive.
12467 *
12468 * @note Locks this object for writing.
12469 */
12470bool SessionMachine::checkForDeath()
12471{
12472 Uninit::Reason reason;
12473 bool terminated = false;
12474
12475 /* Enclose autoCaller with a block because calling uninit() from under it
12476 * will deadlock. */
12477 {
12478 AutoCaller autoCaller(this);
12479 if (!autoCaller.isOk())
12480 {
12481 /* return true if not ready, to cause the client watcher to exclude
12482 * the corresponding session from watching */
12483 LogFlowThisFunc(("Already uninitialized!\n"));
12484 return true;
12485 }
12486
12487 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12488
12489 /* Determine the reason of death: if the session state is Closing here,
12490 * everything is fine. Otherwise it means that the client did not call
12491 * OnSessionEnd() before it released the IPC semaphore. This may happen
12492 * either because the client process has abnormally terminated, or
12493 * because it simply forgot to call ISession::Close() before exiting. We
12494 * threat the latter also as an abnormal termination (see
12495 * Session::uninit() for details). */
12496 reason = mData->mSession.mState == SessionState_Unlocking ?
12497 Uninit::Normal :
12498 Uninit::Abnormal;
12499
12500#if defined(RT_OS_WINDOWS)
12501
12502 AssertMsg(mIPCSem, ("semaphore must be created"));
12503
12504 /* release the IPC mutex */
12505 ::ReleaseMutex(mIPCSem);
12506
12507 terminated = true;
12508
12509#elif defined(RT_OS_OS2)
12510
12511 AssertMsg(mIPCSem, ("semaphore must be created"));
12512
12513 /* release the IPC mutex */
12514 ::DosReleaseMutexSem(mIPCSem);
12515
12516 terminated = true;
12517
12518#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12519
12520 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
12521
12522 int val = ::semctl(mIPCSem, 0, GETVAL);
12523 if (val > 0)
12524 {
12525 /* the semaphore is signaled, meaning the session is terminated */
12526 terminated = true;
12527 }
12528
12529#else
12530# error "Port me!"
12531#endif
12532
12533 } /* AutoCaller block */
12534
12535 if (terminated)
12536 uninit(reason);
12537
12538 return terminated;
12539}
12540
12541/**
12542 * @note Locks this object for reading.
12543 */
12544HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
12545{
12546 LogFlowThisFunc(("\n"));
12547
12548 AutoCaller autoCaller(this);
12549 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12550
12551 ComPtr<IInternalSessionControl> directControl;
12552 {
12553 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12554 directControl = mData->mSession.mDirectControl;
12555 }
12556
12557 /* ignore notifications sent after #OnSessionEnd() is called */
12558 if (!directControl)
12559 return S_OK;
12560
12561 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
12562}
12563
12564/**
12565 * @note Locks this object for reading.
12566 */
12567HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
12568 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
12569{
12570 LogFlowThisFunc(("\n"));
12571
12572 AutoCaller autoCaller(this);
12573 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12574
12575 ComPtr<IInternalSessionControl> directControl;
12576 {
12577 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12578 directControl = mData->mSession.mDirectControl;
12579 }
12580
12581 /* ignore notifications sent after #OnSessionEnd() is called */
12582 if (!directControl)
12583 return S_OK;
12584 /*
12585 * instead acting like callback we ask IVirtualBox deliver corresponding event
12586 */
12587
12588 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, aHostPort, aGuestIp, aGuestPort);
12589 return S_OK;
12590}
12591
12592/**
12593 * @note Locks this object for reading.
12594 */
12595HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
12596{
12597 LogFlowThisFunc(("\n"));
12598
12599 AutoCaller autoCaller(this);
12600 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12601
12602 ComPtr<IInternalSessionControl> directControl;
12603 {
12604 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12605 directControl = mData->mSession.mDirectControl;
12606 }
12607
12608 /* ignore notifications sent after #OnSessionEnd() is called */
12609 if (!directControl)
12610 return S_OK;
12611
12612 return directControl->OnSerialPortChange(serialPort);
12613}
12614
12615/**
12616 * @note Locks this object for reading.
12617 */
12618HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
12619{
12620 LogFlowThisFunc(("\n"));
12621
12622 AutoCaller autoCaller(this);
12623 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12624
12625 ComPtr<IInternalSessionControl> directControl;
12626 {
12627 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12628 directControl = mData->mSession.mDirectControl;
12629 }
12630
12631 /* ignore notifications sent after #OnSessionEnd() is called */
12632 if (!directControl)
12633 return S_OK;
12634
12635 return directControl->OnParallelPortChange(parallelPort);
12636}
12637
12638/**
12639 * @note Locks this object for reading.
12640 */
12641HRESULT SessionMachine::onStorageControllerChange()
12642{
12643 LogFlowThisFunc(("\n"));
12644
12645 AutoCaller autoCaller(this);
12646 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12647
12648 ComPtr<IInternalSessionControl> directControl;
12649 {
12650 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12651 directControl = mData->mSession.mDirectControl;
12652 }
12653
12654 /* ignore notifications sent after #OnSessionEnd() is called */
12655 if (!directControl)
12656 return S_OK;
12657
12658 return directControl->OnStorageControllerChange();
12659}
12660
12661/**
12662 * @note Locks this object for reading.
12663 */
12664HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
12665{
12666 LogFlowThisFunc(("\n"));
12667
12668 AutoCaller autoCaller(this);
12669 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12670
12671 ComPtr<IInternalSessionControl> directControl;
12672 {
12673 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12674 directControl = mData->mSession.mDirectControl;
12675 }
12676
12677 /* ignore notifications sent after #OnSessionEnd() is called */
12678 if (!directControl)
12679 return S_OK;
12680
12681 return directControl->OnMediumChange(aAttachment, aForce);
12682}
12683
12684/**
12685 * @note Locks this object for reading.
12686 */
12687HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
12688{
12689 LogFlowThisFunc(("\n"));
12690
12691 AutoCaller autoCaller(this);
12692 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
12693
12694 ComPtr<IInternalSessionControl> directControl;
12695 {
12696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12697 directControl = mData->mSession.mDirectControl;
12698 }
12699
12700 /* ignore notifications sent after #OnSessionEnd() is called */
12701 if (!directControl)
12702 return S_OK;
12703
12704 return directControl->OnCPUChange(aCPU, aRemove);
12705}
12706
12707HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
12708{
12709 LogFlowThisFunc(("\n"));
12710
12711 AutoCaller autoCaller(this);
12712 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
12713
12714 ComPtr<IInternalSessionControl> directControl;
12715 {
12716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12717 directControl = mData->mSession.mDirectControl;
12718 }
12719
12720 /* ignore notifications sent after #OnSessionEnd() is called */
12721 if (!directControl)
12722 return S_OK;
12723
12724 return directControl->OnCPUExecutionCapChange(aExecutionCap);
12725}
12726
12727/**
12728 * @note Locks this object for reading.
12729 */
12730HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
12731{
12732 LogFlowThisFunc(("\n"));
12733
12734 AutoCaller autoCaller(this);
12735 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12736
12737 ComPtr<IInternalSessionControl> directControl;
12738 {
12739 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12740 directControl = mData->mSession.mDirectControl;
12741 }
12742
12743 /* ignore notifications sent after #OnSessionEnd() is called */
12744 if (!directControl)
12745 return S_OK;
12746
12747 return directControl->OnVRDEServerChange(aRestart);
12748}
12749
12750/**
12751 * @note Locks this object for reading.
12752 */
12753HRESULT SessionMachine::onUSBControllerChange()
12754{
12755 LogFlowThisFunc(("\n"));
12756
12757 AutoCaller autoCaller(this);
12758 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12759
12760 ComPtr<IInternalSessionControl> directControl;
12761 {
12762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12763 directControl = mData->mSession.mDirectControl;
12764 }
12765
12766 /* ignore notifications sent after #OnSessionEnd() is called */
12767 if (!directControl)
12768 return S_OK;
12769
12770 return directControl->OnUSBControllerChange();
12771}
12772
12773/**
12774 * @note Locks this object for reading.
12775 */
12776HRESULT SessionMachine::onSharedFolderChange()
12777{
12778 LogFlowThisFunc(("\n"));
12779
12780 AutoCaller autoCaller(this);
12781 AssertComRCReturnRC(autoCaller.rc());
12782
12783 ComPtr<IInternalSessionControl> directControl;
12784 {
12785 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12786 directControl = mData->mSession.mDirectControl;
12787 }
12788
12789 /* ignore notifications sent after #OnSessionEnd() is called */
12790 if (!directControl)
12791 return S_OK;
12792
12793 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
12794}
12795
12796/**
12797 * @note Locks this object for reading.
12798 */
12799HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
12800{
12801 LogFlowThisFunc(("\n"));
12802
12803 AutoCaller autoCaller(this);
12804 AssertComRCReturnRC(autoCaller.rc());
12805
12806 ComPtr<IInternalSessionControl> directControl;
12807 {
12808 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12809 directControl = mData->mSession.mDirectControl;
12810 }
12811
12812 /* ignore notifications sent after #OnSessionEnd() is called */
12813 if (!directControl)
12814 return S_OK;
12815
12816 return directControl->OnClipboardModeChange(aClipboardMode);
12817}
12818
12819/**
12820 * @note Locks this object for reading.
12821 */
12822HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
12823{
12824 LogFlowThisFunc(("\n"));
12825
12826 AutoCaller autoCaller(this);
12827 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
12828
12829 ComPtr<IInternalSessionControl> directControl;
12830 {
12831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12832 directControl = mData->mSession.mDirectControl;
12833 }
12834
12835 /* ignore notifications sent after #OnSessionEnd() is called */
12836 if (!directControl)
12837 return S_OK;
12838
12839 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
12840}
12841
12842/**
12843 * @note Locks this object for reading.
12844 */
12845HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove)
12846{
12847 LogFlowThisFunc(("\n"));
12848
12849 AutoCaller autoCaller(this);
12850 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12851
12852 ComPtr<IInternalSessionControl> directControl;
12853 {
12854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12855 directControl = mData->mSession.mDirectControl;
12856 }
12857
12858 /* ignore notifications sent after #OnSessionEnd() is called */
12859 if (!directControl)
12860 return S_OK;
12861
12862 return directControl->OnStorageDeviceChange(aAttachment, aRemove);
12863}
12864
12865/**
12866 * Returns @c true if this machine's USB controller reports it has a matching
12867 * filter for the given USB device and @c false otherwise.
12868 *
12869 * @note locks this object for reading.
12870 */
12871bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
12872{
12873 AutoCaller autoCaller(this);
12874 /* silently return if not ready -- this method may be called after the
12875 * direct machine session has been called */
12876 if (!autoCaller.isOk())
12877 return false;
12878
12879#ifdef VBOX_WITH_USB
12880 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12881
12882 switch (mData->mMachineState)
12883 {
12884 case MachineState_Starting:
12885 case MachineState_Restoring:
12886 case MachineState_TeleportingIn:
12887 case MachineState_Paused:
12888 case MachineState_Running:
12889 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
12890 * elsewhere... */
12891 alock.release();
12892 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
12893 default: break;
12894 }
12895#else
12896 NOREF(aDevice);
12897 NOREF(aMaskedIfs);
12898#endif
12899 return false;
12900}
12901
12902/**
12903 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
12904 */
12905HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
12906 IVirtualBoxErrorInfo *aError,
12907 ULONG aMaskedIfs)
12908{
12909 LogFlowThisFunc(("\n"));
12910
12911 AutoCaller autoCaller(this);
12912
12913 /* This notification may happen after the machine object has been
12914 * uninitialized (the session was closed), so don't assert. */
12915 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12916
12917 ComPtr<IInternalSessionControl> directControl;
12918 {
12919 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12920 directControl = mData->mSession.mDirectControl;
12921 }
12922
12923 /* fail on notifications sent after #OnSessionEnd() is called, it is
12924 * expected by the caller */
12925 if (!directControl)
12926 return E_FAIL;
12927
12928 /* No locks should be held at this point. */
12929 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
12930 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
12931
12932 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
12933}
12934
12935/**
12936 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
12937 */
12938HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
12939 IVirtualBoxErrorInfo *aError)
12940{
12941 LogFlowThisFunc(("\n"));
12942
12943 AutoCaller autoCaller(this);
12944
12945 /* This notification may happen after the machine object has been
12946 * uninitialized (the session was closed), so don't assert. */
12947 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12948
12949 ComPtr<IInternalSessionControl> directControl;
12950 {
12951 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12952 directControl = mData->mSession.mDirectControl;
12953 }
12954
12955 /* fail on notifications sent after #OnSessionEnd() is called, it is
12956 * expected by the caller */
12957 if (!directControl)
12958 return E_FAIL;
12959
12960 /* No locks should be held at this point. */
12961 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
12962 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
12963
12964 return directControl->OnUSBDeviceDetach(aId, aError);
12965}
12966
12967// protected methods
12968/////////////////////////////////////////////////////////////////////////////
12969
12970/**
12971 * Helper method to finalize saving the state.
12972 *
12973 * @note Must be called from under this object's lock.
12974 *
12975 * @param aRc S_OK if the snapshot has been taken successfully
12976 * @param aErrMsg human readable error message for failure
12977 *
12978 * @note Locks mParent + this objects for writing.
12979 */
12980HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
12981{
12982 LogFlowThisFuncEnter();
12983
12984 AutoCaller autoCaller(this);
12985 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12986
12987 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12988
12989 HRESULT rc = S_OK;
12990
12991 if (SUCCEEDED(aRc))
12992 {
12993 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
12994
12995 /* save all VM settings */
12996 rc = saveSettings(NULL);
12997 // no need to check whether VirtualBox.xml needs saving also since
12998 // we can't have a name change pending at this point
12999 }
13000 else
13001 {
13002 // delete the saved state file (it might have been already created);
13003 // we need not check whether this is shared with a snapshot here because
13004 // we certainly created this saved state file here anew
13005 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13006 }
13007
13008 /* notify the progress object about operation completion */
13009 Assert(mConsoleTaskData.mProgress);
13010 if (SUCCEEDED(aRc))
13011 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13012 else
13013 {
13014 if (aErrMsg.length())
13015 mConsoleTaskData.mProgress->notifyComplete(aRc,
13016 COM_IIDOF(ISession),
13017 getComponentName(),
13018 aErrMsg.c_str());
13019 else
13020 mConsoleTaskData.mProgress->notifyComplete(aRc);
13021 }
13022
13023 /* clear out the temporary saved state data */
13024 mConsoleTaskData.mLastState = MachineState_Null;
13025 mConsoleTaskData.strStateFilePath.setNull();
13026 mConsoleTaskData.mProgress.setNull();
13027
13028 LogFlowThisFuncLeave();
13029 return rc;
13030}
13031
13032/**
13033 * Deletes the given file if it is no longer in use by either the current machine state
13034 * (if the machine is "saved") or any of the machine's snapshots.
13035 *
13036 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13037 * but is different for each SnapshotMachine. When calling this, the order of calling this
13038 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13039 * is therefore critical. I know, it's all rather messy.
13040 *
13041 * @param strStateFile
13042 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
13043 */
13044void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
13045 Snapshot *pSnapshotToIgnore)
13046{
13047 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13048 if ( (strStateFile.isNotEmpty())
13049 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13050 )
13051 // ... and it must also not be shared with other snapshots
13052 if ( !mData->mFirstSnapshot
13053 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13054 // this checks the SnapshotMachine's state file paths
13055 )
13056 RTFileDelete(strStateFile.c_str());
13057}
13058
13059/**
13060 * Locks the attached media.
13061 *
13062 * All attached hard disks are locked for writing and DVD/floppy are locked for
13063 * reading. Parents of attached hard disks (if any) are locked for reading.
13064 *
13065 * This method also performs accessibility check of all media it locks: if some
13066 * media is inaccessible, the method will return a failure and a bunch of
13067 * extended error info objects per each inaccessible medium.
13068 *
13069 * Note that this method is atomic: if it returns a success, all media are
13070 * locked as described above; on failure no media is locked at all (all
13071 * succeeded individual locks will be undone).
13072 *
13073 * This method is intended to be called when the machine is in Starting or
13074 * Restoring state and asserts otherwise.
13075 *
13076 * The locks made by this method must be undone by calling #unlockMedia() when
13077 * no more needed.
13078 */
13079HRESULT SessionMachine::lockMedia()
13080{
13081 AutoCaller autoCaller(this);
13082 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13083
13084 AutoMultiWriteLock2 alock(this->lockHandle(),
13085 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13086
13087 AssertReturn( mData->mMachineState == MachineState_Starting
13088 || mData->mMachineState == MachineState_Restoring
13089 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13090 /* bail out if trying to lock things with already set up locking */
13091 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13092
13093 clearError();
13094 MultiResult mrc(S_OK);
13095
13096 /* Collect locking information for all medium objects attached to the VM. */
13097 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13098 it != mMediaData->mAttachments.end();
13099 ++it)
13100 {
13101 MediumAttachment* pAtt = *it;
13102 DeviceType_T devType = pAtt->getType();
13103 Medium *pMedium = pAtt->getMedium();
13104
13105 MediumLockList *pMediumLockList(new MediumLockList());
13106 // There can be attachments without a medium (floppy/dvd), and thus
13107 // it's impossible to create a medium lock list. It still makes sense
13108 // to have the empty medium lock list in the map in case a medium is
13109 // attached later.
13110 if (pMedium != NULL)
13111 {
13112 MediumType_T mediumType = pMedium->getType();
13113 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13114 || mediumType == MediumType_Shareable;
13115 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13116
13117 alock.release();
13118 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13119 !fIsReadOnlyLock /* fMediumLockWrite */,
13120 NULL,
13121 *pMediumLockList);
13122 alock.acquire();
13123 if (FAILED(mrc))
13124 {
13125 delete pMediumLockList;
13126 mData->mSession.mLockedMedia.Clear();
13127 break;
13128 }
13129 }
13130
13131 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13132 if (FAILED(rc))
13133 {
13134 mData->mSession.mLockedMedia.Clear();
13135 mrc = setError(rc,
13136 tr("Collecting locking information for all attached media failed"));
13137 break;
13138 }
13139 }
13140
13141 if (SUCCEEDED(mrc))
13142 {
13143 /* Now lock all media. If this fails, nothing is locked. */
13144 alock.release();
13145 HRESULT rc = mData->mSession.mLockedMedia.Lock();
13146 alock.acquire();
13147 if (FAILED(rc))
13148 {
13149 mrc = setError(rc,
13150 tr("Locking of attached media failed"));
13151 }
13152 }
13153
13154 return mrc;
13155}
13156
13157/**
13158 * Undoes the locks made by by #lockMedia().
13159 */
13160void SessionMachine::unlockMedia()
13161{
13162 AutoCaller autoCaller(this);
13163 AssertComRCReturnVoid(autoCaller.rc());
13164
13165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13166
13167 /* we may be holding important error info on the current thread;
13168 * preserve it */
13169 ErrorInfoKeeper eik;
13170
13171 HRESULT rc = mData->mSession.mLockedMedia.Clear();
13172 AssertComRC(rc);
13173}
13174
13175/**
13176 * Helper to change the machine state (reimplementation).
13177 *
13178 * @note Locks this object for writing.
13179 */
13180HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
13181{
13182 LogFlowThisFuncEnter();
13183 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
13184
13185 AutoCaller autoCaller(this);
13186 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13187
13188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13189
13190 MachineState_T oldMachineState = mData->mMachineState;
13191
13192 AssertMsgReturn(oldMachineState != aMachineState,
13193 ("oldMachineState=%s, aMachineState=%s\n",
13194 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
13195 E_FAIL);
13196
13197 HRESULT rc = S_OK;
13198
13199 int stsFlags = 0;
13200 bool deleteSavedState = false;
13201
13202 /* detect some state transitions */
13203
13204 if ( ( oldMachineState == MachineState_Saved
13205 && aMachineState == MachineState_Restoring)
13206 || ( ( oldMachineState == MachineState_PoweredOff
13207 || oldMachineState == MachineState_Teleported
13208 || oldMachineState == MachineState_Aborted
13209 )
13210 && ( aMachineState == MachineState_TeleportingIn
13211 || aMachineState == MachineState_Starting
13212 )
13213 )
13214 )
13215 {
13216 /* The EMT thread is about to start */
13217
13218 /* Nothing to do here for now... */
13219
13220 /// @todo NEWMEDIA don't let mDVDDrive and other children
13221 /// change anything when in the Starting/Restoring state
13222 }
13223 else if ( ( oldMachineState == MachineState_Running
13224 || oldMachineState == MachineState_Paused
13225 || oldMachineState == MachineState_Teleporting
13226 || oldMachineState == MachineState_LiveSnapshotting
13227 || oldMachineState == MachineState_Stuck
13228 || oldMachineState == MachineState_Starting
13229 || oldMachineState == MachineState_Stopping
13230 || oldMachineState == MachineState_Saving
13231 || oldMachineState == MachineState_Restoring
13232 || oldMachineState == MachineState_TeleportingPausedVM
13233 || oldMachineState == MachineState_TeleportingIn
13234 )
13235 && ( aMachineState == MachineState_PoweredOff
13236 || aMachineState == MachineState_Saved
13237 || aMachineState == MachineState_Teleported
13238 || aMachineState == MachineState_Aborted
13239 )
13240 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
13241 * snapshot */
13242 && ( mConsoleTaskData.mSnapshot.isNull()
13243 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
13244 )
13245 )
13246 {
13247 /* The EMT thread has just stopped, unlock attached media. Note that as
13248 * opposed to locking that is done from Console, we do unlocking here
13249 * because the VM process may have aborted before having a chance to
13250 * properly unlock all media it locked. */
13251
13252 unlockMedia();
13253 }
13254
13255 if (oldMachineState == MachineState_Restoring)
13256 {
13257 if (aMachineState != MachineState_Saved)
13258 {
13259 /*
13260 * delete the saved state file once the machine has finished
13261 * restoring from it (note that Console sets the state from
13262 * Restoring to Saved if the VM couldn't restore successfully,
13263 * to give the user an ability to fix an error and retry --
13264 * we keep the saved state file in this case)
13265 */
13266 deleteSavedState = true;
13267 }
13268 }
13269 else if ( oldMachineState == MachineState_Saved
13270 && ( aMachineState == MachineState_PoweredOff
13271 || aMachineState == MachineState_Aborted
13272 || aMachineState == MachineState_Teleported
13273 )
13274 )
13275 {
13276 /*
13277 * delete the saved state after Console::ForgetSavedState() is called
13278 * or if the VM process (owning a direct VM session) crashed while the
13279 * VM was Saved
13280 */
13281
13282 /// @todo (dmik)
13283 // Not sure that deleting the saved state file just because of the
13284 // client death before it attempted to restore the VM is a good
13285 // thing. But when it crashes we need to go to the Aborted state
13286 // which cannot have the saved state file associated... The only
13287 // way to fix this is to make the Aborted condition not a VM state
13288 // but a bool flag: i.e., when a crash occurs, set it to true and
13289 // change the state to PoweredOff or Saved depending on the
13290 // saved state presence.
13291
13292 deleteSavedState = true;
13293 mData->mCurrentStateModified = TRUE;
13294 stsFlags |= SaveSTS_CurStateModified;
13295 }
13296
13297 if ( aMachineState == MachineState_Starting
13298 || aMachineState == MachineState_Restoring
13299 || aMachineState == MachineState_TeleportingIn
13300 )
13301 {
13302 /* set the current state modified flag to indicate that the current
13303 * state is no more identical to the state in the
13304 * current snapshot */
13305 if (!mData->mCurrentSnapshot.isNull())
13306 {
13307 mData->mCurrentStateModified = TRUE;
13308 stsFlags |= SaveSTS_CurStateModified;
13309 }
13310 }
13311
13312 if (deleteSavedState)
13313 {
13314 if (mRemoveSavedState)
13315 {
13316 Assert(!mSSData->strStateFilePath.isEmpty());
13317
13318 // it is safe to delete the saved state file if ...
13319 if ( !mData->mFirstSnapshot // ... we have no snapshots or
13320 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
13321 // ... none of the snapshots share the saved state file
13322 )
13323 RTFileDelete(mSSData->strStateFilePath.c_str());
13324 }
13325
13326 mSSData->strStateFilePath.setNull();
13327 stsFlags |= SaveSTS_StateFilePath;
13328 }
13329
13330 /* redirect to the underlying peer machine */
13331 mPeer->setMachineState(aMachineState);
13332
13333 if ( aMachineState == MachineState_PoweredOff
13334 || aMachineState == MachineState_Teleported
13335 || aMachineState == MachineState_Aborted
13336 || aMachineState == MachineState_Saved)
13337 {
13338 /* the machine has stopped execution
13339 * (or the saved state file was adopted) */
13340 stsFlags |= SaveSTS_StateTimeStamp;
13341 }
13342
13343 if ( ( oldMachineState == MachineState_PoweredOff
13344 || oldMachineState == MachineState_Aborted
13345 || oldMachineState == MachineState_Teleported
13346 )
13347 && aMachineState == MachineState_Saved)
13348 {
13349 /* the saved state file was adopted */
13350 Assert(!mSSData->strStateFilePath.isEmpty());
13351 stsFlags |= SaveSTS_StateFilePath;
13352 }
13353
13354#ifdef VBOX_WITH_GUEST_PROPS
13355 if ( aMachineState == MachineState_PoweredOff
13356 || aMachineState == MachineState_Aborted
13357 || aMachineState == MachineState_Teleported)
13358 {
13359 /* Make sure any transient guest properties get removed from the
13360 * property store on shutdown. */
13361
13362 HWData::GuestPropertyList::iterator it;
13363 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
13364 if (!fNeedsSaving)
13365 for (it = mHWData->mGuestProperties.begin();
13366 it != mHWData->mGuestProperties.end(); ++it)
13367 if ( (it->mFlags & guestProp::TRANSIENT)
13368 || (it->mFlags & guestProp::TRANSRESET))
13369 {
13370 fNeedsSaving = true;
13371 break;
13372 }
13373 if (fNeedsSaving)
13374 {
13375 mData->mCurrentStateModified = TRUE;
13376 stsFlags |= SaveSTS_CurStateModified;
13377 SaveSettings(); // @todo r=dj why the public method? why first SaveSettings and then saveStateSettings?
13378 }
13379 }
13380#endif
13381
13382 rc = saveStateSettings(stsFlags);
13383
13384 if ( ( oldMachineState != MachineState_PoweredOff
13385 && oldMachineState != MachineState_Aborted
13386 && oldMachineState != MachineState_Teleported
13387 )
13388 && ( aMachineState == MachineState_PoweredOff
13389 || aMachineState == MachineState_Aborted
13390 || aMachineState == MachineState_Teleported
13391 )
13392 )
13393 {
13394 /* we've been shut down for any reason */
13395 /* no special action so far */
13396 }
13397
13398 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
13399 LogFlowThisFuncLeave();
13400 return rc;
13401}
13402
13403/**
13404 * Sends the current machine state value to the VM process.
13405 *
13406 * @note Locks this object for reading, then calls a client process.
13407 */
13408HRESULT SessionMachine::updateMachineStateOnClient()
13409{
13410 AutoCaller autoCaller(this);
13411 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13412
13413 ComPtr<IInternalSessionControl> directControl;
13414 {
13415 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13416 AssertReturn(!!mData, E_FAIL);
13417 directControl = mData->mSession.mDirectControl;
13418
13419 /* directControl may be already set to NULL here in #OnSessionEnd()
13420 * called too early by the direct session process while there is still
13421 * some operation (like deleting the snapshot) in progress. The client
13422 * process in this case is waiting inside Session::close() for the
13423 * "end session" process object to complete, while #uninit() called by
13424 * #checkForDeath() on the Watcher thread is waiting for the pending
13425 * operation to complete. For now, we accept this inconsistent behavior
13426 * and simply do nothing here. */
13427
13428 if (mData->mSession.mState == SessionState_Unlocking)
13429 return S_OK;
13430
13431 AssertReturn(!directControl.isNull(), E_FAIL);
13432 }
13433
13434 return directControl->UpdateMachineState(mData->mMachineState);
13435}
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